論mysql5.7.13性能優化之索引優化

防僞碼:吾日三省吾身


一、MySQL 性能優化之-影響性能的因素

1. 商業需求的影響

不合理需求造成資源投入產出比過低,這裏我們就用一個看上去很簡單的功能來分析一下。

需求:一個論壇帖子總量的統計,附加要求:實時更新

從功能上來看非常容易實現,執行一條 SELECT COUNT(*) from 表名 Query 就可以得到結

果。但是,如果我們採用不是 MyISAM 存儲引擎,而是使用的 Innodb 的存儲引擎,那麼大

家可以試想一下,如果存放帖子的表中已經有上千萬的帖子的時候,執行這條 Query 語句

需要多少成本?恐怕再好的硬件設備,恐怕都不可能在 10 秒之內完成一次查詢吧

注:沒有 where count(*)使用 MyISAM 要比 InnoDB 快得多。因爲 MyISAM 內置了一個計

數器,count(*)時它直接從計數器中讀,而 InnoDB 必須掃描全表。所以在 InnoDB 上執行

count(*)時一般要伴隨 where,且 where 中要包含主鍵以外的索引列。

既然這樣查詢不行,那我們是不是該專門爲這個功能建一個表,就只有一個字段,一條記錄,

就存放這個統計量,每次有新的帖子產生的時候,都將這個值增加 1,這樣我們每次都只需

要查詢這個表就可以得到結果了,這個效率肯定能夠滿足要求了。確實,查詢效率肯定能夠

滿足要求,可是如果帖子產生很快,在高峯時期可能每秒就有幾十甚至上百個帖子新增操作

的時候,恐怕這個統計表又要成爲大家的噩夢了。要麼因爲併發的問題造成統計結果的不準

確,要麼因爲鎖資源爭用嚴重造成整體性能的大幅度下降。

其實這裏問題的焦點不應該是實現這個功能的技術細節,而是在於這個功能的附加要求“實

時更新”上面。當一個論壇的帖子數量很大了之後,到底有多少人會關注這個統計數據是否

是實時變化的?有多少人在乎這個數據在短時間內的不精確性?恐怕不會有人會盯着這個

統計數字並追究當自己發了一個帖子然後回頭刷新頁面發現這個統計數字沒有加 1 吧?所

以只要去掉了這個“實時更新”的附加條件,就可以非常容易的實現這個功能了。就像之前

所提到的那樣,通過創建一個統計表,然後通過一個定時任務每隔一定時間段去更新一次裏

面的統計值,這樣既可以解決統計值查詢的效率問題,又可以保證不影響新發貼的效率,一

舉兩得。

2.系統架構及實現的影響

所有數據都是適合在數據庫中存放的嗎?數據庫爲我們提供了太多的功能,反而讓很多並不

是太瞭解數據庫的人錯誤的使用了數據庫的很多並不是太擅長或者對性能影響很大的功能,

最後卻全部怪罪到數據庫身上。

實際上,以下幾類數據都是不適合在數據庫中存放的:

1 二進制多媒體數據

這種數據主要包括圖片,音頻、視頻和其他一些相關的二進制文件。將二進制多媒體數據存

放在數據庫中,一個問題是數據庫空間資源耗用非常嚴重,另一個問題是這些數據的存儲很

消耗數據庫主機的 CPU 資源。這些數據的處理本不是數據庫的優勢,如果我們硬要將他們

塞入數據庫,肯定會造成數據庫的處理資源消耗嚴重。

2)超大文本數據

對於 5.0.3 之前的 MySQL 版本,VARCHAR 類型的數據最長只能存放 255 個字節,如果需

要存儲更長的文本數據到一個字段,我們就必須使用 TEXT 類型(最大可存放 64KB)的字

段,甚至是更大的 LONGTEXT 類型(最大 4GB)。而 TEXT 類型數據的處理性能要遠比 VARCHAR

類型數據的處理性能低下很多。從 5.0.3 版本開始,VARCHAR 類型的最大長度被調整到 64KB

了,所以,超大文本數據存放在數據庫中不僅會帶來性能低下的問題,還會帶來空間佔用的

浪費問題。

是否合理的利用了應用層 Cache 機制?

對於 Web 應用,活躍數據的數據量總是不會特別的大,有些活躍數據更是很少變化。對於這類數據,我們是否有必要每次需要的時候都到數據庫中去查詢呢?如果我們能夠將變化相

對較少的部分活躍數據通過應用層的 Cache 機制 Cache 到內存中,對性能的提升肯定是成

數量級的,而且由於是活躍數據,對系統整體的性能影響也會很大。

3.查詢語句對性能的影響

SQL 語句的優劣是對性能有影響的,每個 SQL 語句在優化之前和優化之後的性能差異也是

各不相同。

在數據庫管理軟件中,最大的性能瓶頸就是在於磁盤 IO,也就是數據的存取操作上面。而

對於同一份數據,當我們以不同方式去尋找其中的某一點內容的時候,所需要讀取的數據量

可能會有天壤之別,所消耗的資源也自然是區別很大。

功能完全相同的兩條 SQL 的在性能方面的差異。

我們在執行 sql 語句時可以用 explain 來查看執行計劃:

                             wKiom1ipxnCxiqSjAAGoLkl6MD0222.png-wh_50

還可以打開 mysql profiling 功能,來查看 sql 的實際執行計劃

wKioL1ipxoCR5BpVAAI3XGHxD_c216.png-wh_50

通過執行“SHOW PROFILE命令獲取當前系統中保存的多個 Query profile 的概要信息。;

wKioL1ipxpSw0kWkAADv1vo53b0646.png-wh_50

wKiom1ipxqfTxWY2AAEwQJ8SyVc197.png-wh_50

4.數據庫 Schema 設計對性能的影響

5.硬件選擇對性能的影響

首先,數據庫主機是存取數據的地方,所以數據庫主機的 IO 性能肯定是需要最優先考慮的

