'MySQL性能優化的21個最佳實踐'

MySQL SQL 數據庫 程序員 DBA 設計 phpMyAdmin 中央處理器 IT技術資源共享 2019-07-31
"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

MySQL性能優化的21個最佳實踐

從上圖你可以看到那個搜索字串 “last_name LIKE ‘a%’”,一個是建了索引,一個是沒有索引,性能差了 4 倍左右。

另外,你應該也需要知道什麼樣的搜索是不能使用正常的索引的。例如, 當你需要在一篇大的文章中搜索一個詞時,如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜索關鍵詞或是 Tag 什麼的)

5.在 Join 表的時候使用相當類型的例,並將其索引

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 Join 的字段是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。

而且,這些被用來 Join 的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個 INT 字段 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

MySQL性能優化的21個最佳實踐

從上圖你可以看到那個搜索字串 “last_name LIKE ‘a%’”,一個是建了索引,一個是沒有索引,性能差了 4 倍左右。

另外,你應該也需要知道什麼樣的搜索是不能使用正常的索引的。例如, 當你需要在一篇大的文章中搜索一個詞時,如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜索關鍵詞或是 Tag 什麼的)

5.在 Join 表的時候使用相當類型的例,並將其索引

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 Join 的字段是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。

而且,這些被用來 Join 的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個 INT 字段 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

MySQL性能優化的21個最佳實踐

6.千萬不要 ORDER BY RAND()

想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的性能問題。

如果你真的想把返回的數據行打亂了,你有 N 種方法可以達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的下降。這裡的問題是:MySQL 會不得不去執行 RAND()函數(很耗 CPU 時間),而且這是為了每一行記錄去記行,然後再對其排序。就算是你用了 Limit 1 也無濟於事(因為要排序)

下面的示例是隨機挑一條記錄

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

MySQL性能優化的21個最佳實踐

從上圖你可以看到那個搜索字串 “last_name LIKE ‘a%’”,一個是建了索引,一個是沒有索引,性能差了 4 倍左右。

另外,你應該也需要知道什麼樣的搜索是不能使用正常的索引的。例如, 當你需要在一篇大的文章中搜索一個詞時,如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜索關鍵詞或是 Tag 什麼的)

5.在 Join 表的時候使用相當類型的例,並將其索引

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 Join 的字段是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。

而且,這些被用來 Join 的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個 INT 字段 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

MySQL性能優化的21個最佳實踐

6.千萬不要 ORDER BY RAND()

想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的性能問題。

如果你真的想把返回的數據行打亂了,你有 N 種方法可以達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的下降。這裡的問題是:MySQL 會不得不去執行 RAND()函數(很耗 CPU 時間),而且這是為了每一行記錄去記行,然後再對其排序。就算是你用了 Limit 1 也無濟於事(因為要排序)

下面的示例是隨機挑一條記錄

MySQL性能優化的21個最佳實踐

7.避 免 SELECT *

從數據庫裡讀出越多的數據,那麼查詢就會變得越慢。並且,如果你的數據庫服務器和 WEB 服務器是兩臺獨立的服務器的話,這還會增加網絡傳輸的負載。

所以,你應該養成一個需要什麼就取什麼的好的習慣。

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

MySQL性能優化的21個最佳實踐

從上圖你可以看到那個搜索字串 “last_name LIKE ‘a%’”,一個是建了索引,一個是沒有索引,性能差了 4 倍左右。

另外,你應該也需要知道什麼樣的搜索是不能使用正常的索引的。例如, 當你需要在一篇大的文章中搜索一個詞時,如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜索關鍵詞或是 Tag 什麼的)

5.在 Join 表的時候使用相當類型的例,並將其索引

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 Join 的字段是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。

而且,這些被用來 Join 的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個 INT 字段 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

MySQL性能優化的21個最佳實踐

6.千萬不要 ORDER BY RAND()

想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的性能問題。

如果你真的想把返回的數據行打亂了,你有 N 種方法可以達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的下降。這裡的問題是:MySQL 會不得不去執行 RAND()函數(很耗 CPU 時間),而且這是為了每一行記錄去記行,然後再對其排序。就算是你用了 Limit 1 也無濟於事(因為要排序)

下面的示例是隨機挑一條記錄

MySQL性能優化的21個最佳實踐

7.避 免 SELECT *

從數據庫裡讀出越多的數據,那麼查詢就會變得越慢。並且,如果你的數據庫服務器和 WEB 服務器是兩臺獨立的服務器的話,這還會增加網絡傳輸的負載。

所以,你應該養成一個需要什麼就取什麼的好的習慣。

MySQL性能優化的21個最佳實踐

8.永遠為每張表設置一個 ID

我們應該為數據庫裡的每張表都設置一個 ID 做為其主鍵,而且最好的是一個 INT 型的(推薦使用 UNSIGNED),並設置上自動增加的 AUTO_INCREMENT 標

志。

就算是你 users 表有一個主鍵叫 “email”的字段,你也別讓它成為主鍵。使用 VARCHAR 類型來當主鍵會使用得性能下降。另外,在你的程序中,你應該使用表的 ID 來構造你的數據結構。

而且,在 MySQL 數據引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設置變得非常重要,比如,集群,分區……

在這裡,只有一個情況是例外,那就是“關聯表”的“外鍵”,也就是 說,這個表的主鍵,通過若干個別的表的主鍵構成。我們把這個情況叫做“外

鍵”。比如:有一個“學生表”有學生的 ID,有一個“課程表”有課程 ID,那麼,“成績表”就是“關聯表”了,其關聯了學生表和課程表,在成績表中, 學生 ID 和課程 ID 叫“外鍵”其共同組成主鍵。

9.使用 ENUM 而不是 VARCHAR

ENUM 類型是非常快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來,用這個字段來做一些選項列表變得相當的完美。

如果你有一個字段,比如“性別”,“國家”,“民族”,“狀態”或

“部門”,你知道這些字段的取值是有限而且固定的,那麼,你應該使用 ENUM

