文章目錄
查詢優化
小表驅動大表
優化原則:小表驅動大表,即小的數據集驅動大的數據集。
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 就要啓動雙路排序和單路排序
-
雙路排序
- MySQL 4.1 之前時使用雙路排序,字面意思就是兩次掃描磁盤,最終得到數據,讀取行指針和 order by 列,對他們進行排序,然後掃描已經排序好的列表,按照列表中的值重寫從列表中讀取對應的數據輸出。
- 從磁盤取出排序字段,在 buffer 進行排序,再從磁盤取出其他字段。
-
取一批數據,要對磁盤進行兩次掃描,衆所周知,I\O是很耗時的,所以在 mysql4.1 之後,出現了第二種改進的算法,就是單路排序。
-
單路排序
- 從磁盤中讀取查詢需要的所有列,按照 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 例子
- 查看當前多少秒算滿 >
show variables like 'long_query_time%';
- 設置慢的閾值時間:
set global long_query_time=3;
- 爲什麼設置後看不出變化
需要重新連接或新開一個會話才能看到修改值。
show variables like 'long_query_time%;
show global variables like 'long_query_time';
- 記錄慢 SQL 並後續分析
- 查詢當前系統中有多少條慢查詢記錄
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
,各個參數的意義如下:
- s:是表示按照何種方式排序
- c:訪問次數
- I:鎖定時間
- r:返回記錄
- t:查詢時間
- aI:平均鎖定時間
- ar:平均返回記錄數
- at:平均查詢時間
- t:即爲返回前面多少條的數據
- 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 分析步驟
- 是否支持,看看當前版本是否支持
Show variables like 'profiling';
- 開啓功能,默認關閉,使用前需要開啓
set profiling=on;
- 運行 SQL
- 查看結果:
show profiles;
- 診斷 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 --顯示交換次數相關開銷的信息
- 日常開發需要注意的結論
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;
永遠不要在生產環境開啓這個功能。