一個因素,這一點不管是什麼類型的數據庫應用都是適用的。在主機中決定 IO 性能部件主

要由磁盤和內存所決定,當然也包括各種與 IO 相關的板卡。

其次,由於數據庫主機和普通的應用程序服務器相比,資源要相對集中很多,單臺主機上所

需要進行的計算量自然也就比較多,所以數據庫主機的 CPU 處理能力也不能忽視。

最後,由於數據庫負責數據的存儲,與各應用程序的交互中傳遞的數據量比其他各類服務器都要多,所以數據庫主機的網絡設備的性能也可能會成爲系統的瓶頸。

所以,數據庫應用系統的優化,實際上是一個需要多方面配合,多方面優化的才能產生根本

性改善的事情。簡單來說,可以通過下面四句話來簡單的概括數據庫應用系統的性能優化:

商業需求合理化,系統架構最優化,邏輯實現精簡化,硬件設施理性化。

二、MySQL 性能優化之-索引

關於 MySQL索引的好處,如果正確合理設計並且使用索引的 MySQL 是一輛蘭博基尼的話,

那麼沒有設計和使用索引的 MySQL 就是一個人力三輪車。對於沒有索引的表,單表查詢可

能幾十萬數據就是瓶頸,而通常大型網站單日就可能會產生幾十萬甚至幾百萬的數據,沒有

索引查詢會變的非常緩慢。

做一個簡單測試,假如我們創建了一個 tb1 表,向表中插入 20000 行數據,表的創建和數據

插入用如下腳本實現

wKiom1ipxr7jZCi0AANRlT9R4uw772.png

再手動插入一行數據,如

wKioL1ipxtKiE2h3AACJg93MfRI387.png-wh_50

下面開始測試,查詢 stuname=admin’的記錄

情況 1stuname 列上沒有創建索引的情況

wKioL1ipxuSCY87iAAEaIYO5byI565.png-wh_50

情況 2stuname 列上創建索引的情況再查詢

wKiom1ipxvSg238AAAF5YOItIRg019.png-wh_50

在查找 stuname="admin"的記錄時,如果在 stuname 上已經建立了索引,MySQL無須任何

掃描全表,即準確可找到該記錄。相反,MySQL 會掃描所有記錄。

所以在數據庫表中,對字段建立索引可以大大提高查詢速度。

索引是在存儲引擎中實現的,而不是在服務器層中實現的。所以,每種存儲引擎的索引都不一定完全相同,

並不是所有的存儲引擎都支持所有的索引類型。

 

索引概述:

什麼是索引?

索引(Index)是幫助 MySQL 高效獲取數據的數據結構,這是一種比較官方的說法。它的存

在形式是文件。索引能夠幫助我們快速定位數據。更通俗的說,數據庫索引好比是一本書

前面的目錄,能加快數據庫的查詢速度。

索引的數據結構

這裏主介紹 B-tree 索引的結構


如上圖,是一顆 b+樹,這裏只說一些重點,淺藍色的塊我們稱之爲一個磁盤塊,可以看到

每個磁盤塊包含幾個數據項(深藍色所示)和指針(×××所示),如磁盤塊 1 包含數據項17

35,包含指針 P1P2P3P1 表示小於 17 的磁盤塊,P2 表示在 17 35 之間的磁盤塊,

P3 表示大於 35 的磁盤塊。真實的數據存在於葉子節點即 3591013152829

366075799099。非葉子節點只不存儲真實的數據,只存儲指引搜索方向的數據

項和指針,如 1735 並不真實存在於數據表中。

b+樹的查找過程

如圖所示,如果要查找數據項 29,那麼首先會把磁盤塊 1 由磁盤加載到內存,此時發生一

IO,在內存中用二分查找確定 29 17 35 之間,鎖定磁盤塊 1 P2 指針,內存時間

因爲非常短(相比磁盤的 IO)可以忽略不計,通過磁盤塊 1 P2 指針的磁盤地址把磁盤塊

3 由磁盤加載到內存,發生第二次 IO29 26 30 之間,鎖定磁盤塊 3 P2 指針,通過

指針加載磁盤塊 8 到內存,發生第三次 IO,同時內存中做二分查找找到 29,結束查詢,總

計三次 IO。真實的情況是,3 層的 b+樹可以表示上百萬的數據,如果上百萬的數據查找只

需要三次 IO,性能提高將是巨大的,如果沒有索引,每個數據項都要發生一次 IO,那麼總

共需要百萬次的 IO,顯然成本非常非常高。

爲什麼使用索引?

索引可以讓 mysql 高效運行,可以大大提高 mysql 的查詢(包括排序,分組)效率;數據約束(唯一索引的建立)。

索引給我帶來什麼好處?

提高查詢效率,快速定位數據

使用索引產生的代價?

1、索引本身是以文件的形式存放在硬盤,需要的時候才加載至內存,所以添加索引會增加

磁盤的開銷;

2、寫數據:需要更新索引,對數據庫是個很大的開銷,降低表更新、添加和刪除的速度

不建議使用索引的情況:

a) 表記錄較少

b) 索引的選擇性較低,所謂索引的選擇性,是指不重複的索引值與表記錄數的比值,取值

範圍(0-1)。選擇性越高,索引的價值越大。

索引的類型?

索引包括單列索引和組合索引

單列索引,即一個索引只包含單個列,一個表可以有多個單列索引,但這不是組合索引。組

合索引,即一個索包含多個列。

1 普通索引

這是最基本的索引,它沒有任何限制

CREATE INDEX indexName ONtablename(column1[,column2,……])

2 唯一索引

它與前面的普通索引類似,不同的就是索引列的值必須唯一,但允許空值,空值是指 null

如果是組合索引,組合列的值必須唯一

CREATE UNIQUE INDEX indexName ONtablename(column1[,column2,……])