而不是 VARCHAR。

MySQL 也有一個“建議”(見第十條)告訴你怎麼去重新組織你的表結構。當你有一個 VARCHAR 字段時,這個建議會告訴你把其改成 ENUM 類型。使用PROCEDURE ANALYSE() 你可以得到相關的建議。

10.從 PROCEDURE ANALYSE() 取得建議

PROCEDURE ANALYSE() 會讓 MySQL 幫你去分析你的字段和其實際的數據,並會給你一些有用的建議。只有表中有實際的數據,這些建議才會變得有用,因為要做一些大的決定是需要有數據作為基礎的。

例如,如果你創建了一個 INT 字段作為你的主鍵,然而並沒有太多的數據,那麼,PROCEDURE ANALYSE()會建議你把這個字段的類型改成 MEDIUMINT 。或是你使用了一個 VARCHAR 字段,因為數據不多,你可能會得到一個讓你把它改成 ENUM 的建議。這些建議,都是可能因為數據不夠多,所以決策做得就不夠準。

在 phpmyadmin 裡,你可以在查看錶時,點擊 “Propose table structure” 來查看這些建議

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

MySQL性能優化的21個最佳實踐

從上圖你可以看到那個搜索字串 “last_name LIKE ‘a%’”,一個是建了索引,一個是沒有索引,性能差了 4 倍左右。

另外,你應該也需要知道什麼樣的搜索是不能使用正常的索引的。例如, 當你需要在一篇大的文章中搜索一個詞時,如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜索關鍵詞或是 Tag 什麼的)

5.在 Join 表的時候使用相當類型的例,並將其索引

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 Join 的字段是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。

而且,這些被用來 Join 的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個 INT 字段 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

MySQL性能優化的21個最佳實踐

6.千萬不要 ORDER BY RAND()

想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的性能問題。

如果你真的想把返回的數據行打亂了,你有 N 種方法可以達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的下降。這裡的問題是:MySQL 會不得不去執行 RAND()函數(很耗 CPU 時間),而且這是為了每一行記錄去記行,然後再對其排序。就算是你用了 Limit 1 也無濟於事(因為要排序)

下面的示例是隨機挑一條記錄

MySQL性能優化的21個最佳實踐

7.避 免 SELECT *

從數據庫裡讀出越多的數據,那麼查詢就會變得越慢。並且,如果你的數據庫服務器和 WEB 服務器是兩臺獨立的服務器的話,這還會增加網絡傳輸的負載。

所以,你應該養成一個需要什麼就取什麼的好的習慣。

MySQL性能優化的21個最佳實踐

8.永遠為每張表設置一個 ID

我們應該為數據庫裡的每張表都設置一個 ID 做為其主鍵,而且最好的是一個 INT 型的(推薦使用 UNSIGNED),並設置上自動增加的 AUTO_INCREMENT 標

志。

就算是你 users 表有一個主鍵叫 “email”的字段,你也別讓它成為主鍵。使用 VARCHAR 類型來當主鍵會使用得性能下降。另外,在你的程序中,你應該使用表的 ID 來構造你的數據結構。

而且,在 MySQL 數據引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設置變得非常重要,比如,集群,分區……

在這裡,只有一個情況是例外,那就是“關聯表”的“外鍵”,也就是 說,這個表的主鍵,通過若干個別的表的主鍵構成。我們把這個情況叫做“外

鍵”。比如:有一個“學生表”有學生的 ID,有一個“課程表”有課程 ID,那麼,“成績表”就是“關聯表”了,其關聯了學生表和課程表,在成績表中, 學生 ID 和課程 ID 叫“外鍵”其共同組成主鍵。

9.使用 ENUM 而不是 VARCHAR

ENUM 類型是非常快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來,用這個字段來做一些選項列表變得相當的完美。

如果你有一個字段,比如“性別”,“國家”,“民族”,“狀態”或

“部門”,你知道這些字段的取值是有限而且固定的,那麼,你應該使用 ENUM

而不是 VARCHAR。

MySQL 也有一個“建議”(見第十條)告訴你怎麼去重新組織你的表結構。當你有一個 VARCHAR 字段時,這個建議會告訴你把其改成 ENUM 類型。使用PROCEDURE ANALYSE() 你可以得到相關的建議。

10.從 PROCEDURE ANALYSE() 取得建議

PROCEDURE ANALYSE() 會讓 MySQL 幫你去分析你的字段和其實際的數據,並會給你一些有用的建議。只有表中有實際的數據,這些建議才會變得有用,因為要做一些大的決定是需要有數據作為基礎的。

例如,如果你創建了一個 INT 字段作為你的主鍵,然而並沒有太多的數據,那麼,PROCEDURE ANALYSE()會建議你把這個字段的類型改成 MEDIUMINT 。或是你使用了一個 VARCHAR 字段,因為數據不多,你可能會得到一個讓你把它改成 ENUM 的建議。這些建議,都是可能因為數據不夠多,所以決策做得就不夠準。

在 phpmyadmin 裡,你可以在查看錶時,點擊 “Propose table structure” 來查看這些建議

MySQL性能優化的21個最佳實踐

一定要注意,這些只是建議,只有當你的表裡的數據越來越多時,這些建議才會變得準確。一定要記住,你才是最終做決定的人。

11.儘可能的使用 NOT NULL

除非你有一個很特別的原因去使用 NULL 值,你應該總是讓你的字段保持NOT NULL。這看起來好像有點爭議,請往下看。

首先,問問你自己“Empty”和“NULL”有多大的區別(如果是 INT,那就是 0 和 NULL)?如果你覺得它們之間沒有什麼區別,那麼你就不要使用 NULL。(你知道嗎?在 Oracle 裡,NULL 和 Empty 的字符串是一樣的!)

不要以為 NULL 不需要空間,其需要額外的空間,並且,在你進行比較的時候,你的程序會更復雜。 當然,這裡並不是說你就不能使用 NULL 了,現實情況是很複雜的,依然會有些情況下,你需要使用 NULL 值。

