上一篇,咱們介紹了《DB——數據的讀取和存儲方式》,這篇聊聊sql優化器的工做。程序員
關係型數據庫的一大優點之一,用戶無需關心數據的訪問方式,由於這些優化器都幫咱們處理好了,但sql查詢優化的時候,我不得不要對此進行關注,由於這牽扯到查詢性能問題。sql
有經驗的程序員都會對一些sql優化了如指掌,好比咱們常說的最左匹配原則,非BT謂詞規避等等,那麼優化器是如何肯定這些的?以及爲什麼必定要最左匹配,最左匹配的原理是什麼,你是否有深刻了解?數據庫
這一篇咱們就經過一些實例來剖析優化器作了哪些工做,以方便咱們更好的優化SQL查詢。數組
本篇你能夠知道:緩存
sql的訪問路徑是什麼函數
優化器如何肯定最優訪問路徑性能
最左匹配的原則依據是什麼fetch
如何有效的評估sql命中行數優化
示例table:spa
CREATE TABLE test ( id int(11) NOT NULL AUTO_INCREMENT, user_name varchar(100) DEFAULT NULL, sex int(11) DEFAULT NULL, age int(11) DEFAULT NULL, c_date datetime DEFAULT NULL, PRIMARY KEY (id), # 索引 KEY id_name_sex (id,user_name,sex), KEY name_sex_age (user_name,sex,age) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
在SQL語句可以被真正執行以前,優化器必須首先肯定如何訪問數據。這包括:應該使用哪個索引,索引的訪問方式如何,是否須要輔助式隨機讀,等等。
從一條SQL,到優化器優化,再到引擎進行數據查詢,落地到數據的存儲頁面,這是一個訪問路徑肯定的過程。
謂詞就是咱們常說的where子句中的一個或多個搜索參數組成。謂詞表達式是索引設計的主要入手點,若是一個索引可以知足select查詢語句的全部謂詞表達式,那麼優化器就可能創建一個高效的訪問路徑。
select * from test where id =1 and user_name like ’test%’
好比,上述查詢 中,where後面的搜索參數,id 和user_name 就是謂詞。
索引片即表明謂詞表達式所肯定的值域範圍,而訪問路徑的成本很大程度上取決於索引片的厚度。
索引片越厚,須要掃描的索引頁就越多,須要處理的索引記錄也越多,並且最大的開銷仍是來自於須要對標進行同步讀操做。相反,索引片比較窄,就會顯著減小索引訪問的那部分開銷,同時會有更少的表同步讀取上。
同步讀是一個隨機IO操做,單次的讀取就要耗費10ms左右的時間。這個咱們在上篇有說明。
好比:
//會匹配到5個數據 sql1:select * from test where sex=1; // 匹配到2個數據 sql2:select * from test where sex=1 and age <10;
所以咱們須要經過謂詞來肯定索引片的厚度,過濾的值域範圍越少,索引片厚度就越窄。那麼謂詞必定就能匹配到索引麼,或者說匹配的規則是什麼?
謂詞不必定都能匹配到索引,可以匹配上的,咱們稱之爲匹配列。此時它能夠參與索引片的定義。
只有匹配列和過濾列能夠參與索引片的定義和過濾,其餘不可。
咱們來看下謂詞匹配的定義:
檢查索引列,從頭至尾依次檢查索引列,查看如下規則:
在where子句中,該列是否至少擁有一個足夠簡單的謂詞與之對應?若是有,則這個列就是匹配列。若是沒有,那麼這個列及其後面的索引列都是非匹配列。
謂詞是不是一個範圍謂詞,若是是,那麼剩餘的索引列都是非匹配列。
對於最後一個匹配列以後的索引列,若是擁有一個足夠簡單的謂詞與其對應,那麼該列爲過濾列。
select * from test where user_name=’test1’ and sex>0 and age =10
發現索引id_name_sex
逐行檢查其索引列(
id
,user_name
,sex
)首先檢查
id
,發現where後面的謂詞沒有與之對應,則 這個索引列以及後面的索引列都是非匹配列索引
id_name_sex
匹配結束,無匹配列
發現索引name_sex_age
逐行檢查其索引列(
user_name
,sex
,age
)首先檢查
user_name
,發現where後面的 謂詞user_name
有與之對應,認定此列爲匹配列檢查索引字段
sex
,發現where後面有謂詞sex
與之對應,認定此列爲匹配列,因爲謂詞sex
是範圍謂詞,則剩餘的索引爲非匹配列。索引列
age
是在最後一個匹配列sex
以後,而又有謂詞age
與之對應,所以此列 爲過濾列,
經過這個示例,咱們最終肯定了:
匹配索引:
name_sex_age
匹配列:
user_name
,sex
過濾列:
age
咱們查看下 explain ,和咱們分析的對應。
肯定匹配列以後咱們能夠知道當前的查詢會用到哪些索引,以及匹配到該索引的哪些列,最終能夠提早鎖定數據的訪問範圍,爲數據的讀取節省讀取壓力。
相對於沒用匹配到索引的查詢,有匹配列的查詢,條件過濾是前置的,而沒有匹配到索引的查詢,條件過濾是後置的,即全表掃描以後,再過濾結果,如此磁盤IO壓力過大。
另外 「最左匹配」原則也是基於匹配列規則而來,爲什麼是最左匹配,除了B樹的原理以外,還有一個重要的緣由,在覈對匹配列的時候,是從頭至尾依次檢查索引列。
因此對因而否可以匹配到索引,where後面的謂詞順序不重要,重要的是索引列的順序。
好比:
select * from test where user_name=’test1’ and sex>0 and age =10 select * from test where sex>0 and user_name=’test1’ and age =10 select * from test where age =10 and user_name='test1' and sex>0
均可以匹配到name_sex_age
索引
若是值是%xx ,那麼將會選擇全索引掃描,不參與索引匹配,若是是xx%,這會參與索引匹配,選擇索引片掃描。
即使是簡單的謂詞,若是它們與其餘謂詞之間爲OR操做,對優化器而言是異常困難的,除非在多索引訪問,纔有可能參與到一個索引片的定義,儘可能不要用。
假設一個謂詞的斷定結果爲false,而此時不檢查其餘謂詞就不能肯定的將一行記錄排除在外,那麼這類謂詞對優化器而言就是十分困難的。
好比只有and 操做符,那麼全部的簡單謂詞均可以稱謂BT謂詞,也就是好的謂詞,除非訪問路徑是一個多索引掃描,不然只有BT謂詞能夠參加定義索引片。
好比謂詞的值採用了函數,或者參與了計算,優化器在作靜態SQL綁定的時候,每次都須要從新計算選擇,沒法緩存,耗費大量的CPU,也沒法參與索引列的匹配。
匹配列肯定了使用那些索引列,但索引片的厚度(也就是預計要訪問多少行),尚未估算出來。此處須要進行經過過濾因子來肯定。
過濾因子描述的謂詞的選擇性,即表中知足謂詞條件的記錄行數所佔用的比例,依賴於列值分佈狀況。
好比,咱們的的test表有10000條記錄,謂詞user_name 匹配了 一個索引列,其過濾因子是0.2%(1/不一樣user_name數量=user_name中有500個不一樣值的比率),則意味着查詢結果會包含20行的記錄。
select * from test where user_name=’test’
當有多個謂詞符合匹配列的時候,咱們能夠經過單個謂詞的過濾因子推導出組合過濾因子。通常的公式是:
組合過濾因子=謂詞1過濾因子*謂詞2過濾因子....
好比以下查詢
select * from test where user_name=’test’ and sex=1 and age =10
包含3個謂詞,user_name、sex、age、其中user_name有500個不一樣的值,sex有2個不一樣的值,age有40個不一樣的值。
則每一個謂詞的過濾因子:
FF(user_name) =1/500*100 =0.2%
FF(sex) =1/2*100=50%
FF(age) =1/40*100=2.5%
組合過濾因子=0.2%*50%*2.5%=0.0025%
經過以上組合過濾因子,能夠推算出最終的結果集=10000*0.0025%=0.25 ~=1
經過以上過濾因子評估以後,咱們能夠看到,最終須要查找的結果集只須要獲取1行就夠了,這對數據庫的磁盤訪問有很高的性能提高。
這也是優化器在評估可選訪問路徑成時,必須先進行過濾因子評估的重要性。
物化結果集意味着經過執行必要的數據庫訪問來構建結果集。最好狀況下,只須要返回一條記錄,而最壞的狀況下須要返回多條記錄,須要發起大量的磁盤讀取。而排序就是其中一種。
在如下狀況中,一次fetch調用只須要物化一條記錄,不然對結果進行排序的時候就須要物化整個結果集。
沒有排序需求,好比order by,group by 等。
雖然須要排序知足如下兩個條件:
<!--存在一個索引知足結果集的排序需求,好比上述的(id_name_sex) 或者(name_sex_age)-->
<!--優化器決定以傳統的方式使用這個索引,即訪問第一條知足條件的索引行並讀取相應的錶行,而後訪問第二條知足條件的索引行並讀取相應的錶行,依次類推。-->
<!--好比使用索引(name_sex_age)時候,select * from test where user_name=’test’ order by sex ,此時在索引中,結果集基於sex自己就是有序的-->
sql優化器作的不只僅是你這些工做,但索引片的大小的預估,以及訪問路徑的肯定倒是它最重要的工做,後續咱們再繼續介紹。
-----------------------------------------------------------------------------
想看更多有趣原創的技術文章,掃描關注公衆號。