主鍵索引:一種特殊的唯一索引,不允許有空值,一般在建表的時候同時建立主鍵索引

CREATE TABLE tablename(ID INT NOT NULL,username VARCHAR(16) NOT NULL, PRIMARY

KEY(ID) );

3、組合索引

爲了進一步提升 MySQL 的效率,就要考慮建立組合索引

例如:創建一個表,包含如下字段

wKioL1ipx0_Rl6s9AACQTb1BYQA409.png-wh_50

username, city, age 建到一個索引裏

wKioL1ipx2jziiCoAACKf2otKMg167.png-wh_50

如果分別在 usernnamecityage 上建立單列索引,讓該表有 3 個單列索引,查詢時和上

述的組合索引效率也會大不一樣,遠遠低於組合索引。雖然此時有了三個索引,但 MySQL

只能用到其中那個它認爲似乎是最有效率的單列索引。

建立這樣的組合索引,其實是相當於分別建立了下面三組組合索引:

usernname,city,age usernname,city usernname爲什麼沒有 cityage 這樣的組合索引呢?

這是因爲 MySQL 組合索引“最左前綴”的結果。簡單的理解就是隻從最左面的開始組合。並

不是隻要包含這三列的查詢都會用到該組合索引,下面的幾個 SQL 就會用到這個組合索引:

SELECT * FROM mytable WHREEusername="admin" AND city="鄭州"

SELECT * FROM mytable WHREEusername="admin"

而下面幾個則不會用到:

SELECT * FROM mytable WHREE age=20 ANDcity="鄭州"

SELECT * FROM mytable WHREE city="鄭州"

如果對多列進行索引(組合索引),列的順序非常重要,MySQL 僅能對索引最左邊的前綴進行

有效的查找。

例如:

假設存在組合索引 index1(c1,c2),查詢語句 select * from t1 where c1=1 and c2=2 能夠使用該

索引。查詢語句 select * from t1 where c1=1 也能夠使用該索引。但是,查詢語句select * from

t1 where c2=2 不能夠使用該索引,因爲沒有組合索引的引導列,即要想使用 c2 列進行查找,

必需出現 c1 等於某值。因此我們在創建組合索引時應該將最常用作限制條件的列放在最左

邊,依次遞減。

4 全文索引

只用於 MyISAM 對文本域進行索引。字段類型包括 charvarchartext

不過切記對於大容量的數據表,生成全文索引是一個非常消耗時間非常消耗硬盤空間的做法。

CREATE FULLTEXT INDEX indexname ON tablename(column)

查看索引

mysql> show index from tablename;

mysql> show keys from tablename;

建立索引的時機

到這裏我們已經學會了建立索引,那麼我們需要在什麼情況下建立索引呢?

一般來說,在 WHERE JOIN 子句中出現的列需要建立索引,例如:

代碼如下: username 上創建索引

SELECT * FROM mytable WHREEusername="admin" AND city="鄭州"

代碼如下:

SELECT t.Name FROM mytable1 t LEFT JOINmytable2 m ON t.username=m.username;

此時就需要對兩個表的 userame 上建立索引。

使用索引的注意事項

剛纔提到只有某些時候的 LIKE 才需建立索引。因爲在以通配符%_開頭作查詢時,MySQL

不會使用索引。例如下句會使用索引:

SELECT * FROM mytable WHERE usernamelike'admin%'

而下句就不會使用:

SELECT * FROM mytable WHEREt Namelike'%admin'

不要在列上進行運算,例如

select * from users whereYEAR(adddate)<2007;

將在每個行上進行運算,這將導致索引失效而進行全表掃描,因此我們可以改成

select * from users whereadddate<‘2007-01-01';

總結:

選擇索引列:

a.性能優化過程中,選擇在哪個列上創建索引是最重要的步驟之一。可以考慮使用索引

的主要有兩種類型的列:在 where 子句中出現的列,在 join 子句中出現的列。

b.考慮列中值的分佈,索引的列的基數越大,索引的效果越好。

c.使用短索引,如果對字符串列進行索引,應該指定一個前綴長度,可節省大量索引空間,

提升查詢速度。

例如:CREATE INDEX username_city_age ON mytable(username10,city,age);

建表時,usernname 長度爲 16,這裏用 10。這是因爲一般情況下名字的長度不會超過 10

這樣會加速索引查詢速度,還會減少索引文件的大小,提高 INSERT 的更新速度。

d.利用最左前綴

e.不要過度索引,只保持所需的索引。每個額外的索引都要佔用額外的磁盤空間,並降低寫

操作的性能。在修改表的內容時,索引必須進行更新,有時可能需要重構,因此,索引越

多,所花的時間越長。

MySQL 只對以下操作符才使用索引:<,<=,=,>,>=,between,in,

以及某些時候的 like(不以通配符%_開頭的情形)

以下就是有關 Mysql 索引的相關理論介紹,下面我們來學習如何慢查詢分析、優化索引和配

 

三、mysql 性能優化-慢查詢分析、優化索引和配置

基本思路:

1)性能瓶頸定位

Show 命令

慢查詢日誌

explain 分析查詢

profiling 分析查詢

2)索引及查詢優化

3)配置優化

MySQL 數據庫是常見的兩個瓶頸是 CPU I/O 的瓶頸,CPU 在飽和的時候一般發生在數據裝

入內存或從磁盤上讀取數據時候。磁盤 I/O 瓶頸發生在裝入數據遠大於內存容量的時候,如

果應用分佈在網絡上,那麼查詢量相當大的時候那麼平瓶頸就會出現在網絡上,我們可以用

mpstat, iostat, sar vmstat 來查看系統的性能狀態。

除了服務器硬件的性能瓶頸,對於 MySQL 系統本身,我們可以使用工具來優化數據庫的性

能,通常有三種:使用索引,使用 EXPLAIN 分析查詢以及調整 MySQL 的內部配置。

1 查詢與索引優化分析

