mysql性能優化-索引與優化

http://hongge.blog.51cto.com/html

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

1.商業需求的影響linux

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

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

從功能上來看很是容易實現,執行一條SELECT COUNT(*) from 表名 的Query 就能夠獲得結果。可是,若是咱們採用不是MyISAM 存儲引擎,而是使用的Innodb 的存儲引擎,那麼你們能夠試想一下,若是存放帖子的表中已經有上千萬的帖子的時候,執行這條Query 語句須要多少成本?恐怕再好的硬件設備,恐怕都不可能在10秒以內完成一次查詢吧sql

注:沒有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來查看執行計劃:

mysql> explain

-> select stuid,stuname,cardid from tb1 where stuid between 3000 and 5000

-> order by stuid desc

-> limit 20 \G

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

id: 1

select_type: SIMPLE

table: tb1

partitions: NULL

type: range

possible_keys: PRIMARY

key: PRIMARY

key_len: 4

ref: NULL

rows: 3678

filtered: 100.00

Extra: Using where

1 row in set, 1 warning (0.00 sec)

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

mysql> set profiling=1;

mysql> select stuid,stuname,cardid from tb1 where stuid between 3000 and 5000 order by stuid desc limit 5\G

mysql>show profile;

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

mysql> show profile CPU,BLOCK IO for query 1;
4.數據庫Schema設計對性能的影響
5.硬件選擇對性能的影響

首先,數據庫主機是存取數據的地方,因此數據庫主機的IO 性能確定是須要最優先考慮的一個因素,這一點無論是什麼類型的數據庫應用都是適用的。在主機中決定IO 性能部件主要由磁盤和內存所決定,固然也包括各類與IO 相關的板卡。

其次,因爲數據庫主機和普通的應用程序服務器相比,資源要相對集中不少,單臺主機上所須要進行的計算量天然也就比較多,因此數據庫主機的CPU處理能力也不能忽視。

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

因此,數據庫應用系統的優化,其實是一個須要多方面配合,多方面優化的才能產生根本性改善的事情。簡單來講,能夠經過下面三句話來簡單的歸納數據庫應用系統的性能優化:商業需求合理化,系統架構最優化,邏輯實現精簡化,硬件設施理性化。

2、MySQL性能優化之-索引

關於MySQL索引的好處,若是正確合理設計而且使用索引的MySQL是一輛蘭博基尼的話,那麼沒有設計和使用索引的MySQL就是一我的力三輪車。對於沒有索引的表,單表查詢可能幾十萬數據就是瓶頸,而一般大型網站單日就可能會產生幾十萬甚至幾百萬的數據,沒有索引查詢會變的很是緩慢。

作一個簡單測試,假如咱們建立了一個tb1表,向表中插入20000行數據,表的建立和數據插入用以下腳本實現

[root@localhost ~]# cat mysql3.sh

#!/bin/bash

HOSTNAME="localhost"

PORT="3306"

USERNAME="root"

PASSWORD="123.abc"

DBNAME="test1"

TABLENAME="tb1"

#create database

mysql -h ${HOSTNAME} -P ${PORT} -u ${USERNAME} -p${PASSWORD} -e "drop database if exists ${DBNAME}"

create_db_sql="create database if not exists ${DBNAME}"

mysql -h ${HOSTNAME} -P ${PORT} -u ${USERNAME} -p${PASSWORD} -e "${create_db_sql}"

#create table

create_table_sql="create table if not exists ${TABLENAME}(stuid int not null primary key,stuname varchar(20) not null,stusex char(1) not null,cardid varchar(20) not null,birthday datetime,entertime datetime,address varchar(100) default null)"

mysql -h ${HOSTNAME} -P ${PORT} -u ${USERNAME} -p${PASSWORD} ${DBNAME} -e "${create_table_sql}"

#insert data to table

i=1

while [ $i -le 20000 ]

do

insert_sql="insert into ${TABLENAME} values ($i,'zhangsan','1','1234567890123456','1999-10-10','2016-9-3','zhongguo beijingshi changpinqu')"

mysql -h ${HOSTNAME} -P ${PORT} -u ${USERNAME} -p${PASSWORD} ${DBNAME} -e "${insert_sql}"

let i++

done

#select data

select_sql="select count(*) from ${TABLENAME}"