12.Prepared Statements

Prepared Statements 很像存儲過程,是一種運行在後臺的 SQL 語句集合, 我們可以從使用 prepared statements 獲得很多好處,無論是性能問題還是安全問題。

Prepared Statements 可以檢查一些你綁定好的變量,這樣可以保護你的程序不會受到“SQL 注入式”攻擊。當然,你也可以手動地檢查你的這些變量, 然而,手動的檢查容易出問題,而且很經常會被程序員忘了。當我們使用一些framework 或是 ORM 的時候,這樣的問題會好一些。

在性能方面,當一個相同的查詢被使用多次的時候,這會為你帶來可觀的性能優勢。你可以給這些 Prepared Statements 定義一些參數,而 MySQL 只會解析一次。

雖然最新版本的 MySQL 在傳輸 Prepared Statements 是使用二進制形勢, 所以這會使得網絡傳輸非常有效率。

當然,也有一些情況下,我們需要避免使用 Prepared Statements,因為其不支持查詢緩存。但據說版本 5.1 後支持了。

在 PHP 中要使用 prepared statements,你可以查看其使用手冊:mysqli

擴展 或是使用數據庫抽象層,如: PDO.

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

MySQL性能優化的21個最佳實踐

從上圖你可以看到那個搜索字串 “last_name LIKE ‘a%’”,一個是建了索引,一個是沒有索引,性能差了 4 倍左右。

另外,你應該也需要知道什麼樣的搜索是不能使用正常的索引的。例如, 當你需要在一篇大的文章中搜索一個詞時,如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜索關鍵詞或是 Tag 什麼的)

5.在 Join 表的時候使用相當類型的例,並將其索引

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 Join 的字段是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。

而且,這些被用來 Join 的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個 INT 字段 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

MySQL性能優化的21個最佳實踐

6.千萬不要 ORDER BY RAND()

想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的性能問題。

如果你真的想把返回的數據行打亂了,你有 N 種方法可以達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的下降。這裡的問題是:MySQL 會不得不去執行 RAND()函數(很耗 CPU 時間),而且這是為了每一行記錄去記行,然後再對其排序。就算是你用了 Limit 1 也無濟於事(因為要排序)

下面的示例是隨機挑一條記錄

MySQL性能優化的21個最佳實踐

7.避 免 SELECT *

從數據庫裡讀出越多的數據,那麼查詢就會變得越慢。並且,如果你的數據庫服務器和 WEB 服務器是兩臺獨立的服務器的話,這還會增加網絡傳輸的負載。

所以,你應該養成一個需要什麼就取什麼的好的習慣。

MySQL性能優化的21個最佳實踐

8.永遠為每張表設置一個 ID

我們應該為數據庫裡的每張表都設置一個 ID 做為其主鍵,而且最好的是一個 INT 型的(推薦使用 UNSIGNED),並設置上自動增加的 AUTO_INCREMENT 標

志。

就算是你 users 表有一個主鍵叫 “email”的字段,你也別讓它成為主鍵。使用 VARCHAR 類型來當主鍵會使用得性能下降。另外,在你的程序中,你應該使用表的 ID 來構造你的數據結構。

而且,在 MySQL 數據引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設置變得非常重要,比如,集群,分區……

在這裡,只有一個情況是例外,那就是“關聯表”的“外鍵”,也就是 說,這個表的主鍵,通過若干個別的表的主鍵構成。我們把這個情況叫做“外

鍵”。比如:有一個“學生表”有學生的 ID,有一個“課程表”有課程 ID,那麼,“成績表”就是“關聯表”了,其關聯了學生表和課程表,在成績表中, 學生 ID 和課程 ID 叫“外鍵”其共同組成主鍵。

9.使用 ENUM 而不是 VARCHAR

ENUM 類型是非常快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來,用這個字段來做一些選項列表變得相當的完美。

如果你有一個字段,比如“性別”,“國家”,“民族”,“狀態”或

“部門”,你知道這些字段的取值是有限而且固定的,那麼,你應該使用 ENUM

而不是 VARCHAR。

MySQL 也有一個“建議”(見第十條)告訴你怎麼去重新組織你的表結構。當你有一個 VARCHAR 字段時,這個建議會告訴你把其改成 ENUM 類型。使用PROCEDURE ANALYSE() 你可以得到相關的建議。

10.從 PROCEDURE ANALYSE() 取得建議

PROCEDURE ANALYSE() 會讓 MySQL 幫你去分析你的字段和其實際的數據,並會給你一些有用的建議。只有表中有實際的數據,這些建議才會變得有用,因為要做一些大的決定是需要有數據作為基礎的。

例如,如果你創建了一個 INT 字段作為你的主鍵,然而並沒有太多的數據,那麼,PROCEDURE ANALYSE()會建議你把這個字段的類型改成 MEDIUMINT 。或是你使用了一個 VARCHAR 字段,因為數據不多,你可能會得到一個讓你把它改成 ENUM 的建議。這些建議,都是可能因為數據不夠多,所以決策做得就不夠準。

在 phpmyadmin 裡,你可以在查看錶時,點擊 “Propose table structure” 來查看這些建議

MySQL性能優化的21個最佳實踐

一定要注意,這些只是建議,只有當你的表裡的數據越來越多時,這些建議才會變得準確。一定要記住,你才是最終做決定的人。

11.儘可能的使用 NOT NULL

除非你有一個很特別的原因去使用 NULL 值,你應該總是讓你的字段保持NOT NULL。這看起來好像有點爭議,請往下看。

首先,問問你自己“Empty”和“NULL”有多大的區別(如果是 INT,那就是 0 和 NULL)?如果你覺得它們之間沒有什麼區別,那麼你就不要使用 NULL。(你知道嗎?在 Oracle 裡,NULL 和 Empty 的字符串是一樣的!)

不要以為 NULL 不需要空間,其需要額外的空間,並且,在你進行比較的時候,你的程序會更復雜。 當然,這裡並不是說你就不能使用 NULL 了,現實情況是很複雜的,依然會有些情況下,你需要使用 NULL 值。

