MySQL性能【查詢截取分析】

查詢優化

小表驅動大表

優化原則:小表驅動大表,即小的數據集驅動大的數據集。

select * from A where id in (select id from B)
等價於:
for select id from B
for select * from A where A.id = B.id

當 B 表的數據集必須小於 A 表的數據集時,用 in 優於 exists。

select * from A where exists (select 1 from B where B.id = A.id)
等價於:
for select * from A
for select * from B where B.id = A.id

當 A 表的數據集小於 B 表的數據集時,用 exists 優先於 in。

  • EXISTS

select … from table where exists(subquery)

該語法可以理解爲:將主查詢的數據,放到子查詢中做條件驗證,根據驗證結果(true 或 false)來決定主查詢的數據結果是否得以保留。

orderr by 關鍵字優化

  • order by 字句,儘量使用 index 方式排序,避免使用 filesort 方式排序

MySQL 支持兩種方式對的排序, FileSort 和 index,index 效率高,它指 MySQL 掃描索引本身完成排序。FileSort 方式效率較低。

Order by 滿足兩種情況,會使用 Index 方式排序

Order by 語句使用索引最左前列
使用 Where 字句與 Order By 字句條件列組合滿足索引最左前列

  • 儘可能在索引列上完成排序操作,遵照索引的最佳左前綴原則。
  • 如果不在索引列上,filesort 有兩種算法:mysql 就要啓動雙路排序和單路排序
  1. 雙路排序

    • MySQL 4.1 之前時使用雙路排序,字面意思就是兩次掃描磁盤,最終得到數據,讀取行指針和 order by 列,對他們進行排序,然後掃描已經排序好的列表,按照列表中的值重寫從列表中讀取對應的數據輸出。
    • 從磁盤取出排序字段,在 buffer 進行排序,再從磁盤取出其他字段。
  2. 取一批數據,要對磁盤進行兩次掃描,衆所周知,I\O是很耗時的,所以在 mysql4.1 之後,出現了第二種改進的算法,就是單路排序。

  3. 單路排序

    • 從磁盤中讀取查詢需要的所有列,按照 order by 列在 buffer 對它們進行排序,然後掃描排序後的列表進行輸出,它的效率更快一些,避免了第二次讀取數據。並且把隨機 IO 變成了順序 IO,但是它會使用更多的空間,因爲它把每一行都保存在內存中了。
  • 優化策略

增大 sort_buffer_size 參數的設置
增大 max_length_for_sort_data 參數的設置

在這裏插入圖片描述

group by 關鍵字優化

group by 實質是先排序後進行分組,遵照索引建的最佳左前綴

當無法使用索引列,增加 max_length_for_sort_data 參數的設置 + 增大 sort_buffer_size 參數的設置

where 高於 having,能寫在 where 限定的條件就不要去 having 限定了。

慢查詢日誌

MySQL 的慢查詢日誌是 MySQL 提供的一種日誌記錄,它用來記錄在 MySQL 中相應時間超過閾值的語句,具體指運行時間超過 long_query_time 值得 SQL,則會被記錄到慢查詢日誌中。

具體指運行時間超過 long_query_time 值得 SQL,則會被記錄到慢查詢日誌中。long_query_time 得默認值爲 10,意思是運行 10 秒以上的語句。

由它來查看哪些 SQL 超過了我們得最大忍耐時間值,比如一條 SQL 執行超過5秒鐘,我們就算它是慢 SQL,希望能收集超過5秒得SQL,結合 explain 進行全面分析。

如何使用慢查詢日誌

默認情況下,MySQL 數據庫沒有開啓慢查詢日誌,需要我們手動來設置這個參數。
當然,如果不是調優需要得話,一般不建議啓動該參數,因爲開啓慢查詢日誌會或多或少帶來一定的性能影響。慢查詢日誌支持將日誌記錄寫入文件。

查看是否開啓以及如何開啓

默認:show variables like '%slow_query_log%';
開啓:set global slow_query_log = 1;

如何永久生效,修改配置文件 my.cnf 即可(不建議長期開啓)

修改 my.cnf 文件,【mysqld】下增加或修改參數
slow_query_log 和 slow_query_log_file 後,然後重啓MySQL服務器即可。

具體的配置參數如下:

slow_query_log=1
slow_query_log_file=/var/lib/mysql/mysql-slow.log