mysql -h ${HOSTNAME} -P ${PORT} -u ${USERNAME} -p${PASSWORD} ${DBNAME} -e "${select_sql}"

再手動插入一行數據,如

mysql> insert into tb1 values (20001,'admin','0','123456789009877','2000-1-1','2016-9-1','wwwwwwwwwwwww');

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

狀況1:stuname列上沒有建立索引的狀況

mysql> explain select stuid,stuname,stusex,cardid,entertime from tb1 where 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: 18910

filtered: 10.00

Extra: Using where

1 row in set, 1 warning (0.01 sec)

狀況2:stuname列上建立索引的狀況再查詢

mysql> create index index_stuname on tb1(stuname);

mysql> explain select stuid,stuname,stusex,cardid,entertime from tb1 where stuname='admin'\G;

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

id: 1

select_type: SIMPLE

table: tb1

partitions: NULL

type: ref

possible_keys: index_stuname

key: index_stuname

key_len: 62

ref: const

rows: 1

filtered: 100.00

Extra: NULL

1 row in set, 1 warning (0.00 sec)

在查找stuname="admin"的記錄時,若是在stuname上已經創建了索引,MySQL無須任何掃描全表,即準確可找到該記錄。相反,MySQL會掃描全部記錄。

因此在數據庫表中,對字段創建索引能夠大大提升查詢速度。

索引是在存儲引擎中實現的,而不是在服務器層中實現的。因此,每種存儲引擎的索引都不必定徹底相同,並非全部的存儲引擎都支持全部的索引類型。

索引概述:

什麼是索引?

索引(Index)是幫助MySQL高效獲取數據的數據結構,這是一種比較官方的說法。它的存在形式是文件。索引可以幫助咱們快速定位數據。 更通俗的說,數據庫索引比如是一本書前面的目錄,能加快數據庫的查詢速度。

索引的數據結構

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

image

如上圖,是一顆b+樹,這裏只說一些重點,淺藍色的塊咱們稱之爲一個磁盤塊,能夠看到每一個磁盤塊包含幾個數據項(深藍色所示)和指針(×××所示),如磁盤塊1包含數據項17和35,包含指針P一、P二、P3,P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。真實的數據存在於葉子節點即三、五、九、十、1三、1五、2八、2九、3六、60、7五、7九、90、99。非葉子節點只不存儲真實的數據,只存儲指引搜索方向的數據項和指針,如1七、35並不真實存在於數據表中。

b+樹的查找過程

如圖所示,若是要查找數據項29,那麼首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,在內存中用二分查找肯定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間由於很是短(相比磁盤的IO)能夠忽略不計,經過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO,29在26和30之間,鎖定磁盤塊3的P2指針,經過指針加載磁盤塊8到內存,發生第三次IO,同時內存中作二分查找找到29,結束查詢,總計三次IO。真實的狀況是,3層的b+樹能夠表示上百萬的數據,若是上百萬的數據查找只須要三次IO,性能提升將是巨大的,若是沒有索引,每一個數據項都要發生一次IO,那麼總共須要百萬次的IO,顯然成本很是很是高。

爲何使用索引?

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

索引給我帶來什麼好處?

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

使用索引產生的代價?

一、索引自己是以文件的形式存放在硬盤,須要的時候才加載至內存,因此添加索引會增長磁盤的開銷;

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

不建議使用索引的狀況:

a) 表記錄較少

b) 索引的選擇性較低,所謂索引的選擇性,是指不重複的索引值與表記錄數的比值,取值範圍(0-1)。選擇性越高,索引的價值越大。

索引的類型?

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

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

一、 普通索引

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

CREATE INDEX indexName ON tablename(column1[,column2,……])

二、 惟一索引

它與前面的普通索引相似,不一樣的就是索引列的值必須惟一,但容許空值,空值是指null。若是是組合索引,組合列的值必須惟一

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

主鍵索引:一種特殊的惟一索引,不容許有空值,通常在建表的時候同時創建主鍵索引

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

三、組合索引

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

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

CREATE TABLE mytable(ID INT NOT NULL, username VARCHAR(16) NOT NULL, city VARCHAR(50) NOT NULL, age INT NOT NULL  );

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

CREATE INDEX username_city_age ON mytable(username,city,age);

若是分別在 usernname,city,age上創建單列索引,讓該表有3個單列索引,查詢時和上述的組合索引效率也會大不同,遠遠低於組合索引。雖然此時有了三個索引,但MySQL只能用到其中那個它認爲彷佛是最有效率的單列索引。