在優化 MySQL 時,通常需要對數據庫進行分析,常見的分析手段有慢查詢日誌,EXPLAIN

析查詢,profiling 分析以及 show 命令查詢系統狀態及系統變量,通過定位分析性能的瓶頸,才

能更好的優化數據庫系統的性能。

性能瓶頸定位

show 命令

可以通過 show 命令查看 MySQL 狀態及變量,找到系統的瓶頸:

查看 MySQL 服務器配置信息 mysql> show variables;

查看 MySQL 服務器運行的各種狀態值 mysql> show global status;

# mysqladmin variables -u username-ppassword——顯示系統變量

# mysqladmin extended-status -u username-ppassword——顯示狀態信息

比較全的 show 命令的使用可參考:mysql>help show

http://dev.mysql.com/doc/refman/5.7/en/show.html

慢查詢日誌

慢查詢日誌開啓:

在配置文件 my.cnf 中在 [mysqld] 一行下面加入 3 個配置參數,並重啓 mysql 服務

slow_query_log = 1 //0 關閉 1 開啓

slow_query_log_file =/usr/local/mysql/data/slow-query.log //慢查詢日誌存放地點

long_query_time = 1 //表示查詢超過 1 秒才記錄

my.cnf 中添加 log-queries-not-using-indexes 參數,表示向慢查詢日誌中記錄下沒有使用索

引的查詢。

慢查詢日誌開啓方法二:

我們也可以通過命令行設置變量來即時啓動慢日誌查詢

mysql> set global slow_query_log = on;

mysql> set long_query_time = 0.01;

mysql> set global slow_query_log_file ="/usr/local/mysql/data/slow-query.log";

查看慢查詢的設置信息

mysql> show variables like'%slow_query_log%';

mysql> show variables like'%long_query_time%';

我們可以通過打開 log 文件查看得知哪些 SQL 執行效率低下

 [root@hexiaoshuai data]#cat slow-query.log

# Time: 2016-09-06T14:17:12.582189Z

# User@Host: root[root] @ localhost [] Id:3

# Query_time: 0.008316 Lock_time: 0.000304Rows_sent: 1 Rows_examined: 20002

SET timestamp=1473171432;

select * from test1.tb1 where stusex='0';//沒有使用索引的 query

# Time: 2016-09-06T15:54:42.648291Z

# User@Host: root[root] @ localhost [] Id:14

# Query_time: 0.017319 Lock_time: 0.000612Rows_sent: 1 Rows_examined: 20001

SET timestamp=1473177282;

select * from test1.tb1 where entertime<> '2016-9-3'; //慢查詢 query

從日誌中,可以發現查詢時間超過 0.01 秒的 SQL,而小於 0.01 秒的沒有出現在此日誌中。

如果慢查詢日誌中記錄內容很多,可以使用 mysqldumpslow 工具(MySQL 客戶端安裝自帶)

來對慢查詢日誌進行分類彙總。mysqldumpslow 對日誌文件進行了分類彙總,顯示彙總後摘

要結果。

有關 mysqldumpslow 命令的用法可以參考其幫助:#mysqldumpslow –help

[root@hexiaoshuai data]# mysqldumpslow -a-s at -r slow-query.log

Reading mysql slow query log fromslow-query.log

Count: 1 Time=0.00s (0s) Lock=0.00s (0s)Rows=0.0 (0), 0users@0hosts

Time: 2016-09-06T15:54:42.648291Z

# User@Host: root[root] @ localhost [] Id:14

# Query_time: 0.017319 Lock_time: 0.000612Rows_sent: 1 Rows_examined: 20001

SET timestamp=1473177282;

select * from test1.tb1 where entertime<> '2016-9-3'

上面顯示結果中就是一條慢查詢,如何優化呢?

一是在 entertime 列上創建索引優化查詢

mysql> create index index_entertime ontest1.tb1(entertime);

二是優化這個 sql 查詢語句

mysql> select * from test1.tb1 whereentertime < '2016-9-3' or entertime > '2016-9-3';

從下圖可以看查詢 0.00

wKiom1ipx4XzG1xqAAD-GIMKuBQ571.png-wh_50

使用 mysqldumpslow 命令可以非常明確的得到各種我們需要的查詢語句,對 MySQL 查詢語

句的監控、分析、優化是 MySQL 優化非常重要的一步。開啓慢查詢日誌後,由於日誌記錄

操作,在一定程度上會佔用 CPU 資源影響 mysql 的性能,但是可以階段性開啓來定位性能

瓶頸。

explain 分析查詢

使用 EXPLAIN 關鍵字可以模擬優化器執行 SQL 查詢語句,從而知道 MySQL 是如何處理你的

SQL 語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。通過 explain 命令可以得到:

mysql> explain select * from test1.tb1where stuname='admin'\G;

*************************** 1. row***************************

id: 1

select_type: SIMPLE

table: tb1

partitions: NULL

type: ALL // 全表掃描

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 19986

filtered: 10.00

Extra: Using where //使用 where 過濾數據

1 row in set, 1 warning (0.00 sec)

EXPLAIN 字段:

Table:顯示這一行的數據是關於哪張表的

type:這是最重要的字段之一,顯示查詢使用了何種類型。從最好到最差的連接類型爲 system

consteq_regrefrangeindex ALL

possible_keys:顯示可能應用在這張表中的索引。如果爲空,沒有可能的索引。

key:實際使用的索引。如果爲 NULL,則沒有使用索引。

key_len:使用的索引的長度。在不損失精確性的情況下,長度越短越好

ref:顯示索引的哪一列被使用了,如果可能的話,是一個常數

rowsMySQL 認爲必須檢索的用來返回請求數據的行數

Extra:關於 MYSQL 如何解析查詢的額外信息

從上面的 explain 模擬優化器執行 sql 語句來看是沒有使用索引查詢的,而是全表掃描

優化方法:在 stuname 列上創建索引