關於慢查詢得參數 slow_query_log_file,它指定慢查詢日誌文件得存放路徑,系統默認會給一個缺省的文件 host_name-slow.log(如果沒有指定參數 slow_query_log_file的話)

什麼樣的 SQL 纔會記錄到慢查詢日誌中?

這個是由參數 long_query_time 控制,默認情況下 long_query_time 的值爲 10 秒。
命令:show variables like 'long_query_time%';

在這裏插入圖片描述

可以使用命令修改,也可以在 my.cnf 參數裏面修改。
假如運行時間正好等於 long_query_time 的情況,並不會被記錄下來,也就是說:在 mysql 源碼裏是判斷大於 long_query_time,而非大於等於

Case 例子

  1. 查看當前多少秒算滿 > show variables like 'long_query_time%';
  2. 設置慢的閾值時間:set global long_query_time=3;

在這裏插入圖片描述

  1. 爲什麼設置後看不出變化

需要重新連接或新開一個會話才能看到修改值。
show variables like 'long_query_time%;
show global variables like 'long_query_time';

在這裏插入圖片描述

  1. 記錄慢 SQL 並後續分析

在這裏插入圖片描述

  1. 查詢當前系統中有多少條慢查詢記錄

show global status like '%Slow_queries%%';

在這裏插入圖片描述

永久配置慢日誌開啓

【mysqld】下配置:
slow_query_log==1;
slow_query_log_file=/var/lib/mysql/atguigu-slow.log
long_query_time=3;
log_output=FILE

日誌分析工具 mysqldumpslow

在生產環境中,如果要手工分析日誌,查找、分析SQL,MySQL提供了日誌分析工具 mysqldumpslow。

查看 mysqldumpslow 的幫助指令:mysqldumpslow --help,各個參數的意義如下:

  1. s:是表示按照何種方式排序
  2. c:訪問次數
  3. I:鎖定時間
  4. r:返回記錄
  5. t:查詢時間
  6. aI:平均鎖定時間
  7. ar:平均返回記錄數
  8. at:平均查詢時間
  9. t:即爲返回前面多少條的數據
  10. g:後邊搭配一個正則匹配模式,大小寫不敏感的。

常見例子:

// 得到返回記錄集最多的 10 個SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log

// 得到訪問次數最多的 10 個SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log

// 得到按照時間排序的前 10 條裏面含有左連接的查詢語句
mysqldumpslow -s t -t -g "left join" /var/lib/mysql/atguigu-slow.log

// 另外建立在使用這些命令時結合 | 和 more 使用,否則有可能出現爆屏情況
mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more

批量數據腳本

例如往表中插入 1000W 條數據。

1. 建表

# 新建庫
create database bigData;
use bigData;

# 建部門表 dept
create table dept(
id int unsigned primary key auto_increment,
deptno mediumint unsigned not null default 0,
dname varchar(20) not null default "",
loc varchar(13) not null default ""
)engine=innodb default charset=GBK;

# 建員工表 emp
create table emp(
id int unsigned primary key auto_increment,
empno mediumint unsigned not null default 0,/*編號*/
ename varchar(20) not null default "",/*名字*/
job varchar(9) not null default "",/*工作*/
mgr mediumint unsigned not null default 0,/*上級編號*/
hiredate date not null,/*入職時間*/
sal decimal(7,2) not null,/*薪水*/
comm decimal(7,2) not null,/*紅利*/
deptno mediumint unsigned not null default 0/*部門編號*/
)engine=innodb default charset=gbk;

2. 設置參數 log_bin_trust_function_creators

創建函數,假如報錯:This function has none of deterministic…

# 由於開啓過慢查詢日誌,因爲我們開啓了 bin-log,我們就必須爲我們的 function 指定一個參數。
show variables like 'log_bin_trust_function_creators';
set global log_bin_trust_function_creators=1;

# 這樣添加了參數以後,如果 mysqld 重啓,上述參數又會消失,永久方法:
windows:my.ini文件[mysqld]加上log_bin_trust_function_creators=1
linux:/etc/my.cnf下my.cnf文件[mysqld]加上log_bin_trust_function_creators=1

3. 創建函數,保證每條數據都不同

  • 隨機產生字符串:
DELIMITER $$ 
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255) 
BEGIN 
	DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'; 
	DECLARE return_str VARCHAR(255) DEFAULT ''; 
	DECLARE i INT DEFAULT 0; 
	WHILE i < n DO 
	SET return_str=CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1)); 
	SET i = i + 1; 
	END WHILE; 
	RETURN return_str; 
END $$

# 假如要刪除此函數
# drop function rand_string;
  • 隨機產生部門編號:
DELIMITER $$ 
CREATE FUNCTION rand_num()
RETURNS INT(5)
BEGIN 
	DECLARE i INT DEFAULT 0;
	SET i = FLOOR(100+RAND()*10);
	RETURN i;
END $$

# 假如要刪除此函數
# drop function rand_num;

4. 創建存儲過程

  • 創建往 emp 表中插入數據的存儲過程
# 執行存儲過程,往 emp 表中添加隨機數據
DELIMITER $$
CREATE PROCEDURE insert_emp(IN START INT(10), IN max_num INT(10))
BEGIN
	DECLARE i INT DEFAULT 0;
	# set autocommit = 0 把 autocommit 設置爲0
	SET autocommit=0;
	REPEAT
	SET i = i + 1;
	INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES ((START+i), rand_string(6),'SALESMAN',0001,CURDATE(),2000,400,rand_num());
	UNTIL i = max_num
	END REPEAT;
	COMMIT;
END $$
  • 創建往 dept 表中插入數據的存儲過程
// 執行存儲過程,往 dept 表中添加隨機數據
DELIMITER $$
CREATE PROCEDURE insert_dept(IN START INT(10), IN max_num INT(10))
BEGIN
	DECLARE i INT DEFAULT 0;
	SET autocommit = 0;
	REPEAT
	SET i = i + 1;
	INSERT INTO dept (deptno, dname, loc) VALUES ((START+i), rand_string(10), rand_string(8));
	UNTIL i = max_num
	END REPEAT;
	COMMIT;
END $$

5. 調用存儲過程

  • 先調用 dept
DELIMITER ;
CALL insert_dept(100,10);
  • 在調用 emp【機器性能不行的建立插入條數可以適當降低】
DELIMITER ;
CALL insert_emp(100001,500000);

在這裏插入圖片描述

Show Profile

Show Profile 是 mysql 提供可以用來分析當前會話中語句執行的資源消耗情況。可以用於 SQL 的調優的測量

官網:http://dev.mysql.com/doc/refman/5.5/en/show-profile.html

默認情況下,參數處於關閉狀態,並保存最近 15 次的運行結果

Show Profile 分析步驟

  1. 是否支持,看看當前版本是否支持
Show variables like 'profiling';

在這裏插入圖片描述

  1. 開啓功能,默認關閉,使用前需要開啓
set profiling=on;
  1. 運行 SQL
  2. 查看結果:show profiles;
  3. 診斷 SQL:show profile cpu,block io for query 上一步前面的問題 SQL 數字號碼;

參數備註:
type:
| ALL --顯示所有的開銷信息
| BLOCK IO --顯示塊IO相關開銷
| CONTEXT SWITCHES --上下文切換相關開銷
| CPU --顯示CPU相關開銷信息
| IPC --顯示發送和接收相關開銷信息
| MEMORY --顯示內存相關開銷信息
| PAGE FAULTS --顯示頁面錯誤相關開銷信息
| SOURCE --顯示和 Source_function,Source_file,Source_line 相關的開銷信息
| SWAPS --顯示交換次數相關開銷的信息

  1. 日常開發需要注意的結論

converting HEAP to MyISAM 查詢結果太大,內存都不夠用了往磁盤上搬了。

Creating tmp table 創建臨時表:拷貝數據到臨時表;用完再刪除。

Copying to tmp table on disk 把內存中臨時表複製到磁盤,危險!!!

locked

全局查詢日誌

千萬不要在生產環境開啓這個功能。

配置啓動

在 mysql 的 my.cnf 中,配置如下:
# 開啓
general_log=1
# 記錄日誌文件的路徑
general_log_file=/path/logfile
# 輸出格式
log_output=FILE

編碼啓動

set global general_log=1;
set global log_output='TABLE';
// 此命令用於查看你編寫的 sql 語句
select * from mysql.general_log;

永遠不要在生產環境開啓這個功能。

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