創建這樣的組合索引,實際上是至關於分別創建了下面三組組合索引:

usernname,city,age   usernname,city   usernname  爲何沒有 city,age這樣的組合索引呢?這是由於MySQL組合索引「最左前綴」的結果。簡單的理解就是隻從最左面的開始組合。並非只要包含這三列的查詢都會用到該組合索引,下面的幾個SQL就會用到這個組合索引:

SELECT * FROM mytable WHREE username="admin" AND city="鄭州" 

SELECT * FROM mytable WHREE username="admin"

而下面幾個則不會用到:

SELECT * FROM mytable WHREE age=20 AND city="鄭州" 

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等於某值。所以咱們在建立組合索引時應該將最經常使用做限制條件的列放在最左邊,依次遞減。

四、 全文索引

只用於MyISAM 表 對文本域進行索引。字段類型包括char、varchar、text

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

CREATE FULLTEXT INDEX indexname ON tablename(column)

查看索引

mysql> show index from tablename;

mysql> show keys from tablename;

創建索引的時機

到這裏咱們已經學會了創建索引,那麼咱們須要在什麼狀況下創建索引呢?

通常來講,在WHERE和JOIN子句中出現的列須要創建索引,例如:

代碼以下:在username上建立索引

SELECT * FROM mytable WHREE username="admin" AND city="鄭州"

代碼以下:

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

此時就須要對兩個表的userame上創建索引。

使用索引的注意事項

剛纔提到只有某些時候的LIKE才需創建索引。由於在以通配符%和_開頭做查詢時,MySQL不會使用索引。例以下句會使用索引:

SELECT * FROM mytable WHERE username like'admin%'

而下句就不會使用:

SELECT * FROM mytable WHEREt Name like'%admin'

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

select * from users where YEAR(adddate)<2007;

將在每一個行上進行運算,這將致使索引失效而進行全表掃描,所以咱們能夠改爲

select * from users where adddate<‘2007-01-01';

總結:

選擇索引列:
a.性能優化過程當中,選擇在哪一個列上建立索引是最重要的步驟之一。能夠考慮使用索引 的主要有 兩種類型的列:在where子句中出現的列,在join子句中出現的列。
b.考慮列中值的分佈,索引的列的基數越大,索引的效果越好。
c.使用短索引,若是對字符串列進行索引,應該指定一個前綴長度,可節省大量索引空間,提高查詢速度。

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

建表時,usernname長度爲 16,這裏用 10。這是由於通常狀況下名字的長度不會超過10,這樣會加速索引查詢速度,還會減小索引文件的大小,提升INSERT的更新速度。

d.利用最左前綴
e.不要過分索引,只保持所需的索引。每一個額外的索引都要佔用額外的磁盤空間,並下降寫操做的性能。 在修改表的內容時,索引必須進行更新,有時可能須要重構,所以,索引越多,所花的時間越長。
MySQL只對一下操做符才使用索引:<,<=,=,>,>=,between,in,
以及某些時候的like(不以通配符%或_開頭的情形)。

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

3、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@localhost 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.000304 Rows_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.000612 Rows_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@localhost data]# mysqldumpslow -a -s at -r slow-query.log

Reading mysql slow query log from slow-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.000612 Rows_sent: 1 Rows_examined: 20001

SET timestamp=1473177282;

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

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

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

mysql> create index index_entertime on test1.tb1(entertime);

二是優化這個sql查詢語句

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

從下圖能夠看查詢0.00秒

image

使用mysqldumpslow命令能夠很是明確的獲得各類咱們須要的查詢語句,對MySQL查詢語句的監控、分析、優化是MySQL優化很是重要的一步。開啓慢查詢日誌後,因爲日誌記錄操做,在必定程度上會佔用CPU資源影響mysql的性能,可是能夠階段性開啓來定位性能瓶頸。

explain分析查詢

使用 EXPLAIN 關鍵字能夠模擬優化器執行SQL查詢語句,從而知道MySQL是如何處理你的SQL語句的。這能夠幫你分析你的查詢語句或是表結構的性能瓶頸。經過explain命令能夠獲得:

mysql> explain select * from test1.tb1 where 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、const、eq_reg、ref、range、index和ALL

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

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

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

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

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

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

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

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