12.Prepared Statements

Prepared Statements 很像存儲過程,是一種運行在後臺的 SQL 語句集合, 我們可以從使用 prepared statements 獲得很多好處,無論是性能問題還是安全問題。

Prepared Statements 可以檢查一些你綁定好的變量,這樣可以保護你的程序不會受到“SQL 注入式”攻擊。當然,你也可以手動地檢查你的這些變量, 然而,手動的檢查容易出問題,而且很經常會被程序員忘了。當我們使用一些framework 或是 ORM 的時候,這樣的問題會好一些。

在性能方面,當一個相同的查詢被使用多次的時候,這會為你帶來可觀的性能優勢。你可以給這些 Prepared Statements 定義一些參數,而 MySQL 只會解析一次。

雖然最新版本的 MySQL 在傳輸 Prepared Statements 是使用二進制形勢, 所以這會使得網絡傳輸非常有效率。

當然,也有一些情況下,我們需要避免使用 Prepared Statements,因為其不支持查詢緩存。但據說版本 5.1 後支持了。

在 PHP 中要使用 prepared statements,你可以查看其使用手冊:mysqli

擴展 或是使用數據庫抽象層,如: PDO.

MySQL性能優化的21個最佳實踐

13.無緩衝的查詢

正常的情況下,當你在當你在你的腳本中執行一個 SQL 語句的時候,你的程序會停在那裡直到沒這個 SQL 語句返回,然後你的程序再往下繼續執行。你可以使用無緩衝查詢來改變這個行為。

mysql_unbuffered_query() 發送一個 SQL 語句到 MySQL 而並不像mysql_query()一樣去自動 fethch 和緩存結果。這會相當節約很多可觀的內存,尤其是那些會產生大量結果的查詢語句,並且,你不需要等到所有的結果都返回,只需要第一行數據返回的時候,你就可以開始馬上開始工作於查詢結果了。

然而,這會有一些限制。因為你要麼把所有行都讀走,或是你要在進行下 一次的查詢前調用 mysql_free_result() 清除結果。而且, mysql_num_rows() 或 mysql_data_seek() 將無法使用。所以,是否使用無緩衝的查詢你需要仔細考慮。

14.把 IP 地址存成 UNSIGNED INT

很多程序員都會創建一個 VARCHAR(15) 字段來存放字符串形式的 IP 而不是整形的 IP。如果你用整形來存放,只需要 4 個字節,並且你可以有定長的字段。而且,這會為你帶來查詢上的優勢,尤其是當你需要使用這樣的 WHERE 條件:IP between ip1 and ip2。

我們必需要使用 UNSIGNED INT,因為 IP 地址會使用整個 32 位的無符號整形。

而你的查詢,你可以使用 INET_ATON() 來把一個字符串 IP 轉成一個整形, 並使用 INET_NTOA() 把一個整形轉成一個字符串 IP。在 PHP 中,也有這樣的函數 ip2long() 和 long2ip()。

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

MySQL性能優化的21個最佳實踐

從上圖你可以看到那個搜索字串 “last_name LIKE ‘a%’”,一個是建了索引,一個是沒有索引,性能差了 4 倍左右。

另外,你應該也需要知道什麼樣的搜索是不能使用正常的索引的。例如, 當你需要在一篇大的文章中搜索一個詞時,如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜索關鍵詞或是 Tag 什麼的)

5.在 Join 表的時候使用相當類型的例,並將其索引

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 Join 的字段是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。

而且,這些被用來 Join 的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個 INT 字段 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

MySQL性能優化的21個最佳實踐

6.千萬不要 ORDER BY RAND()

想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的性能問題。

如果你真的想把返回的數據行打亂了,你有 N 種方法可以達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的下降。這裡的問題是:MySQL 會不得不去執行 RAND()函數(很耗 CPU 時間),而且這是為了每一行記錄去記行,然後再對其排序。就算是你用了 Limit 1 也無濟於事(因為要排序)

下面的示例是隨機挑一條記錄

MySQL性能優化的21個最佳實踐

7.避 免 SELECT *

從數據庫裡讀出越多的數據,那麼查詢就會變得越慢。並且,如果你的數據庫服務器和 WEB 服務器是兩臺獨立的服務器的話,這還會增加網絡傳輸的負載。

所以,你應該養成一個需要什麼就取什麼的好的習慣。

MySQL性能優化的21個最佳實踐

8.永遠為每張表設置一個 ID

我們應該為數據庫裡的每張表都設置一個 ID 做為其主鍵,而且最好的是一個 INT 型的(推薦使用 UNSIGNED),並設置上自動增加的 AUTO_INCREMENT 標

志。

就算是你 users 表有一個主鍵叫 “email”的字段,你也別讓它成為主鍵。使用 VARCHAR 類型來當主鍵會使用得性能下降。另外,在你的程序中,你應該使用表的 ID 來構造你的數據結構。

而且,在 MySQL 數據引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設置變得非常重要,比如,集群,分區……

在這裡,只有一個情況是例外,那就是“關聯表”的“外鍵”,也就是 說,這個表的主鍵,通過若干個別的表的主鍵構成。我們把這個情況叫做“外

鍵”。比如:有一個“學生表”有學生的 ID,有一個“課程表”有課程 ID,那麼,“成績表”就是“關聯表”了,其關聯了學生表和課程表,在成績表中, 學生 ID 和課程 ID 叫“外鍵”其共同組成主鍵。

9.使用 ENUM 而不是 VARCHAR

ENUM 類型是非常快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來,用這個字段來做一些選項列表變得相當的完美。

如果你有一個字段,比如“性別”,“國家”,“民族”,“狀態”或

“部門”,你知道這些字段的取值是有限而且固定的,那麼,你應該使用 ENUM

而不是 VARCHAR。

