本篇是MySQL知識體系總結系列的第二篇,該篇的主要內容是通過explain逐步分析sql,並通過修改sql語句與建立索引的方式對sql語句進行調優,也可以通過查看日誌的方式,瞭解sql的執行情況,還介紹了MySQL數據庫的行鎖和表鎖。
一、explain返回列簡介
1、type常用關鍵字
system:表僅有一行,基本用不到;
const:表最多一行數據配合,主鍵查詢時觸發較多;
eq_ref:對於每個來自於前面的表的行組合,從該表中讀取一行。這可能是最好的聯接類型,除了const類型;
ref:對於每個來自於前面的表的行組合,所有有匹配索引值的行將從這張表中讀取;
range:只檢索給定範圍的行,使用一個索引來選擇行。當使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比較關鍵字列時,可以使用range;
index:該聯接類型與ALL相同,除了只有索引樹被掃描。這通常比ALL快,因爲索引文件通常比數據文件小;
all:全表掃描;
2、Extra常用關鍵字
Using index:只從索引樹中獲取信息,而不需要回表查詢;
Using where:WHERE子句用於限制哪一個行匹配下一個表或發送到客戶。除非你專門從表中索取或檢查所有行,如果Extra值不爲Using where並且表聯接類型爲ALL或index,查詢可能會有一些錯誤。需要回表查詢。
Using temporary:mysql常建一個臨時表來容納結果,典型情況如查詢包含可以按不同情況列出列的GROUP BY和ORDER BY子句時;
二、觸發索引代碼實例
1、建表語句 + 聯合索引
CREATE TABLE `student` (
`id` int(10) NOT NULL,
`name` varchar(20) NOT NULL,
`age` int(10) NOT NULL,
`sex` int(11) DEFAULT NULL,
`address` varchar(100) DEFAULT NULL,
`phone` varchar(100) DEFAULT NULL,
`create_time` timestamp NULL DEFAULT NULL,
`update_time` timestamp NULL DEFAULT NULL,
`deleted` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `student_union_index` (`name`,`age`,`sex`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、使用主鍵查詢
3、使用聯合索引查詢
4、聯合索引,但與索引順序不一致
5、聯合索引,但其中一個條件是 >
6、聯合索引,order by
三、單表sql優化
1、刪除student表中的聯合索引。
2、添加索引
alter table student add index student_union_index(name,age,sex);
3、更改索引順序
select distinct ... from ... join ... on ... where ... group by ... having ... order by ... limit ...
from ... on ... join ... where ... group by ... having ... select distinct ... order by ... limit ...
alter table student add index student_union_index2(age,sex,name);
drop index student_union_index on student
ALTER TABLE student RENAME INDEX student_union_index2 TO student_union_index
4、去掉in
5、小結
保持索引的定義和使用順序一致性;
索引需要逐步優化,不要總想着一口喫成胖子;
將含in的範圍查詢,放到where條件的最後,防止索引失效;
四、雙表sql優化
1、建表語句
CREATE TABLE `student` (
`id` int(10) NOT NULL,
`name` varchar(20) NOT NULL,
`age` int(10) NOT NULL,
`sex` int(11) DEFAULT NULL,
`address` varchar(100) DEFAULT NULL,
`phone` varchar(100) DEFAULT NULL,
`create_time` timestamp NULL DEFAULT NULL,
`update_time` timestamp NULL DEFAULT NULL,
`deleted` int(11) DEFAULT NULL,
`teacher_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `teacher` (
`id` int(11) DEFAULT NULL,
`name` varchar(100) DEFAULT NULL,
`course` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、左連接查詢
explain select s.name,t.name from student s left join teacher t on s.teacher_id = t.id where t.course = '數學'
聯合查詢時,小表驅動大表。小表也稱爲驅動表。其實就相當於雙重for循環,小表就是外循環,第二張表(大表)就是內循環。
雖然最終的循環結果都是一樣的,都是循環一樣的次數,但是對於雙重循環來說,一般建議將數據量小的循環放外層,數據量大的放內層,這是編程語言的優化原則。
再次代碼測試:
student數據:四條
explain select teacher.name,student.name from teacher left join student on teacher.id = student.id where teacher.course = '數學'
優化一般是需要索引的,那麼此時,索引應該怎麼加呢?往哪個表上加索引?
索引的基本理念是:索引要建在經常使用的字段上。
由on teacher.id = student.id可知,teacher表的id字段使用較爲頻繁。
left join on,一般給左表加索引;因爲是驅動表嘛。
alter table teacher add index teacher_index(id);
alter table teacher add index teacher_course(course);
3、小結
小表驅動大表
索引建立在經常查詢的字段上
sql優化,是一種概率層面的優化,是否實際使用了我們的優化,需要通過explain推測。
五、避免索引失效的一些原則
1、複合索引,不要跨列或無序使用(最佳左前綴);
2、符合索引,儘量使用全索引匹配;
3、不要在索引上進行任何操作,例如對索引進行(計算、函數、類型轉換),索引失效;
4、複合索引不能使用不等於(!=或<>)或 is null(is not null),否則索引失效;
5、儘量使用覆蓋索引(using index);
6、like儘量以常量開頭,不要以%開頭,否則索引失效;如果必須使用%name%進行查詢,可以使用覆蓋索引挽救,不用回表查詢時可以觸發索引;
7、儘量不要使用類型轉換,否則索引失效;
8、儘量不要使用or,否則索引失效;
六、一些其他的優化方法
1、exist和in
select name,age from student exist/in (子查詢);
2、order by 優化
sing filesort有兩種算法:雙路排序、雙路排序(根據IO的次數)
MySQL4.1之前,默認使用雙路排序;雙路:掃描兩次磁盤(①從磁盤讀取排序字段,對排序字段進行排序;②獲取其它字段)。
MySQL4.1之後,默認使用單路排序;單路:只讀取一次(全部字段),在buffer中進行排序。但單路排序會有一定的隱患(不一定真的是隻有一次IO,有可能多次IO)。
注意:單路排序會比雙路排序佔用更多的buffer。
單路排序時,如果數據量較大,可以調大buffer的容量大小。
set max_length_for_sort_data = 1024;單位是字節byte。
如果max_length_for_sort_data值太低,MySQL底層會自動將單路切換到雙路。
太低指的是列的總大小超過了max_length_for_sort_data定義的字節數。
提高order by查詢的策略:
選擇使用單路或雙路,調整buffer的容量大小;
避免select * from student;(① MySQL底層需要對*進行翻譯,消耗性能;② *永遠不會觸發索引覆蓋 using index);
符合索引不要跨列使用,避免using filesort;
保證全部的排序字段,排序的一致性(都是升序或降序);
七、sql順序 -> 慢日誌查詢
1、慢查詢日誌
show variables like '%slow_query_log%'
(2)臨時開啓:
set global slow_query_log = 1;
service mysql restart;
slow_query_log=1
slow_query_log_file=/var/lib/mysql/localhost-slow.log
2、閾值
show variables like '%long_query_time%'
set global long_query_time = 5;
(3)永久修改默認閾值:
/etc/my.cnf中追加配置:
放到[mysqld]下:
long_query_time = 5;
(4)MySQL中的sleep:
select sleep(5);
(5)查看執行時間超過閾值的sql:
show global status like '%slow_queries%';
八、慢查詢日誌 --> mysqldumpslow工具
1、mysqldumpslow工具
cat /var/lib/mysql/localhost-slow.log
mysqldumpslow --help
2、查詢不同條件下的慢sql
mysqldumpslow -s r -t 3 /var/lib/mysql/localhost-slow.log
mysqldumpslow -s c -t 3 /var/lib/mysql/localhost-slow.log
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/localhost-slow.log
九、分析海量數據
1、show profiles
2、精確分析,sql診斷
3、全局查詢日誌
十、鎖機制詳解
1、操作分類
2、操作範圍
如MyISAM存儲引擎使用表鎖,開銷小、加鎖快、無死鎖;但鎖的範圍大,容易發生衝突、併發度低。
行鎖:一次性對一條數據加鎖。
如InnoDB存儲引擎使用的就是行鎖,開銷大、加鎖慢、容易出現死鎖;鎖的範圍較小,不易發生鎖衝突,併發度高(很小概率發生高併發問題:髒讀、幻讀、不可重複讀)
lock table 表1 read/write,表2 read/write,...
查看加鎖的表:
show open tables;
3、加讀鎖,代碼實例
會話0:
lock table student read;
select * from student; --查,可以
delete from student where id = 1;--增刪改,不可以
select * from user; --查,不可以
delete from user where id = 1;--增刪改,不可以
會話1:
select * from student; --查,可以
delete from student where id = 1;--增刪改,會“等待”會話0將鎖釋放
會話1:
select * from user; --查,可以
delete from user where id = 1;--增刪改,可以
4、加寫鎖
會話0:
lock table student write;
5、MyISAM表級鎖的鎖模式
(1)對MyISAM表的讀操作(加讀鎖),不會阻塞其它會話(進程)對同一表的讀請求。但會阻塞對同一表的寫操作。只有當讀鎖釋放後,纔會執行其它進程的寫操作。
(2)對MyISAM表的寫操作(加寫鎖),會阻塞其它會話(進程)對同一表的讀和寫操作,只有當寫鎖釋放後,纔會執行其它進程的讀寫操作。
6、MyISAM分析表鎖定
able_locks_immediate:可能獲取到的鎖數
Table_locks_waited:需要等待的表鎖數(該值越大,說明存在越大的鎖競爭)
一般建議:Table_locks_immediate/Table_locks_waited > 5000,建議採用InnoDB引擎,否則採用MyISAM引擎。
7、InnoDB分析表鎖定
Innodb_row_lock_current_waits:當前正在等待鎖的數量
Innodb_row_lock_time:等待總時長。從系統啓動到現在一共等待的時間
Innodb_row_lock_time_avg:平均等待時長。從系統啓動到現在一共等待的時間
Innodb_row_lock_time_max:最大等待時長。從系統啓動到現在一共等待的時間
Innodb_row_lock_waits:等待次數。從系統啓動到現在一共等待的時間
8、加行鎖代碼實例
select id,name,age from student
update student set age = 18 where id = 1
9、行鎖的注意事項
本文分享自微信公衆號 - JAVA高級架構(gaojijiagou)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。