mysql> create index index_stuname on test1.tb1(stuname);

再次執行explain

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

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

id: 1

select_type: SIMPLE

table: tb1

partitions: NULL

type: ref

possible_keys: index_stuname

key: index_stuname

key_len: 62

ref: const

rows: 1

filtered: 100.00

Extra: NULL

1 row in set, 1 warning (0.00 sec)

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

profiling分析查詢

經過慢日誌查詢能夠知道哪些SQL語句執行效率低下,經過explain咱們能夠得知SQL語句的具體執行狀況,索引使用等,還能夠結合show命令查看執行狀態。若是以爲explain的信息不夠詳細,能夠同經過profiling命令獲得更準確的SQL執行消耗系統資源的信息。

profiling默認是關閉的。能夠經過如下語句查看

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

image

image

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

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

mysql> select @@profiling;

+---------------------+

| @@profiling |

+---------------------+

| 1 |

+----------------------+

執行要測試的sql語句

mysql> select * from test1.tb1 where stuname='admin' and entertime='2016-9-1';

mysql> show profiles\G; //能夠獲得被執行的SQL語句的時間和ID

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

Query_ID: 1

Duration: 0.00012650

Query: select @@profiling

*************************** 2. row ***************************

Query_ID: 2

Duration: 0.00121725

Query: select * from test1.tb1 where stuname='admin' and entertime='2016-9-1'

mysql> show profile for query 2; //獲得對應SQL語句執行的詳細信息

+----------------------+-------------------------+

| Status | Duration |

+----------------------+-------------------------+

| starting | 0.000230 |

| checking permissions | 0.000013 |

| Opening tables | 0.000030 |

| init | 0.000087 |

| System lock | 0.000018 |

| optimizing | 0.000128 |

| statistics | 0.000378 |

| preparing | 0.000026 |

| executing | 0.000005 |

| Sending data | 0.000187 |

| end | 0.000013 |

| query end | 0.000011 |

| closing tables | 0.000010 |

| freeing items | 0.000061 |

| cleaning up | 0.000021 |

+----------------------+-------------------------+

status:是profile裏的狀態,duration:是status狀態下的耗時。所以咱們關注的就是那個狀態最耗時,這些狀態中那些能夠優化。

固然也能夠查看更多的信息如CPU等等

SHOW PROFILE [type [, type] ... ] [FOR QUERY n]

type:
       ALL:顯示全部的開銷信息
      BLOCK IO:顯示塊IO相關開銷
      CPU:顯示用戶CPU時間、系統CPU時間
      IPC:顯示發送和接收相關開銷信息
       PAGE FAULTS:顯示頁面錯誤相關開銷信息
      SWAPS:顯示交換次數相關開銷的信息

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

mysql> set profiling=0;

二、配置優化

Mysql參數優化對於不一樣的網站,及其在線量,訪問量,帖子數量,網絡狀況,以及機器硬件配置都有關係,優化不可能一次性完成,須要不斷的觀察以及調試,纔有可能獲得最佳效果。

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

1) 鏈接請求的變量:

1.max_connections

MySQL的最大鏈接數,若是服務器的併發鏈接請求量比較大,建議調高此值,以增長並行鏈接數量,固然這創建在機器能支撐的狀況下,由於若是鏈接數越多, MySQL會爲每一個鏈接提供鏈接緩衝區,就會開銷越多的內存,因此要適當調整該值,不能盲目提升設值。

數值太小會常常出現ERROR 1040: Too many connections錯誤,能夠過mysql> show status 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的設置將要對你的web application沒有多大的影響

查看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:100,1:1000更好(上述狀態值可使用SHOW STATUS LIKE ‘key_read%’得到)。

image

一共有6個索引讀取請求,有3個請求在內存中沒有找到直接從硬盤讀取索引,計算索引未命中緩存的機率:
key_cache_miss_rate = Key_reads / Key_read_requests * 100% =50%

key_buffer_size只對MyISAM表起做用。即便你不使用MyISAM表,可是內部的臨時磁盤表是MyISAM表,也要使用該值。可使用檢查狀態值created_tmp_disk_tables得知詳情。

image

如何調整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%’得到)。

image

Qcache_free_blocks:緩存中相鄰內存塊的個數。若是該值顯示較大,則說明Query Cache 中的內存碎片較多了,FLUSH QUERY CACHE會對緩存中的碎片進行整理,從而獲得一個空閒塊。