MySQL 也有一個“建議”(見第十條)告訴你怎麼去重新組織你的表結構。當你有一個 VARCHAR 字段時,這個建議會告訴你把其改成 ENUM 類型。使用PROCEDURE ANALYSE() 你可以得到相關的建議。

10.從 PROCEDURE ANALYSE() 取得建議

PROCEDURE ANALYSE() 會讓 MySQL 幫你去分析你的字段和其實際的數據,並會給你一些有用的建議。只有表中有實際的數據,這些建議才會變得有用,因為要做一些大的決定是需要有數據作為基礎的。

例如,如果你創建了一個 INT 字段作為你的主鍵,然而並沒有太多的數據,那麼,PROCEDURE ANALYSE()會建議你把這個字段的類型改成 MEDIUMINT 。或是你使用了一個 VARCHAR 字段,因為數據不多,你可能會得到一個讓你把它改成 ENUM 的建議。這些建議,都是可能因為數據不夠多,所以決策做得就不夠準。

在 phpmyadmin 裡,你可以在查看錶時,點擊 “Propose table structure” 來查看這些建議

MySQL性能優化的21個最佳實踐

一定要注意,這些只是建議,只有當你的表裡的數據越來越多時,這些建議才會變得準確。一定要記住,你才是最終做決定的人。

11.儘可能的使用 NOT NULL

除非你有一個很特別的原因去使用 NULL 值,你應該總是讓你的字段保持NOT NULL。這看起來好像有點爭議,請往下看。

首先,問問你自己“Empty”和“NULL”有多大的區別(如果是 INT,那就是 0 和 NULL)?如果你覺得它們之間沒有什麼區別,那麼你就不要使用 NULL。(你知道嗎?在 Oracle 裡,NULL 和 Empty 的字符串是一樣的!)

不要以為 NULL 不需要空間,其需要額外的空間,並且,在你進行比較的時候,你的程序會更復雜。 當然,這裡並不是說你就不能使用 NULL 了,現實情況是很複雜的,依然會有些情況下,你需要使用 NULL 值。

12.Prepared Statements

Prepared Statements 很像存儲過程,是一種運行在後臺的 SQL 語句集合, 我們可以從使用 prepared statements 獲得很多好處,無論是性能問題還是安全問題。

Prepared Statements 可以檢查一些你綁定好的變量,這樣可以保護你的程序不會受到“SQL 注入式”攻擊。當然,你也可以手動地檢查你的這些變量, 然而,手動的檢查容易出問題,而且很經常會被程序員忘了。當我們使用一些framework 或是 ORM 的時候,這樣的問題會好一些。

在性能方面,當一個相同的查詢被使用多次的時候,這會為你帶來可觀的性能優勢。你可以給這些 Prepared Statements 定義一些參數,而 MySQL 只會解析一次。

雖然最新版本的 MySQL 在傳輸 Prepared Statements 是使用二進制形勢, 所以這會使得網絡傳輸非常有效率。

當然,也有一些情況下,我們需要避免使用 Prepared Statements,因為其不支持查詢緩存。但據說版本 5.1 後支持了。

在 PHP 中要使用 prepared statements,你可以查看其使用手冊:mysqli

擴展 或是使用數據庫抽象層,如: PDO.

MySQL性能優化的21個最佳實踐

13.無緩衝的查詢

正常的情況下,當你在當你在你的腳本中執行一個 SQL 語句的時候,你的程序會停在那裡直到沒這個 SQL 語句返回,然後你的程序再往下繼續執行。你可以使用無緩衝查詢來改變這個行為。

mysql_unbuffered_query() 發送一個 SQL 語句到 MySQL 而並不像mysql_query()一樣去自動 fethch 和緩存結果。這會相當節約很多可觀的內存,尤其是那些會產生大量結果的查詢語句,並且,你不需要等到所有的結果都返回,只需要第一行數據返回的時候,你就可以開始馬上開始工作於查詢結果了。

然而,這會有一些限制。因為你要麼把所有行都讀走,或是你要在進行下 一次的查詢前調用 mysql_free_result() 清除結果。而且, mysql_num_rows() 或 mysql_data_seek() 將無法使用。所以,是否使用無緩衝的查詢你需要仔細考慮。

14.把 IP 地址存成 UNSIGNED INT

很多程序員都會創建一個 VARCHAR(15) 字段來存放字符串形式的 IP 而不是整形的 IP。如果你用整形來存放,只需要 4 個字節,並且你可以有定長的字段。而且,這會為你帶來查詢上的優勢,尤其是當你需要使用這樣的 WHERE 條件:IP between ip1 and ip2。

我們必需要使用 UNSIGNED INT,因為 IP 地址會使用整個 32 位的無符號整形。

而你的查詢,你可以使用 INET_ATON() 來把一個字符串 IP 轉成一個整形, 並使用 INET_NTOA() 把一個整形轉成一個字符串 IP。在 PHP 中,也有這樣的函數 ip2long() 和 long2ip()。

MySQL性能優化的21個最佳實踐

15.固定長度的表會更快

如果表中的所有字段都是“固定長度”的,整個表會被認為是 “static” 或 “fixed-length”。 例如,表中沒有如下類型的字段: VARCHAR,TEXT, BLOB。只要你包括了其中一個這些字段,那麼這個表就不是“固定長度靜態表”了,這樣,MySQL 引擎會用另一種方法來處理。

固定長度的表會提高性能,因為 MySQL 搜尋得會更快一些,因為這些固定的長度是很容易計算下一個數據的偏移量的,所以讀取的自然也會很快。而如果字段不是定長的,那麼,每一次要找下一條的話,需要程序找到主鍵。

並且,固定長度的表也更容易被緩存和重建。不過,唯一的副作用是,固定長度的字段會浪費一些空間,因為定長的字段無論你用不用,他都是要分配那麼多的空間。

使用“垂直分割”技術(見下一條),你可以分割你的表成為兩個一個是定長的,一個則是不定長的。

16.垂直分割

“垂直分割”是一種把數據庫中的表按列變成幾張表的方法,這樣可以降低表的複雜度和字段的數目,從而達到優化的目的。(以前,在銀行做過項目, 見過一張表有 100 多個字段,很恐怖)