mysql> create index index_stuname ontest1.tb1(stuname);

再次執行 explain

wKioL1ipx52TLZIXAAEHnPRfIeQ903.png-wh_50

顯示結果說明該查詢語句使用了 index_stuname 索引查詢數據而非全表掃描。

profiling 分析查詢

通過慢日誌查詢可以知道哪些 SQL 語句執行效率低下,通過 explain 我們可以得知 SQL 語句的

具體執行情況,索引使用等,還可以結合 show 命令查看執行狀態。如果覺得 explain 的信息不

夠詳細,可以同通過 profiling 命令得到更準確的 SQL 執行消耗系統資源的信息。

profiling 默認是關閉的。可以通過以下語句查看

mysql> show variables like'%profiling%'; //off 表示未開啓

wKiom1ipx62hWQpoAACY2hbptoA279.png-wh_50

mysql> select @@profiling; //0 表示未開啓

wKioL1ipx72zPolGAABki0LZeqA080.png-wh_50

打開 profiling 功能: mysql>set profiling=1; 執行需要測試的 sql 語句:

wKiom1ipx9DSHQQ7AACS5c6FE3w780.png-wh_50

執行要測試的 sql 語句

wKiom1ipyB6RTWLvAAHj41JBwD4858.png-wh_50

wKiom1ipyDazehh5AAESmGaBYRU069.png-wh_50

status: profile 裏的狀態,duration:是 status 狀態下的耗時。因此我們關注的就是那個狀

態最耗時,這些狀態中那些可以優化。

當然也可以查看更多的信息如 CPU 等等

SHOW PROFILE [type [, type] ... ] [FORQUERY n]

type:

ALL:顯示所有的開銷信息

BLOCK IO:顯示塊 IO 相關開銷

CPU:顯示用戶 CPU 時間、系統 CPU 時間

IPC:顯示發送和接收相關開銷信息

PAGE FAULTS:顯示頁面錯誤相關開銷信息

SWAPS:顯示交換次數相關開銷的信息

測試完成之以後,記得要關閉調試功能,以免影響數據庫的正常使用:

mysql> set profiling=0;

2、配置優化

Mysql 參數優化對於不同的網站,及其在線量,訪問量,帖子數量,網絡情況,以及機器硬

件配置都有關係,優化不可能一次性完成,需要不斷的觀察以及調試,纔有可能得到最佳效

果。

下面列出了對性能優化影響較大的主要變量,主要分爲連接請求的變量和緩衝區變量

1 連接請求的變量:

1. max_connections

MySQL 的最大連接數,如果服務器的併發連接請求量比較大,建議調高此值,以增加並行

連接數量,當然這建立在機器能支撐的情況下,因爲如果連接數越多, MySQL 會爲每個連

接提供連接緩衝區,就會開銷越多的內存,所以要適當調整該值,不能盲目提高設值。

數值過小會經常出現 ERROR 1040: Too many connections 錯誤,可以過 mysql> showstatus like

'connections';通配符查看當前狀態的連接數量(試圖連接到MySQL(不管是否連接成功)的連接

),以定奪該值的大小。

mysql>show variables like max_connections 最大連接數

mysql>show status like max_used_connections 響應的連接數

max_used_connections / max_connections *100% (理想值≈ 85%

如果 max_used_connections max_connections 相同那麼就是 max_connections 設置過低或

者超過服務器負載上限了,低於 10%則設置過大。

如何設置 max_connections?

修改/etc/my.cnf 文件,在[mysqld]下面添加如下內容,如設置最大連接數爲 1024

max_connections = 1024

重啓 mysql 服務

2.back_log

MySQL 能暫存的連接數量。當主要 MySQL 線程在一個很短時間內得到非常多的連接請求,

它就會起作用。如果 MySQL 的連接數據達到 max_connections 時,新來的請求將會被存在堆

棧中,以等待某一連接釋放資源,該堆棧的數量即 back_log,如果等待連接的數量超過

back_log,將不被授予連接資源。

back_log 值指出在 MySQL 暫時停止回答新請求之前的短時間內有多少個請求可以被存在堆

棧中。只有如果期望在一個短時間內有很多連接,你需要增加它。

當觀察你主機進程列表(mysql> show full processlist),發現大量

xxxxx | unauthenticated user |xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待連接

進程時,就要加大 back_log 的值了或加大 max_connections 的值。

通過 mysql> show variables like 'back_log';查看 back_log 的設置

如何設置 back_log?

修改/etc/my.cnf 文件,在[mysqld]下面添加如下內容,如設置最大連接數爲 1024

back_log = 數值

重啓 mysql 服務

3. wait_timeout interactive_timeout

wait_timeout -- 指的是 MySQL在關閉一個非交互的連接之前所要等待的秒數

interactive_time -- 指的是 mysql 在關閉一個交互的連接之前所要等待的秒數,比如我們在終

端上進入 mysql 管理,使用的即使交互的連接,這時候,如果沒有操作的時間超過了

interactive_time 設置的時間就會自動斷開。默認數值是 28800,可調優爲 7200

對性能的影響:

wait_timeout

(1)    如果設置大小,那麼連接關閉的很快,從而使一些持久的連接不起作用

2)如果設置太大,容易造成連接打開時間過長,在 show processlist 時,能看到太多的sleep

狀態的連接,從而造成 too many connections 錯誤

3)一般希望 wait_timeout 儘可能地低

interactive_timeout 的設置將要對你的 webapplication 沒有多大的影響

查看 wait_timeout interactive_timeout

mysql> show variables like'%wait_tmeout%';

mysql> show variables like'%interactive_timeout%';

如何設置 wait_timeout interactive_timeout ?

修改/etc/my.cnf 文件,在[mysqld]下面添加如下內容

wait_timeout=100

interactive_timeout=100

重啓 MySQL Server 進入後,查看設置已經生效。

2)綬衝區變量

