1.單表主鍵查詢:
語句:
select * fromgmvcsbase.base_file where id='29830957'
執行計劃:
Id爲base_file表的主鍵,
Select_type爲simple表示簡單的select,沒有union和子查詢
Table爲base_file表示輸出的行所用的表
Type爲const表示表最多有一個匹配行,const用於比較primary key 或者unique索引。
Possible_keys提示使用哪些索引會在該表中找到行
Key表示實際使用的索引,實際使用的是主鍵索引
Key_len表示使用的索引部分的長度
Ref顯示使用哪個列或常數與key一起從表中選擇行
Rows表示執行查詢的行數,數值越大越不好,說明沒有用好索引
Extra包含MySQL解決查詢的詳細信息
執行圖:
查詢base_file表
Access Type訪問類型Single Row單行
Cost Hint代價估算 Very low cost很低的代價
Key/Index:PRIMARY使用主鍵索引
Used Key Parts使用了主鍵索引的哪部分
Filtered過濾比率,100%過濾最好,小於1%最差
過濾比率低則表明查詢檢查了很多行數據,但是很多都不滿足條件。
執行結果:
結論:
以上是MySQL單表主鍵查詢的執行計劃。結論自己理解吧。
2.單表唯一索引查詢
語句:
select * from gmvcsbase.base_file where sid='c465202be7664ff5a11663f7fd1bff4b'
執行計劃:
執行圖:
執行結果:
結論:
單表唯一索引查詢和單表主鍵查詢在效率上幾乎一樣。由於唯一索引和主鍵索引是一對一的關係,索引找到唯一索引值即可直接找到主鍵值。
3.單表非唯一索引查詢
語句:
select * from gmvcsbase.base_file where police_id='00003771' limit 10
執行計劃:
Type爲ref,如果鍵不是UNIQUE或PRIMARY KEY(換句話說,如果聯接不能基於關鍵字選擇單個行的話),或者聯接只使用鍵的最左邊的前綴,則使用ref。
Possible_keys有很多,可以理解爲任何包含police_id的索引都是possible_keys
實際使用的是index_user_id。
Rows有2999個,說明滿足police_id=’00003771’的數據大概有2999條吧。
Extra使用索引條件
執行圖:
Non-Unique Key Lookup非唯一索引查詢
代價估算:Low if number of matching rows is small,higher as the numberof rows increases。
執行結果:
結論:
非唯一索引查詢比主鍵索引查詢和唯一索引查詢要差一些。
思考不加limit情況會怎樣,limit 2900,20會如何,order by start_capture_time會如何。
4.單表無索引查詢
語句:
select * from gmvcsbase.base_file where device_serial='1615CF85CA08' limit 20
執行計劃:
Type爲ALL表示需要進行全表掃描,不使用索引,通常查詢效率很差。
Rows爲29971746,說明全表數據量大概這麼多行吧。
Extra爲Using where用於限制哪一個行匹配,在這裏的匹配條件是:
device_serial='1615CF85CA08'
執行圖:
29.97M rows就是全表掃描2997萬行數據
代價估算:Very High,No usable indexes,must search every row。
This could also mean thesearch range is so broad that the index would be useless.
也有可能是查詢範圍太廣,索引會沒多大用處。
Key/Index空,沒有使用
Attached Condition附加條件,device_serial='1615CF85CA08',貌似using where就是這個意思了,掃表的時候,判斷這個條件是否滿足。
Filtered爲100%說明attachedCondition在這裏不算過濾條件,因爲這個確實沒辦法算嘛。
執行結果:
0秒不代表查詢快,那是因爲加了limit 20這個條件,如果不加,卡死。
結論:
全表掃描應該避免。
5.單表非唯一索引範圍查詢
語句:
select * from gmvcsbase.base_file
where start_capture_time>='2015-11-01' andstart_capture_time<'2015-11-02'
limit 20
執行計劃:
Type爲range 給定範圍內的檢索,使用一個索引來檢查行。
Key爲index_start_capture_time
Rows713248就是start_cpauture_time在2015-11-01和2015-11-02之間有這麼多條數據。
Extra使用索引條件
執行圖:
71萬數據滿足索引條件
range範圍查詢
代價估算:中等,部分索引掃描
執行結果:
結論:
用索引進行範圍查詢總比不用索引進行範圍查詢好吧。
思考,當查詢start_capture_time大時間範圍時,還會使用index_start_capture_time這個索引麼。
6.單表非唯一索引範圍+附加條件+order by查詢
語句:
select fi.id,fi.sid,fi.name file_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi
where
fi.start_capture_time>= '2015-10-01 00:00:00' and fi.start_capture_time <= '2015-11-2309:25:39'
and fi.status=0
and(fi.workstation_type=1 or fi.workstation_type=0 orfi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
order by fi.start_capture_time desc limit0,20;
執行計劃:
Extra使用索引條件查詢,使用where查詢。
執行圖:
由圖可知,此種查詢首先會使用索引Index_start_capture_time進行範圍查詢,滿足
fi.start_capture_time >= '2015-10-0100:00:00' and fi.start_capture_time <= '2015-11-23 09:25:39'
條件的是索引搜索範圍。
每次按照時間順序拿X條滿足上面索引條件的記錄,然後找到這些記錄,判斷這些記錄是否滿足where條件:
and fi.status=0
and (fi.workstation_type=1 orfi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
如果全部滿足,則此條記錄返回。
由於index_start_capture_time本身是有序的,而且index_start_capture_time本身就是索引條件,此處order by start_capture_time就不需要再排序了。此處需要加深理解。
執行結果:
結論:
單獨的使用索引範圍並且有limit查詢時是很快的,但如果有where附加條件,那麼問題就不一樣了,比如說where附加條件實際情況不存在的情況,索引範圍查詢也會遍歷滿足索引條件的所有記錄,然後才知道滿足where附加條件的數據根本不存在。下一條給出示例。
7.單表非唯一索引範圍+附加條件(附加條件不存在於實際數據中)+order by查詢
語句:
select fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and (fi.workstation_type=1or fi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
and fi.type='0' order by fi.start_capture_time desc limit 0,20;
執行計劃:
和上例一模一樣
執行圖:
和上例基本一樣,只是attachedcondition附加條件中多了一條and fi.type='0'媒體類型爲其他。
執行結果:
查詢耗時2.403秒,查詢出結果爲空即沒有行滿足查詢條件。
結論:
要儘量避免此類情況的出現,如果有這類查詢,需要做特殊處理吧。
8.單表非唯一索引範圍+附加條件+order by(不按照索引條件)查詢
語句:
select fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and (fi.workstation_type=1or fi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
order by fi.capture_time desc limit 0,20;
執行計劃:
Extra多了個Using filesort就是要排序的意思
執行圖:
Using Filesort爲True,意思是沒法索引自然排序,必須要把數據全部查出來,然後再排序。
執行結果:
這個耗時跟上面的不存在條件查詢差不多,因爲兩者都進行了全索引範圍查詢,不然的話不能保證排序是正確的
結論:
排序要慎用,尤其當查詢的數據量大的時候,如果排序不能直接按照索引自然排,則查詢會查所有,然後再排序和limit。
9.單表LIKE索引查詢
語句1:
select fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi
where
police_id like'00003771%'
limit 0,20;
語句2:
select fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi
where
police_id like '%00003771%'
limit 0,20;
執行計劃1:
使用索引index_user_id
執行計劃2:
全表掃描
執行圖1:
滿足索引條件的有3000行數據,部分索引掃描
執行圖2:
全表掃描,無法使用索引
執行結果1:
執行結果2:
結論:
貌似執行結果都蠻快,實際上那是因爲加了limit條件而已。如果,limit條件改動,兩者查詢效率會有十分大的區別。
LIKE 可以使用索引前綴來加快查詢速度,但是是有條件的,就是LIKE條件不能以%開頭,必須是具體的數據。
10.單表聚合(count、sum、avg)查詢
語句1:
select count(*) from gmvcsbase.base_file fiwhere
start_capture_time>='2015-11-01' andstart_capture_time<'2015-11-02'
start_capture_time有加索引
語句2:
select count(*) from gmvcsbase.base_file fiforce index(start_capture_time)where
capture_time>='2015-11-01' andcapture_time<'2015-11-02'
capture_time未加索引,全表掃描
語句3:
Select count(*) from gmvcsbase.base_file fiwhere
capture_time>='2015-11-01' andcapture_time<'2015-11-02'
執行計劃1:
索引掃描
執行計劃2:
全表掃描
執行計劃3:
使用了索引index_user_createtime(police_id,capture_time)
執行圖1:
因爲有個start_capture_time加索引,所以count的時候就可以直接算索引條數即可,而且索引是有序的,這樣就更快了。
執行圖2:
全表掃描,需要掃描3000W條記錄
代價估算非常高
執行圖3:
此次查詢使用了Fullindex Scan全索引掃描
執行結果1:
0.28秒
執行結果2:
24.242秒
執行結果3:
14.4秒
結論:
在進行聚合查詢時,如果聚合查詢的條件能有效使用合適的索引,則能很好地提高查詢效率。
3000W條記錄進行全表掃描耗時24.242秒,如此計算,每秒大概能掃描比對100W條記錄,這是因爲全表掃描是順序讀取數據而非隨機讀取數據,如果是隨機讀取,則耗時不可想象。
語句3走的是聯合索引,而且police_id在前,start_capture_time在後,因此需要進行全索引掃描,效率上比直接索引查詢慢,跟全表掃描的效率其實是同一個量級。
單表查詢總結:
至此,單表查詢就介紹到這裏了,基本概念應該也比較清晰了,下面開始介紹多表聯合查詢。
11.主表left join外表,多對一或一對一關係,主鍵或唯一索引作匹配條件
語句:
select
fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d %H:%i:%s')import_time,
user.name user_name,
dep.name dep_name
from gmvcsbase.base_file fi
left joingmvcsbase.base_department dep ON fi.police_dep = dep.code
left joingmvcsbase.base_user user ON fi.police_id = user.police_id
where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and (fi.workstation_type=1or fi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
order by fi.start_capture_time desc limit0,20;
執行計劃:
執行圖:
Nested loop表示嵌套循環
Unique Key Lookup表示唯一索引查找,因爲dep.code和user.police_id都是各自表中的主鍵,所以查詢時能根據索引直接找到需要的行記錄,而且匹配行數只有一行,速度很快。
執行流程如上圖從上至下依次執行。
首先是base_file表,別名fi,使用索引index_start_capture_time和附加條件查詢出需要的數據。
然後嵌套循環這些查詢出的數據,根據police_code去查找base_department表(別名dep)中相應的部門名稱name。
然後嵌套循環這些數據,根據police_id去查找base_user表(別名user)中的警員名稱name。
最後將查詢出的數據進行排序,由於是按照start_capture_time進行排序,而且在第一步的時候使用了index_start_capture_time,所以在第一步時已經是有序的數據了。
執行結果:
耗時0.016秒
結論:
這種查詢的特點是base_file表爲主表,需要根據base_file表中的某一字段(在外表中爲主鍵或者唯一索引)去外表中獲取對應行的信息。查詢效率很高。
12.主表left join外表,多對一或一對一關係,主鍵或唯一索引作匹配條件,外表有額外條件
語句:
select
fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time,
user.name user_name,
dep.name dep_name
from gmvcsbase.base_file fi
left joingmvcswxs.tmis_filelabel label on fi.sid=label.WJBH
left join gmvcsbase.base_user user ONfi.police_id = user.police_id
left join gmvcsbase.base_department dep ONfi.police_dep = dep.code
where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and (fi.workstation_type=1or fi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
and fi.is_unusual=0 and fi.is_mark =1
and label.BZDM='0301'and label.is_delete=0
order by fi.start_capture_time desc limit0,20;
Base_file表和標註表是1對1的關係。Fi.sid是base_file表的唯一索引,label.WJBH是標註表的唯一索引。where條件中加入了filelabel表中的條件,BZDM標註類別,is_delete=0標註信息未刪除。
執行計劃:
首先查base_file表,然後嵌套循環查出警員名稱,然後嵌套循環查出部門名稱,然後嵌套循環按照and label.BZDM='0301' and label.is_delete=0過濾數據,將滿足條件的前20條數據返回。
執行圖:
跟執行計劃流程基本吻合。
關鍵點看此圖,這裏有了附加條件,通過附加條件進行數據過濾,只有滿足條件的數據纔是我們需要的數據。
顯然這裏的order by也是自然就排好了的。
執行結果:
耗時0.265秒
結論:
很簡單的理解,從base_file表找出了20條數據(按照start_capture_time已排序),遍歷這20條數據,根據當前條數據的fi.police_id去user表查找user.name,相當於如下語句:
Select user.name from user whereuser.police_id=fi.police_id
將查找到的user.name加入該條數據中。
。。。。。。dep表同理。
關於label表也類似,相當於如下語句:
Select 1 from label where label.WJBH =fi.sid andlabel.BZDM='0301' and label.is_delete=0
此處fi.sid有具體的值,是常數,如果返回1,則表示這條記錄滿足條件,如果沒返回1,則不滿足條件,此條記錄被過濾掉。
注意:假如label.BZDM=’某實際不存在的值’,結果應該能夠理解。
假如order bycapture_time,結果也應該能夠理解。無法自然排序的limit是在全部數據查出之後。
13.主表left join外表,多對多或一對多關係,外表附加條件
語句:
select
fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time,
user.name user_name,
dep.name dep_name
from gmvcsbase.base_file fi
left joingmvcswxs.tmis_match gl on fi.sid=gl.WJBH
left join gmvcsbase.base_user user ONfi.police_id = user.police_id
left join gmvcsbase.base_department dep ONfi.police_dep = dep.code
where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and fi.is_relation =1
and (fi.workstation_type=1 orfi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
and gl.SJLX='PECC'and gl.is_delete=0
group by fi.id
order by fi.start_capture_time desc limit0,20
這裏由於是1對多關係,1條base_file表記錄在tmis_match表中有對應多條記錄。
根據left join規則,聯表後,需要group by fi.id才能去除fi表重複的記錄。
執行計劃:
執行圖:
注意gl表的流程,Non-Unique Key Lookup非唯一索引檢索,因爲是1對多關係,gl表中的WJBH不能成爲唯一索引。
注意group的下面有個tmp table,說明gl表聯表查詢後形成了臨時表,然後對這個臨時表進行了group by fi.id操作,最後,將group by好的數據再按照start_capture_time進行排序。
注意,Low ifnumber of matching rows is small,higher as the number of rows increases。
使用了臨時表,沒有排序。
最後order by時,需要排序。
執行結果:
耗時2.262秒,好像還行,可以接受,但這是因爲fi表中已經過濾了大部分數據,如果fi表中沒法過濾大部分數據,那查詢效率不敢想象。
結論:
一對多,多對多是不好優化的,最好在起始表就過濾大量數據,不然後續的臨時表會非常大,對臨時表進行分組,然後排序也會消耗大量資源(內存,時間)。
14.嘗試使用exists進行13的優化
語句:
select
fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time,
user.name user_name,
dep.name dep_name
from gmvcsbase.base_file fi
left join gmvcsbase.base_user user ONfi.police_id = user.police_id
left join gmvcsbase.base_department dep ONfi.police_dep = dep.code
where fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and fi.is_relation =1
and (fi.workstation_type=1 orfi.workstation_type=0 or fi.workstation_type=101)
and fi.police_dep in('440100-00010','440101-00064','440101-00331','440101-00332')
and exists(select 1 fromgmvcswxs.tmis_match gl where gl.SJLX='PECC' and gl.is_delete=0 andfi.sid=gl.WJBH)
order by fi.start_capture_time desc limit0,20
想想爲何這裏沒有groupby
執行計劃:
DEPENDENT SUBQUERY獨立子查詢
執行圖:
這張執行圖就比較複雜了。
起始fi表查詢中多了一個附加條件attached condition。
這裏是需要去做判斷,比如說我查到一條fi表記錄,根據這條記錄的sid去tmis_match表查找該sid是否滿足上面條件,如果滿足條件則此記錄保留,如果不滿足則丟棄。
針對gl表的子查詢如上圖所示,其實就是一條語句
select 1 from gmvcswxs.tmis_match gl wheregl.SJLX='PECC' and gl.is_delete=0 and gl.WJBH=fi.sid
這裏的fi.sid是常數。由於走了索引,WJBH有加索引,所以子查詢效率足夠快。
返回1就是exists。可以推知,無需group by也無需order by也沒有臨時表。
執行結果:
0.156秒較上面的查詢快了至少一個數量級。
結論:
一對多,多對多查詢如果只查主表數據,不涉及到外表,則可以用exists來避免臨時表,groupby和排序。
1.單表主鍵查詢:
語句:
select * fromgmvcsbase.base_file where id='29830957'
執行計劃:
Id爲base_file表的主鍵,
Select_type爲simple表示簡單的select,沒有union和子查詢
Table爲base_file表示輸出的行所用的表
Type爲const表示表最多有一個匹配行,const用於比較primary key 或者unique索引。
Possible_keys提示使用哪些索引會在該表中找到行
Key表示實際使用的索引,實際使用的是主鍵索引
Key_len表示使用的索引部分的長度
Ref顯示使用哪個列或常數與key一起從表中選擇行
Rows表示執行查詢的行數,數值越大越不好,說明沒有用好索引
Extra包含MySQL解決查詢的詳細信息
執行圖:
查詢base_file表
Access Type訪問類型Single Row單行
Cost Hint代價估算 Very low cost很低的代價
Key/Index:PRIMARY使用主鍵索引
Used Key Parts使用了主鍵索引的哪部分
Filtered過濾比率,100%過濾最好,小於1%最差
過濾比率低則表明查詢檢查了很多行數據,但是很多都不滿足條件。
執行結果:
結論:
以上是MySQL單表主鍵查詢的執行計劃。結論自己理解吧。
2.單表唯一索引查詢
語句:
select * from gmvcsbase.base_file where sid='c465202be7664ff5a11663f7fd1bff4b'
執行計劃:
執行圖:
執行結果:
結論:
單表唯一索引查詢和單表主鍵查詢在效率上幾乎一樣。由於唯一索引和主鍵索引是一對一的關係,索引找到唯一索引值即可直接找到主鍵值。
3.單表非唯一索引查詢
語句:
select * from gmvcsbase.base_file where police_id='00003771' limit 10
執行計劃:
Type爲ref,如果鍵不是UNIQUE或PRIMARY KEY(換句話說,如果聯接不能基於關鍵字選擇單個行的話),或者聯接只使用鍵的最左邊的前綴,則使用ref。
Possible_keys有很多,可以理解爲任何包含police_id的索引都是possible_keys
實際使用的是index_user_id。
Rows有2999個,說明滿足police_id=’00003771’的數據大概有2999條吧。
Extra使用索引條件
執行圖:
Non-Unique Key Lookup非唯一索引查詢
代價估算:Low if number of matching rows is small,higher as the numberof rows increases。
執行結果:
結論:
非唯一索引查詢比主鍵索引查詢和唯一索引查詢要差一些。
思考不加limit情況會怎樣,limit 2900,20會如何,order by start_capture_time會如何。
4.單表無索引查詢
語句:
select * from gmvcsbase.base_file where device_serial='1615CF85CA08' limit 20
執行計劃:
Type爲ALL表示需要進行全表掃描,不使用索引,通常查詢效率很差。
Rows爲29971746,說明全表數據量大概這麼多行吧。
Extra爲Using where用於限制哪一個行匹配,在這裏的匹配條件是:
device_serial='1615CF85CA08'
執行圖:
29.97M rows就是全表掃描2997萬行數據
代價估算:Very High,No usable indexes,must search every row。
This could also mean thesearch range is so broad that the index would be useless.
也有可能是查詢範圍太廣,索引會沒多大用處。
Key/Index空,沒有使用
Attached Condition附加條件,device_serial='1615CF85CA08',貌似using where就是這個意思了,掃表的時候,判斷這個條件是否滿足。
Filtered爲100%說明attachedCondition在這裏不算過濾條件,因爲這個確實沒辦法算嘛。
執行結果:
0秒不代表查詢快,那是因爲加了limit 20這個條件,如果不加,卡死。
結論:
全表掃描應該避免。
5.單表非唯一索引範圍查詢
語句:
select * from gmvcsbase.base_file
where start_capture_time>='2015-11-01' andstart_capture_time<'2015-11-02'
limit 20
執行計劃:
Type爲range 給定範圍內的檢索,使用一個索引來檢查行。
Key爲index_start_capture_time
Rows713248就是start_cpauture_time在2015-11-01和2015-11-02之間有這麼多條數據。
Extra使用索引條件
執行圖:
71萬數據滿足索引條件
range範圍查詢
代價估算:中等,部分索引掃描
執行結果:
結論:
用索引進行範圍查詢總比不用索引進行範圍查詢好吧。
思考,當查詢start_capture_time大時間範圍時,還會使用index_start_capture_time這個索引麼。
6.單表非唯一索引範圍+附加條件+order by查詢
語句:
select fi.id,fi.sid,fi.name file_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi
where
fi.start_capture_time>= '2015-10-01 00:00:00' and fi.start_capture_time <= '2015-11-2309:25:39'
and fi.status=0
and(fi.workstation_type=1 or fi.workstation_type=0 orfi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
order by fi.start_capture_time desc limit0,20;
執行計劃:
Extra使用索引條件查詢,使用where查詢。
執行圖:
由圖可知,此種查詢首先會使用索引Index_start_capture_time進行範圍查詢,滿足
fi.start_capture_time >= '2015-10-0100:00:00' and fi.start_capture_time <= '2015-11-23 09:25:39'
條件的是索引搜索範圍。
每次按照時間順序拿X條滿足上面索引條件的記錄,然後找到這些記錄,判斷這些記錄是否滿足where條件:
and fi.status=0
and (fi.workstation_type=1 orfi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
如果全部滿足,則此條記錄返回。
由於index_start_capture_time本身是有序的,而且index_start_capture_time本身就是索引條件,此處order by start_capture_time就不需要再排序了。此處需要加深理解。
執行結果:
結論:
單獨的使用索引範圍並且有limit查詢時是很快的,但如果有where附加條件,那麼問題就不一樣了,比如說where附加條件實際情況不存在的情況,索引範圍查詢也會遍歷滿足索引條件的所有記錄,然後才知道滿足where附加條件的數據根本不存在。下一條給出示例。
7.單表非唯一索引範圍+附加條件(附加條件不存在於實際數據中)+order by查詢
語句:
select fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and (fi.workstation_type=1or fi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
and fi.type='0' order by fi.start_capture_time desc limit 0,20;
執行計劃:
和上例一模一樣
執行圖:
和上例基本一樣,只是attachedcondition附加條件中多了一條and fi.type='0'媒體類型爲其他。
執行結果:
查詢耗時2.403秒,查詢出結果爲空即沒有行滿足查詢條件。
結論:
要儘量避免此類情況的出現,如果有這類查詢,需要做特殊處理吧。
8.單表非唯一索引範圍+附加條件+order by(不按照索引條件)查詢
語句:
select fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and (fi.workstation_type=1or fi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
order by fi.capture_time desc limit 0,20;
執行計劃:
Extra多了個Using filesort就是要排序的意思
執行圖:
Using Filesort爲True,意思是沒法索引自然排序,必須要把數據全部查出來,然後再排序。
執行結果:
這個耗時跟上面的不存在條件查詢差不多,因爲兩者都進行了全索引範圍查詢,不然的話不能保證排序是正確的
結論:
排序要慎用,尤其當查詢的數據量大的時候,如果排序不能直接按照索引自然排,則查詢會查所有,然後再排序和limit。
9.單表LIKE索引查詢
語句1:
select fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi
where
police_id like'00003771%'
limit 0,20;
語句2:
select fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time
from gmvcsbase.base_file fi
where
police_id like '%00003771%'
limit 0,20;
執行計劃1:
使用索引index_user_id
執行計劃2:
全表掃描
執行圖1:
滿足索引條件的有3000行數據,部分索引掃描
執行圖2:
全表掃描,無法使用索引
執行結果1:
執行結果2:
結論:
貌似執行結果都蠻快,實際上那是因爲加了limit條件而已。如果,limit條件改動,兩者查詢效率會有十分大的區別。
LIKE 可以使用索引前綴來加快查詢速度,但是是有條件的,就是LIKE條件不能以%開頭,必須是具體的數據。
10.單表聚合(count、sum、avg)查詢
語句1:
select count(*) from gmvcsbase.base_file fiwhere
start_capture_time>='2015-11-01' andstart_capture_time<'2015-11-02'
start_capture_time有加索引
語句2:
select count(*) from gmvcsbase.base_file fiforce index(start_capture_time)where
capture_time>='2015-11-01' andcapture_time<'2015-11-02'
capture_time未加索引,全表掃描
語句3:
Select count(*) from gmvcsbase.base_file fiwhere
capture_time>='2015-11-01' andcapture_time<'2015-11-02'
執行計劃1:
索引掃描
執行計劃2:
全表掃描
執行計劃3:
使用了索引index_user_createtime(police_id,capture_time)
執行圖1:
因爲有個start_capture_time加索引,所以count的時候就可以直接算索引條數即可,而且索引是有序的,這樣就更快了。
執行圖2:
全表掃描,需要掃描3000W條記錄
代價估算非常高
執行圖3:
此次查詢使用了Fullindex Scan全索引掃描
執行結果1:
0.28秒
執行結果2:
24.242秒
執行結果3:
14.4秒
結論:
在進行聚合查詢時,如果聚合查詢的條件能有效使用合適的索引,則能很好地提高查詢效率。
3000W條記錄進行全表掃描耗時24.242秒,如此計算,每秒大概能掃描比對100W條記錄,這是因爲全表掃描是順序讀取數據而非隨機讀取數據,如果是隨機讀取,則耗時不可想象。
語句3走的是聯合索引,而且police_id在前,start_capture_time在後,因此需要進行全索引掃描,效率上比直接索引查詢慢,跟全表掃描的效率其實是同一個量級。
單表查詢總結:
至此,單表查詢就介紹到這裏了,基本概念應該也比較清晰了,下面開始介紹多表聯合查詢。
11.主表left join外表,多對一或一對一關係,主鍵或唯一索引作匹配條件
語句:
select
fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d %H:%i:%s')import_time,
user.name user_name,
dep.name dep_name
from gmvcsbase.base_file fi
left joingmvcsbase.base_department dep ON fi.police_dep = dep.code
left joingmvcsbase.base_user user ON fi.police_id = user.police_id
where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and (fi.workstation_type=1or fi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
order by fi.start_capture_time desc limit0,20;
執行計劃:
執行圖:
Nested loop表示嵌套循環
Unique Key Lookup表示唯一索引查找,因爲dep.code和user.police_id都是各自表中的主鍵,所以查詢時能根據索引直接找到需要的行記錄,而且匹配行數只有一行,速度很快。
執行流程如上圖從上至下依次執行。
首先是base_file表,別名fi,使用索引index_start_capture_time和附加條件查詢出需要的數據。
然後嵌套循環這些查詢出的數據,根據police_code去查找base_department表(別名dep)中相應的部門名稱name。
然後嵌套循環這些數據,根據police_id去查找base_user表(別名user)中的警員名稱name。
最後將查詢出的數據進行排序,由於是按照start_capture_time進行排序,而且在第一步的時候使用了index_start_capture_time,所以在第一步時已經是有序的數據了。
執行結果:
耗時0.016秒
結論:
這種查詢的特點是base_file表爲主表,需要根據base_file表中的某一字段(在外表中爲主鍵或者唯一索引)去外表中獲取對應行的信息。查詢效率很高。
12.主表left join外表,多對一或一對一關係,主鍵或唯一索引作匹配條件,外表有額外條件
語句:
select
fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time,
user.name user_name,
dep.name dep_name
from gmvcsbase.base_file fi
left joingmvcswxs.tmis_filelabel label on fi.sid=label.WJBH
left join gmvcsbase.base_user user ONfi.police_id = user.police_id
left join gmvcsbase.base_department dep ONfi.police_dep = dep.code
where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and (fi.workstation_type=1or fi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
and fi.is_unusual=0 and fi.is_mark =1
and label.BZDM='0301'and label.is_delete=0
order by fi.start_capture_time desc limit0,20;
Base_file表和標註表是1對1的關係。Fi.sid是base_file表的唯一索引,label.WJBH是標註表的唯一索引。where條件中加入了filelabel表中的條件,BZDM標註類別,is_delete=0標註信息未刪除。
執行計劃:
首先查base_file表,然後嵌套循環查出警員名稱,然後嵌套循環查出部門名稱,然後嵌套循環按照and label.BZDM='0301' and label.is_delete=0過濾數據,將滿足條件的前20條數據返回。
執行圖:
跟執行計劃流程基本吻合。
關鍵點看此圖,這裏有了附加條件,通過附加條件進行數據過濾,只有滿足條件的數據纔是我們需要的數據。
顯然這裏的order by也是自然就排好了的。
執行結果:
耗時0.265秒
結論:
很簡單的理解,從base_file表找出了20條數據(按照start_capture_time已排序),遍歷這20條數據,根據當前條數據的fi.police_id去user表查找user.name,相當於如下語句:
Select user.name from user whereuser.police_id=fi.police_id
將查找到的user.name加入該條數據中。
。。。。。。dep表同理。
關於label表也類似,相當於如下語句:
Select 1 from label where label.WJBH =fi.sid andlabel.BZDM='0301' and label.is_delete=0
此處fi.sid有具體的值,是常數,如果返回1,則表示這條記錄滿足條件,如果沒返回1,則不滿足條件,此條記錄被過濾掉。
注意:假如label.BZDM=’某實際不存在的值’,結果應該能夠理解。
假如order bycapture_time,結果也應該能夠理解。無法自然排序的limit是在全部數據查出之後。
13.主表left join外表,多對多或一對多關係,外表附加條件
語句:
select
fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time,
user.name user_name,
dep.name dep_name
from gmvcsbase.base_file fi
left joingmvcswxs.tmis_match gl on fi.sid=gl.WJBH
left join gmvcsbase.base_user user ONfi.police_id = user.police_id
left join gmvcsbase.base_department dep ONfi.police_dep = dep.code
where
fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and fi.is_relation =1
and (fi.workstation_type=1 orfi.workstation_type=0 or fi.workstation_type=101)
and fi.police_depin('440100-00010','440101-00064','440101-00331','440101-00332')
and gl.SJLX='PECC'and gl.is_delete=0
group by fi.id
order by fi.start_capture_time desc limit0,20
這裏由於是1對多關係,1條base_file表記錄在tmis_match表中有對應多條記錄。
根據left join規則,聯表後,需要group by fi.id才能去除fi表重複的記錄。
執行計劃:
執行圖:
注意gl表的流程,Non-Unique Key Lookup非唯一索引檢索,因爲是1對多關係,gl表中的WJBH不能成爲唯一索引。
注意group的下面有個tmp table,說明gl表聯表查詢後形成了臨時表,然後對這個臨時表進行了group by fi.id操作,最後,將group by好的數據再按照start_capture_time進行排序。
注意,Low ifnumber of matching rows is small,higher as the number of rows increases。
使用了臨時表,沒有排序。
最後order by時,需要排序。
執行結果:
耗時2.262秒,好像還行,可以接受,但這是因爲fi表中已經過濾了大部分數據,如果fi表中沒法過濾大部分數據,那查詢效率不敢想象。
結論:
一對多,多對多是不好優化的,最好在起始表就過濾大量數據,不然後續的臨時表會非常大,對臨時表進行分組,然後排序也會消耗大量資源(內存,時間)。
14.嘗試使用exists進行13的優化
語句:
select
fi.id,fi.sid,fi.namefile_name,fi.police_id,fi.police_dep,fi.type,fi.quality,fi.duration,
DATE_FORMAT(fi.start_capture_time,'%Y-%m-%d%H:%i:%s') capture_time,
DATE_FORMAT(fi.import_time,'%Y-%m-%d%H:%i:%s') import_time,
user.name user_name,
dep.name dep_name
from gmvcsbase.base_file fi
left join gmvcsbase.base_user user ONfi.police_id = user.police_id
left join gmvcsbase.base_department dep ONfi.police_dep = dep.code
where fi.start_capture_time>='2015-10-0100:00:00' and fi.start_capture_time<'2015-10-02'
and fi.status=0 and fi.is_relation =1
and (fi.workstation_type=1 orfi.workstation_type=0 or fi.workstation_type=101)
and fi.police_dep in('440100-00010','440101-00064','440101-00331','440101-00332')
and exists(select 1 fromgmvcswxs.tmis_match gl where gl.SJLX='PECC' and gl.is_delete=0 andfi.sid=gl.WJBH)
order by fi.start_capture_time desc limit0,20
想想爲何這裏沒有groupby
執行計劃:
DEPENDENT SUBQUERY獨立子查詢
執行圖:
這張執行圖就比較複雜了。
起始fi表查詢中多了一個附加條件attached condition。
這裏是需要去做判斷,比如說我查到一條fi表記錄,根據這條記錄的sid去tmis_match表查找該sid是否滿足上面條件,如果滿足條件則此記錄保留,如果不滿足則丟棄。
針對gl表的子查詢如上圖所示,其實就是一條語句
select 1 from gmvcswxs.tmis_match gl wheregl.SJLX='PECC' and gl.is_delete=0 and gl.WJBH=fi.sid
這裏的fi.sid是常數。由於走了索引,WJBH有加索引,所以子查詢效率足夠快。
返回1就是exists。可以推知,無需group by也無需order by也沒有臨時表。
執行結果:
0.156秒較上面的查詢快了至少一個數量級。
結論:
一對多,多對多查詢如果只查主表數據,不涉及到外表,則可以用exists來避免臨時表,groupby和排序。