示例一:在 Users 表中有一個字段是家庭地址,這個字段是可選字段,相比起,而且你在數據庫操作的時候除了個人信息外,你並不需要經常讀取或是改寫這個字段。那麼,為什麼不把他放到另外一張表中呢? 這樣會讓你的表有更好的性能,大家想想是不是,大量的時候,我對於用戶表來說,只有用戶ID,用戶名,口令,用戶角色等會被經常使用。小一點的表總是會有好的性 能。

示例二: 你有一個叫 “last_login” 的字段,它會在每次用戶登錄時被更新。但是,每次更新時會導致該表的查詢緩存被清空。所以,你可以把這個字段放到另一個表中,這樣就不會影響你對用戶 ID,用戶名,用戶角色的不停地讀取了,因為查詢緩存會幫你增加很多性能。

另外,你需要注意的是,這些被分出去的字段所形成的表,你不會經常性地去 Join 他們,不然的話,這樣的性能會比不分割時還要差,而且,會是極數級的下降。

17.拆分大的 DELETE 或 INSERT 語句

如果你需要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢, 你需要非常小心,要避免你的操作讓你的整個網站停止相應。因為這兩個操作是會鎖表的,表一鎖住了,別的操作都進不來了。

Apache 會有很多的子進程或線程。所以,其工作起來相當有效率,而我們的服務器也不希望有太多的子進程,線程和數據庫鏈接,這是極大的佔服務器資源的事情,尤其是內存。

如果你把你的表鎖上一段時間,比如 30 秒鐘,那麼對於一個有很高訪問量的站點來說,這 30 秒所積累的訪問進程/線程,數據庫鏈接,打開的文件數, 可能不僅僅會讓你泊 WEB 服務 Crash,還可能會讓你的整臺服務器馬上掛了。

所以,如果你有一個大的處理,你定你一定把其拆分,使用 LIMIT 條件是一個好的方法。下面是一個示例:

"

MySQL 性能優化的 21 個最佳實踐

今天,數據庫的操作越來越成為整個應用的性能瓶頸了,這點對於 Web 應用尤其明顯。關於數據庫的性能,這並不只是 DBA 才需要擔心的事,而這更是我們程序員需要去關注的事情。當我們去設計數據庫表結構,對操作數據庫時(尤其是查表時的 SQL 語句),我們都需要注意數據操作的性能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的數據庫。希望下面的這些優化技巧對你有用。

1.為查詢緩存優化你的查詢

大多數的 MySQL 服務器都開啟了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裡最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

MySQL性能優化的21個最佳實踐

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢緩存,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變量來代替 MySQL 的函數,從而開啟緩存。

2.EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。

EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個示例中,我們忘記加上了 group_id 索引,並且有表聯接:

MySQL性能優化的21個最佳實踐

當我們為 group_id 字段加上索引後:

MySQL性能優化的21個最佳實踐

我們可以看到,前一個結果顯示搜索了 7883 行,而後一個只是搜索了兩個表的 9 和 16 行。查看 rows 列可以讓我們找到潛在的性能問題。

3.當只要一行數據時使用 LIMIT 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

下面的示例,只是為了找一下是否有“中國”的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

MySQL性能優化的21個最佳實踐

4.為搜索字段建索引

索引並不一定就是給主鍵或是唯一的字段。如果在你的表中,有某個字段你總要會經常用來做搜索,那麼,請為其建立索引吧。

MySQL性能優化的21個最佳實踐

從上圖你可以看到那個搜索字串 “last_name LIKE ‘a%’”,一個是建了索引,一個是沒有索引,性能差了 4 倍左右。

另外,你應該也需要知道什麼樣的搜索是不能使用正常的索引的。例如, 當你需要在一篇大的文章中搜索一個詞時,如: “WHERE post_content LIKE ‘%apple%’”,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜索關鍵詞或是 Tag 什麼的)

5.在 Join 表的時候使用相當類型的例,並將其索引

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 Join 的字段是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。

而且,這些被用來 Join 的字段,應該是相同的類型的。例如:如果你要把DECIMAL 字段和一個 INT 字段 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 類型,還需要有相同的字符集才行。(兩個表的字符集有可能不一樣)

MySQL性能優化的21個最佳實踐

6.千萬不要 ORDER BY RAND()

想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的性能問題。

如果你真的想把返回的數據行打亂了,你有 N 種方法可以達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的下降。這裡的問題是:MySQL 會不得不去執行 RAND()函數(很耗 CPU 時間),而且這是為了每一行記錄去記行,然後再對其排序。就算是你用了 Limit 1 也無濟於事(因為要排序)

下面的示例是隨機挑一條記錄

MySQL性能優化的21個最佳實踐

7.避 免 SELECT *

從數據庫裡讀出越多的數據,那麼查詢就會變得越慢。並且,如果你的數據庫服務器和 WEB 服務器是兩臺獨立的服務器的話,這還會增加網絡傳輸的負載。

所以,你應該養成一個需要什麼就取什麼的好的習慣。

MySQL性能優化的21個最佳實踐

8.永遠為每張表設置一個 ID

我們應該為數據庫裡的每張表都設置一個 ID 做為其主鍵,而且最好的是一個 INT 型的(推薦使用 UNSIGNED),並設置上自動增加的 AUTO_INCREMENT 標

志。

就算是你 users 表有一個主鍵叫 “email”的字段,你也別讓它成為主鍵。使用 VARCHAR 類型來當主鍵會使用得性能下降。另外,在你的程序中,你應該使用表的 ID 來構造你的數據結構。

而且,在 MySQL 數據引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設置變得非常重要,比如,集群,分區……

在這裡,只有一個情況是例外,那就是“關聯表”的“外鍵”,也就是 說,這個表的主鍵,通過若干個別的表的主鍵構成。我們把這個情況叫做“外