全局緩衝:

4.key_buffer_size

key_buffer_size 指定索引緩衝區的大小,它決定索引處理的速度,尤其是索引讀的速度。通

過檢查狀態值 Key_read_requests Key_reads,可以知道 key_buffer_size 設置是否合理。比

key_reads / key_read_requests 應該儘可能的低,至少是 1:1001:1000 更好(上述狀態值

可以使用 SHOW STATUS LIKE key_read%’獲得)。

wKioL1ipyE6AJjhVAADbRMrsP9M646.png-wh_50

一共有 6 個索引讀取請求,有 3 個請求在內存中沒有找到直接從硬盤讀取索引,計算索引

未命中緩存的概率:

key_cache_miss_rate Key_reads /Key_read_requests * 100% =50%

key_buffer_size 只對 MyISAM 表起作用。即使你不使用MyISAM 表,但是內部的臨時磁盤表

MyISAM 表,也要使用該值。可以使用檢查狀態值 created_tmp_disk_tables 得知詳情。

wKioL1ipyHuDBpbKAACSZ3-0Kl8396.png-wh_50

如何調整 key_buffer_size

默認配置數值是 8388608(8M),主機有 4GB 內存,可以調優值爲 268435456(256MB)

修改/etc/my.cnf 文件,在[mysqld]下面添加如下內容

key_buffer_size=268435456 key_buffer_size=256M

重啓 MySQL Server 進入後,查看設置已經生效。

5. query_cache_size(查詢緩存簡稱 QC)

使用查詢緩衝,MySQL 將查詢結果存放在緩衝區中,今後對於同樣的 SELECT 語句(區分大小

寫),將直接從緩衝區中讀取結果。

一個 SQL 查詢如果以 select 開頭,那麼 MySQL 服務器將嘗試對其使用查詢緩存。

注:兩個 SQL 語句,只要相差哪怕是一個字符(例如大小寫不一樣;多一個空格等),那麼這兩

SQL 將使用不同的一個 CACHE

通過檢查狀態值’Qcache%’,可以知道 query_cache_size 設置是否合理(上述狀態值可以使用

SHOW STATUS LIKE Qcache%’獲得)。

wKioL1ipyI3QMfQrAADFctUevDc961.png-wh_50

Qcache_free_blocks:緩存中相鄰內存塊的個數。如果該值顯示較大,則說明 Query Cache

的內存碎片較多了,FLUSH QUERY CACHE 會對緩存中的碎片進行整理,從而得到一個

空閒塊。

注:當一個表被更新之後,和它相關的 cache blocks 將被 free。但是這個 block 依然可能

存在隊列中,除非是在隊列的尾部。可以用 FLUSH QUERY CACHE 語句來清空 free blocks

Qcache_free_memoryQuery Cache 中目前剩餘的內存大小。通過這個參數我們可以較爲

準確的觀察出當前系統中的 Query Cache 內存大小是否足夠,是需要增加還是過多了。

Qcache_hits:表示有多少次命中緩存。我們主要可以通過該值來驗證我們的查詢緩存的效

果。數字越大,緩存效果越理想。

Qcache_inserts:表示多少次未命中然後插入,意思是新來的 SQL 請求在緩存中未找到,不

得不執行查詢處理,執行查詢處理後把結果 insert 到查詢緩存中。這樣的情況的次數越多,

表示查詢緩存應用到的比較少,效果也就不理想。當然系統剛啓動後,查詢緩存是空的,這

很正常。

Qcache_lowmem_prunes:多少條 Query 因爲內存不足而被清除出Query Cache。通過

Qcache_lowmem_prunes”和“Qcache_free_memory”相互結合,能夠更清楚的瞭解到我們系統中 Query Cache 的內存大小是否真的足夠,是否非常頻繁的出現因爲內存不足而有 Query

被換出。這個數字最好長時間來看;如果這個數字在不斷增長,就表示可能碎片非常嚴重,

或者內存很少。(上面的 free_blocks free_memory 可以告訴您屬於哪種情況)

Qcache_not_cached:不適合進行緩存的查詢的數量,通常是由於這些查詢不是 SELECT

語句或者用了 now()之類的函數。

Qcache_queries_in_cache:當前 QueryCache cache Query 數量;

Qcache_total_blocks:當前 QueryCache 中的 block 數量;。

我們再查詢一下服務器關於 query_cache 的配置:

wKiom1ipyJ_D2YN5AAC0WNlPuxw064.png-wh_50

上圖可以看出 query_cache_type off 表示不緩存任何查詢

各字段的解釋:

query_cache_limit:超過此大小的查詢將不緩存

query_cache_min_res_unit:緩存塊的最小大小 query_cache_min_res_unit的配置是一柄”

雙刃劍”,默認是 4KB,設置值大對大數據查詢有好處,但如果你的查詢都是小數據查詢,

就容易造成內存碎片和浪費。

query_cache_size:查詢緩存大小 (注:QC 存儲的最小單位是 1024byte,所以如果你設定了

一個不是 1024 的倍數的值,這個值會被四捨五入到最接近當前值的等於 1024 的倍數的值。)

query_cache_type:緩存類型,決定緩存什麼樣的查詢,注意這個值不能隨便設置,必須設

置爲數字,可選項目以及說明如下:

wKioL1ipyLbhXPRjAAFid998-eY525.png

如果設置爲 0,那麼可以說,你的緩存根本就沒有用,相當於禁用了。

如果設置爲 1,將會緩存所有的結果,除非你的 select 語句使用 SQL_NO_CACHE 禁用了查詢

緩存。

如果設置爲 2,則只緩存在 select 語句中通過 SQL_CACHE 指定需要緩存的查詢。

修改/etc/my.cnf,配置完後的部分文件如下:

query_cache_size=256M

query_cache_type=1

保存文件,重新啓動 MYSQL 服務,然後通過如下查詢來驗證是否真正開啓了:

wKiom1ipyMiCAKtVAAC5n0uRlQY334.png-wh_50

wKioL1ipyNjjs8m7AADJkejccxk017.png-wh_50

query_cache_wlock_invalidate:當有其他客戶端正在對MyISAM 表進行寫操作時,如果查詢

query cache 中,是否返回 cache 結果還是等寫操作完成再讀表獲取結果。

查詢緩存碎片率 = Qcache_free_blocks / Qcache_total_blocks * 100%

如果查詢緩存碎片率超過 20%,可以用 FLUSH QUERY CACHE 整理緩存碎片,或者試試減小

query_cache_min_res_unit,如果你的查詢都是小數據量的話。

查詢緩存利用率 = (query_cache_size Qcache_free_memory) / query_cache_size * 100%

查詢緩存利用率在 25%以下的話說明 query_cache_size 設置的過大,可適當減小;查詢緩存

利用率在80%以上而且Qcache_lowmem_prunes > 50的話說明 query_cache_size可能有點小,

要不就是碎片太多。

查詢緩存命中率 = Qcache_hits/(Qcache_hits +Qcache_inserts) * 100%

Query Cache 的限制

a) 所有子查詢中的外部查詢 SQL 不能被 Cache

b) ProcedureFunction 以及 Trigger 中的 Query 不能被 Cache

c) 包含其他很多每次執行可能得到不一樣結果的函數的 Query 不能被 Cache

鑑於上面的這些限制,在使用 Query Cache 的過程中,建議通過精確設置的方式來使用,僅

僅讓合適的表的數據可以進入 Query Cache,僅僅讓某些 Query 的查詢結果被 Cache

如何設置 query_cache_size

修改/etc/my.cnf 文件,在[mysqld]下面添加如下內容

query_cache_size=256M

query_cache_type=1

重啓 MySQL Server 進入後,查看設置已經生效。

6. max_connect_errors 是一個 MySQL 中與安全有關的計數器值,它負責阻止過多嘗試失敗

的客戶端以防止暴力破解密碼的情況, 當超過指定次數,MYSQL 服務器將禁止 host 的連接

請求,直到 mysql 服務器重啓或通過 flush hosts 命令清空此 host 的相關信息。

max_connect_errors 的值與性能並無太大關係。

修改/etc/my.cnf 文件,在[mysqld]下面添加如下內容

max_connect_errors=20

重啓 MySQL Server 進入後,查看設置已經生效。

7. sort_buffer_size

每個需要進行排序的線程分配該大小的一個緩衝區。增加這值加速 ORDER BY GROUP BY

操作。

Sort_Buffer_Size 是一個connection 級參數,在每個 connectionsession)第一次需要使用這

buffer 的時候,一次性分配設置的內存。

Sort_Buffer_Size 並不是越大越好,由於是connection 級的參數,過大的設置+高併發可能會

耗盡系統內存資源。例如:500 個連接將會消耗 500*sort_buffer_size(2M)=1G 內存

例如設置 sort_buffer_size

修改/etc/my.cnf 文件,在[mysqld]下面添加如下內容

sort_buffer_size = 2M

重啓 MySQL Server 進入後,查看設置已經生效。

8. max_allowed_packet = 32M

MySQL 根據配置文件會限制 Server 接受的數據包大小。有時候大的插入和更新會受

max_allowed_packet 參數限制,導致寫入或者更新失敗。最大值是 1GB,必須設置 1024

倍數。

9join_buffer_size =2M

用於表間關聯緩存的大小,和 sort_buffer_size 一樣,該參數對應的分配內存也是每個連接

獨享。

10. thread_cache_size = 300

服務器線程緩存,這個值表示可以重新利用保存在緩存中線程的數量,當斷開連接時,那麼客

戶端的線程將被放到緩存中以響應下一個客戶而不是銷燬(前提是緩存數未達上限),如果線

程重新被請求,那麼請求將從緩存中讀取,如果緩存中是空的或者是新的請求,那麼這個線

程將被重新創建,如果有很多新的線程,增加這個值可以改善系統性能.通過比

Connections Threads_created 狀態的變量,可以看到這個變量的作用。設置規則如下:

1GB 內存配置爲 82GB 置爲 163GB 配置爲 324GB 或更高內存,可配置更大。服務

器處理此客戶的線程將會緩存起來以響應下一個客戶而不是銷燬(前提是緩存數未達上限)

wKiom1ipyOyAoa30AAB11-t3eiw036.png-wh_50

試圖連接到 MySQL(不管是否連接成功)的連接數

wKiom1ipyP7As_mcAACPNwykaGU116.png-wh_50

Threads_cached :代表當前此時此刻線程緩存中有多少空閒線程。

Threads_connected :代表當前已建立連接的數量,因爲一個連接就需要一個線程,所以也可

以看成當前被使用的線程數。

Threads_created :代表從最近一次服務啓動,已創建線程的數量,如果發現 Threads_created

值過大的話,表明 MySQL 服務器一直在創建線程,這也是比較耗資源,可以適當增加配置文件中 thread_cache_size 值。

Threads_running :代表當前激活的(非睡眠狀態)線程數。並不是代表正在使用的線程數,

有時候連接已建立,但是連接處於 sleep 狀態。

3)配置 InnoDB 的幾個變量

11. innodb_buffer_pool_size

對於 InnoDB 表來說,innodb_buffer_pool_size 的作用就相當於key_buffer_size 對於 MyISAM

表的作用一樣。InnoDB 使用該參數指定大小的內存來緩衝數據和索引。對於單獨的 MySQL

數據庫服務器,最大可以把該值設置成物理內存的 80%。根據 MySQL 手冊,對於 2G 內存

的機器,推薦值是 1G50%)。如果你的數據量不大,並且不會暴增,那麼無需

innodb_buffer_pool_size 設置的太大了。

mysql> show variables like'innodb_buffer_pool_size';