注:當一個表被更新以後,和它相關的cache blocks將被free。可是這個block依然可能存在隊列中,除非是在隊列的尾部。能夠用FLUSH QUERY CACHE語句來清空free blocks
Qcache_free_memory:Query 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:當前Query Cache 中cache 的Query 數量;
Qcache_total_blocks:當前Query Cache 中的block 數量;。

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

image

上圖能夠看出query_cache_type爲off表示不緩存任何查詢

各字段的解釋:
query_cache_limit:超過此大小的查詢將不緩存
query_cache_min_res_unit:緩存塊的最小大小 ,query_cache_min_res_unit的配置是一柄」雙刃劍」,默認是4KB,設置值大對大數據查詢有好處,但若是你的查詢都是小數據查詢,就容易形成內存碎片和浪費。
query_cache_size:查詢緩存大小 (注:QC存儲的最小單位是1024 byte,因此若是你設定了一個不是1024的倍數的值,這個值會被四捨五入到最接近當前值的等於1024的倍數的值。)
query_cache_type:緩存類型,決定緩存什麼樣的查詢,注意這個值不能隨便設置,必須設置爲數字,可選項目以及說明以下:

image

若是設置爲0,那麼能夠說,你的緩存根本就沒有用,至關於禁用了。

若是設置爲1,將會緩存全部的結果,除非你的select語句使用SQL_NO_CACHE禁用了查詢緩存。

若是設置爲2,則只緩存在select語句中經過SQL_CACHE指定須要緩存的查詢。

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

query_cache_size=256M
query_cache_type=1

保存文件,從新啓動MYSQL服務,而後經過以下查詢來驗證是否真正開啓了:

image

image

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) 在Procedure,Function 以及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級參數,在每一個connection(session)第一次須要使用這個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的倍數。

9.join_buffer_size = 2M   
用於表間關聯緩存的大小,和sort_buffer_size同樣,該參數對應的分配內存也是每一個鏈接獨享。

10. thread_cache_size = 300   
服務器線程緩存,這個值表示能夠從新利用保存在緩存中線程的數量,當斷開鏈接時,那麼客戶端的線程將被放到緩存中以響應下一個客戶而不是銷燬(前提是緩存數未達上限),若是線程從新被請求,那麼請求將從緩存中讀取,若是緩存中是空的或者是新的請求,那麼這個線程將被從新建立,若是有不少新的線程,增長這個值能夠改善系統性能.經過比 較 Connections 和 Threads_created 狀態的變量,能夠看到這個變量的做用。設置規則以下:1GB 內存配置爲8,2GB配 置爲16,3GB配置爲32,4GB或更高內存,可配置更大。服務器處理此客戶的線程將會緩存起來以響應下一個客戶而不是銷燬(前提是緩存數未達上限)

image

試圖鏈接到MySQL(無論是否鏈接成功)的鏈接數

image

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內存的機器,推薦值是1G(50%)。 若是你的數據量不大,而且不會暴增,那麼無需把 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

主要控制了innodb將log buffer中的數據寫入日誌文件並flush磁盤的時間點,取值分別爲0、一、2三個。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工具進行測試

[root@localhost ~]#mysqlslap --defaults-file=/etc/my.cnf --concurrency=10 --iterations=1 --create-schema='test1' --query='select * from test1.tb1' --engine=innodb --number-of-queries=2000 -uroot -p123456 –verbose

顯示結果:

Benchmark

Running for engine innodb

Average number of seconds to run all queries: 13.837 seconds

Minimum number of seconds to run all queries: 13.837 seconds

Maximum number of seconds to run all queries: 13.837 seconds

Number of clients running queries: 10

Average number of queries per client: 200

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

[root@localhost ~]#mysqlslap --defaults-file=/etc/my.cnf --concurrency=10 --iterations=1 --create-schema='test1' --query='select * from test1.tb1' --engine=innodb --number-of-queries=2000 -uroot -p123456 –verbose

顯示結果:

Benchmark

Running for engine innodb

Average number of seconds to run all queries: 4.199 seconds

Minimum number of seconds to run all queries: 4.199 seconds

Maximum number of seconds to run all queries: 4.199 seconds

Number of clients running queries: 10

Average number of queries per client: 200

相關優化參數總結:

[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

http://hongge.blog.51cto.com/

相關文章
相關標籤/搜索