鍵”。比如:有一個“學生表”有學生的 ID,有一個“課程表”有課程 ID,那麼,“成績表”就是“關聯表”了,其關聯了學生表和課程表,在成績表中, 學生 ID 和課程 ID 叫“外鍵”其共同組成主鍵。

9.使用 ENUM 而不是 VARCHAR

ENUM 類型是非常快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示為字符串。這樣一來,用這個字段來做一些選項列表變得相當的完美。

如果你有一個字段,比如“性別”,“國家”,“民族”,“狀態”或

“部門”,你知道這些字段的取值是有限而且固定的,那麼,你應該使用 ENUM

而不是 VARCHAR。

MySQL 也有一個“建議”(見第十條)告訴你怎麼去重新組織你的表結構。當你有一個 VARCHAR 字段時,這個建議會告訴你把其改成 ENUM 類型。使用PROCEDURE ANALYSE() 你可以得到相關的建議。

10.從 PROCEDURE ANALYSE() 取得建議

PROCEDURE ANALYSE() 會讓 MySQL 幫你去分析你的字段和其實際的數據,並會給你一些有用的建議。只有表中有實際的數據,這些建議才會變得有用,因為要做一些大的決定是需要有數據作為基礎的。

例如,如果你創建了一個 INT 字段作為你的主鍵,然而並沒有太多的數據,那麼,PROCEDURE ANALYSE()會建議你把這個字段的類型改成 MEDIUMINT 。或是你使用了一個 VARCHAR 字段,因為數據不多,你可能會得到一個讓你把它改成 ENUM 的建議。這些建議,都是可能因為數據不夠多,所以決策做得就不夠準。

在 phpmyadmin 裡,你可以在查看錶時,點擊 “Propose table structure” 來查看這些建議

MySQL性能優化的21個最佳實踐

一定要注意,這些只是建議,只有當你的表裡的數據越來越多時,這些建議才會變得準確。一定要記住,你才是最終做決定的人。

11.儘可能的使用 NOT NULL

除非你有一個很特別的原因去使用 NULL 值,你應該總是讓你的字段保持NOT NULL。這看起來好像有點爭議,請往下看。

首先,問問你自己“Empty”和“NULL”有多大的區別(如果是 INT,那就是 0 和 NULL)?如果你覺得它們之間沒有什麼區別,那麼你就不要使用 NULL。(你知道嗎?在 Oracle 裡,NULL 和 Empty 的字符串是一樣的!)

不要以為 NULL 不需要空間,其需要額外的空間,並且,在你進行比較的時候,你的程序會更復雜。 當然,這裡並不是說你就不能使用 NULL 了,現實情況是很複雜的,依然會有些情況下,你需要使用 NULL 值。

12.Prepared Statements

Prepared Statements 很像存儲過程,是一種運行在後臺的 SQL 語句集合, 我們可以從使用 prepared statements 獲得很多好處,無論是性能問題還是安全問題。

Prepared Statements 可以檢查一些你綁定好的變量,這樣可以保護你的程序不會受到“SQL 注入式”攻擊。當然,你也可以手動地檢查你的這些變量, 然而,手動的檢查容易出問題,而且很經常會被程序員忘了。當我們使用一些framework 或是 ORM 的時候,這樣的問題會好一些。

在性能方面,當一個相同的查詢被使用多次的時候,這會為你帶來可觀的性能優勢。你可以給這些 Prepared Statements 定義一些參數,而 MySQL 只會解析一次。

雖然最新版本的 MySQL 在傳輸 Prepared Statements 是使用二進制形勢, 所以這會使得網絡傳輸非常有效率。

當然,也有一些情況下,我們需要避免使用 Prepared Statements,因為其不支持查詢緩存。但據說版本 5.1 後支持了。

在 PHP 中要使用 prepared statements,你可以查看其使用手冊:mysqli

擴展 或是使用數據庫抽象層,如: PDO.

MySQL性能優化的21個最佳實踐

13.無緩衝的查詢

正常的情況下,當你在當你在你的腳本中執行一個 SQL 語句的時候,你的程序會停在那裡直到沒這個 SQL 語句返回,然後你的程序再往下繼續執行。你可以使用無緩衝查詢來改變這個行為。

mysql_unbuffered_query() 發送一個 SQL 語句到 MySQL 而並不像mysql_query()一樣去自動 fethch 和緩存結果。這會相當節約很多可觀的內存,尤其是那些會產生大量結果的查詢語句,並且,你不需要等到所有的結果都返回,只需要第一行數據返回的時候,你就可以開始馬上開始工作於查詢結果了。

然而,這會有一些限制。因為你要麼把所有行都讀走,或是你要在進行下 一次的查詢前調用 mysql_free_result() 清除結果。而且, mysql_num_rows() 或 mysql_data_seek() 將無法使用。所以,是否使用無緩衝的查詢你需要仔細考慮。

14.把 IP 地址存成 UNSIGNED INT

很多程序員都會創建一個 VARCHAR(15) 字段來存放字符串形式的 IP 而不是整形的 IP。如果你用整形來存放,只需要 4 個字節,並且你可以有定長的字段。而且,這會為你帶來查詢上的優勢,尤其是當你需要使用這樣的 WHERE 條件:IP between ip1 and ip2。

我們必需要使用 UNSIGNED INT,因為 IP 地址會使用整個 32 位的無符號整形。

而你的查詢,你可以使用 INET_ATON() 來把一個字符串 IP 轉成一個整形, 並使用 INET_NTOA() 把一個整形轉成一個字符串 IP。在 PHP 中,也有這樣的函數 ip2long() 和 long2ip()。

MySQL性能優化的21個最佳實踐

15.固定長度的表會更快

如果表中的所有字段都是“固定長度”的,整個表會被認為是 “static” 或 “fixed-length”。 例如,表中沒有如下類型的字段: VARCHAR,TEXT, BLOB。只要你包括了其中一個這些字段,那麼這個表就不是“固定長度靜態表”了,這樣,MySQL 引擎會用另一種方法來處理。

