mysql 查詢理解

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,如果鍵不是UNIQUEPRIMARY 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,如果鍵不是UNIQUEPRIMARY 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和排序。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發佈了63 篇原創文章 · 獲贊 28 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章