設置 innodb_buffer_pool_size

修改/etc/my.cnf 文件,在[mysqld]下面添加如下內容

innodb_buffer_pool_size = 2048M

重啓 MySQL Server 進入後,查看設置已經生效。

12. innodb_flush_log_at_trx_commit

主要控制了innodblog buffer中的數據寫入日誌文件並flush磁盤的時間點,取值分別爲0

12 三個。0,表示當事務提交時,不做日誌寫入操作,而是每秒鐘將 log buffer 中的數據

寫入日誌文件並 flush 磁盤一次;1,則在每秒鐘或是每次事物的提交都會引起日誌文件寫入、

flush 磁盤的操作,確保了事務的 ACID;設置爲 2,每次事務提交引起寫入日誌文件的動作,

但每秒鐘完成一次 flush 磁盤操作。

實際測試發現,該值對插入數據的速度影響非常大,設置爲 2 時插入 10000 條記錄只需要 2

秒,設置爲 0 時只需要 1 秒,而設置爲 1 時則需要 229 秒。因此,MySQL 手冊也建議儘量

將插入操作合併成一個事務,這樣可以大幅提高速度。

根據 MySQL 手冊,在允許丟失最近部分事務的危險的前提下,可以把該值設爲 0 2

13.innodb_thread_concurrency = 0

此參數用來設置 innodb 線程的併發數量,默認值爲 0 表示不限制,若要設置則與服務器的

CPU 核數相同或是 cpu 的核數的 2 倍,建議用默認設置,一般爲8.

14. innodb_log_buffer_size

此參數確定些日誌文件所用的內存大小,以 M 爲單位。緩衝區更大能提高性能,對於較大

的事務,可以增大緩存大小。

innodb_log_buffer_size=32M

15. innodb_log_file_size = 50M

此參數確定數據日誌文件的大小,以 M 爲單位,更大的設置可以提高性能.

16. innodb_log_files_in_group = 3

爲提高性能,MySQL 可以以循環方式將日誌文件寫到多個文件。推薦設置爲 3

17.read_buffer_size = 1M

MySql 讀入緩衝區大小。對錶進行順序掃描的請求將分配一個讀入緩衝區,MySql 會爲它分配一段內存緩衝區。如果對錶的順序掃描請求非常頻繁,並且你認爲頻繁掃描進行得太慢,

可以通過增加該變量值以及內存緩衝區大小提高其性能。和 sort_buffer_size 一樣,該參數

對應的分配內存也是每個連接獨享。

18.read_rnd_buffer_size = 16M

MySql 的隨機讀(查詢操作)緩衝區大小。當按任意順序讀取行時(例如,按照排序順序)

將分配一個隨機讀緩存區。進行排序查詢時,MySql 會首先掃描一遍該緩衝,以避免磁盤搜

索,提高查詢速度,如果需要排序大量數據,可適當調高該值。但 MySql 會爲每個客戶連接

發放該緩衝空間,所以應儘量適當設置該值,以避免內存開銷過大。

注:順序讀是指根據索引的葉節點數據就能順序地讀取所需要的行數據。隨機讀是指一般需

要根據輔助索引葉節點中的主鍵尋找實際行數據,而輔助索引和主鍵所在的數據段不同,因

此訪問方式是隨機的。

19.bulk_insert_buffer_size = 64M

批量插入數據緩存大小,可以有效提高插入效率,默認爲 8M

20.binary log

log-bin=/usr/local/mysql/data/mysql-bin

binlog_cache_size = 2M //爲每個 session 分配的內存,在事務過程中用來存儲二進制日誌

的緩存, 提高記錄 bin-log 的效率。沒有什麼大事務,dml 也不是很頻繁的情況下可以設置小

一點,如果事務大而且多,dml 操作也頻繁,則可以適當的調大一點。前者建議是--1M,後

者建議是:即 2--4M

max_binlog_cache_size = 8M //表示的是 binlog 能夠使用的最大 cache內存大小

max_binlog_size = 512M //指定 binlog 日誌文件的大小,如果當前的日誌大小達到

max_binlog_size,還會自動創建新的二進制日誌。你不能將該變量設置爲大於 1GB 或小於

4096 字節。 默認值是 1GB。在導入大容量的 sql 文件時,建議關閉sql_log_bin,否則硬盤

扛不住,而且建議定期做刪除。

expire_logs_days = 7 //定義了 mysql 清除過期日誌的時間。

二進制日誌自動刪除的天數。默認值爲 0,表示“沒有自動刪除”。

mysqladmin flush-logs 也可以重新開始新的binary log

在優化之前執行 mysqlslap 工具進行測試

wKioL1ipyRSBa2-CAAF-Gf0crMw742.png-wh_50

優化之後執行 mysqlslap 工具進行測試

wKiom1ipySjAHnjuAAHDBhrusW0431.png-wh_50

相關優化參數總結:

[mysqld]

slow_query_log = 1

slow_query_log_file =/usr/local/mysql/data/slow-query.log

long_query_time = 1

log-queries-not-using-indexes

max_connections = 1024

back_log = 128

wait_timeout = 60

interactive_timeout = 7200

key_buffer_size=256M

query_cache_size = 256M

query_cache_type=1

query_cache_limit=50M

max_connect_errors=20

sort_buffer_size = 2M

max_allowed_packet=32M

join_buffer_size=2M

thread_cache_size=200

innodb_buffer_pool_size = 2048M

innodb_flush_log_at_trx_commit = 1

innodb_log_buffer_size=32M

innodb_log_file_size=128M

innodb_log_files_in_group=3

log-bin=mysql-bin

binlog_cache_size=2M

max_binlog_cache_size=8M

max_binlog_size=512M

expire_logs_days=7

read_buffer_size=1M

read_rnd_buffer_size=16M

bulk_insert_buffer_size=64M

log-error =/usr/local/mysql/data/mysqld.err


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章