固定長度的表會提高性能,因為 MySQL 搜尋得會更快一些,因為這些固定的長度是很容易計算下一個數據的偏移量的,所以讀取的自然也會很快。而如果字段不是定長的,那麼,每一次要找下一條的話,需要程序找到主鍵。

並且,固定長度的表也更容易被緩存和重建。不過,唯一的副作用是,固定長度的字段會浪費一些空間,因為定長的字段無論你用不用,他都是要分配那麼多的空間。

使用“垂直分割”技術(見下一條),你可以分割你的表成為兩個一個是定長的,一個則是不定長的。

16.垂直分割

“垂直分割”是一種把數據庫中的表按列變成幾張表的方法,這樣可以降低表的複雜度和字段的數目,從而達到優化的目的。(以前,在銀行做過項目, 見過一張表有 100 多個字段,很恐怖)

示例一:在 Users 表中有一個字段是家庭地址,這個字段是可選字段,相比起,而且你在數據庫操作的時候除了個人信息外,你並不需要經常讀取或是改寫這個字段。那麼,為什麼不把他放到另外一張表中呢? 這樣會讓你的表有更好的性能,大家想想是不是,大量的時候,我對於用戶表來說,只有用戶ID,用戶名,口令,用戶角色等會被經常使用。小一點的表總是會有好的性 能。

示例二: 你有一個叫 “last_login” 的字段,它會在每次用戶登錄時被更新。但是,每次更新時會導致該表的查詢緩存被清空。所以,你可以把這個字段放到另一個表中,這樣就不會影響你對用戶 ID,用戶名,用戶角色的不停地讀取了,因為查詢緩存會幫你增加很多性能。

另外,你需要注意的是,這些被分出去的字段所形成的表,你不會經常性地去 Join 他們,不然的話,這樣的性能會比不分割時還要差,而且,會是極數級的下降。

17.拆分大的 DELETE 或 INSERT 語句

如果你需要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢, 你需要非常小心,要避免你的操作讓你的整個網站停止相應。因為這兩個操作是會鎖表的,表一鎖住了,別的操作都進不來了。

Apache 會有很多的子進程或線程。所以,其工作起來相當有效率,而我們的服務器也不希望有太多的子進程,線程和數據庫鏈接,這是極大的佔服務器資源的事情,尤其是內存。

如果你把你的表鎖上一段時間,比如 30 秒鐘,那麼對於一個有很高訪問量的站點來說,這 30 秒所積累的訪問進程/線程,數據庫鏈接,打開的文件數, 可能不僅僅會讓你泊 WEB 服務 Crash,還可能會讓你的整臺服務器馬上掛了。

所以,如果你有一個大的處理,你定你一定把其拆分,使用 LIMIT 條件是一個好的方法。下面是一個示例:

MySQL性能優化的21個最佳實踐

18.越小的列會越快

對於大多數的數據庫引擎來說,硬盤操作可能是最重大的瓶頸。所以,把你的數據變得緊湊會對這種情況非常有幫助,因為這減少了對硬盤的訪問。

參看 MySQL 的文檔 Storage Requirements 查看所有的數據類型。

如果一個表只會有幾列罷了(比如說字典表,配置表),那麼,我們就沒有理由使用 INT 來做主鍵,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 會更經濟一些。如果你不需要記錄時間,使用 DATE 要比 DATETIME 好得多。

當然,你也需要留夠足夠的擴展空間,不然,你日後來幹這個事,你會死的很難看,參看 Slashdot 的例子(2009 年 11 月 06 日),一個簡單的 ALTER TABLE 語句花了 3 個多小時,因為裡面有一千六百萬條數據。

19.選擇正確的存儲引擎

在 MySQL 中有兩個存儲引擎 MyISAM 和 InnoDB,每個引擎都有利有弊。酷殼以前文章《MySQL: InnoDB 還是 MyISAM?》討論和這個事情。

MyISAM 適合於一些需要大量查詢的應用,但其對於有大量寫操作並不是很好。甚至你只是需要 update 一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都無法操作直到讀操作完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。

InnoDB 的趨勢會是一個非常複雜的存儲引擎,對於一些小的應用,它會比MyISAM 還慢。他是它支持“行鎖” ,於是在寫操作比較多的時候,會更優秀。並且,他還支持更多的高級應用,比如:事務。

下面是 MySQL 的手冊

target=”_blank”MyISAM Storage Engine 
InnoDB Storage Engine

20.使用一個對象關係映射器(Object Relational Mapper)

使用 ORM (Object Relational Mapper),你能夠獲得可靠的性能增漲。一個

ORM 可以做的所有事情,也能被手動的編寫出來。但是,這需要一個高級專家。

ORM 的最重要的是“Lazy Loading”,也就是說,只有在需要的去取值的時候才會去真正的去做。但你也需要小心這種機制的副作用,因為這很有可能會因為要去創建很多很多小的查詢反而會降低性能。

ORM 還可以把你的 SQL 語句打包成一個事務,這會比單獨執行他們快得多得多。

目前,個人最喜歡的 PHP 的 ORM 是:Doctrine。

21.小心“永久鏈接”

“永久鏈接”的目的是用來減少重新創建 MySQL 鏈接的次數。當一個鏈接被創建了,它會永遠處在連接的狀態,就算是數據庫操作已經結束了。而且, 自從我們的 Apache 開始重用它的子進程後——也就是說,下一次的 HTTP 請求會重用 Apache 的子進程,並重用相同的 MySQL 鏈接。

PHP 手冊:mysql_pconnect()

在理論上來說,這聽起來非常的不錯。但是從個人經驗(也是大多數人的) 上來說,這個功能製造出來的麻煩事更多。因為,你只有有限的鏈接數,內存問題,文件句柄數,等等。

而且,Apache 運行在極端並行的環境中,會創建很多很多的了進程。這就是為什麼這種“永久鏈接”的機制工作地不好的原因。在你決定要使用“永久鏈接”之前,你需要好好地考慮一下你的整個系統的架構。

"

相關推薦

推薦中...