學習 mysql實戰45講 筆記(16~21),用於自己後期複習

16章 “orderby”是怎麼工作的

select city,name,age from t where city='杭州' order by name limit 1000  ;

1.全字段排序

在city字段上創建索引之後,我們用explain命令來看看這個語句的執行情況。

Extra這個字段中的“Using filesort”表示的就是需要排序,MySQL會給每個線程分配一塊內存用於排序,稱爲sort_buffer

  1. 初始化sort_buffer,確定放入name、city、age這三個字段;
  2. 從索引city找到第一個滿足city='杭州’條件的主鍵id,也就是圖中的ID_X;
  3. 到主鍵id索引取出整行,取name、city、age三個字段的值,存入sort_buffer中;
  4. 從索引city取下一個記錄的主鍵id;
  5. 重複步驟3、4直到city的值不滿足查詢條件爲止,對應的主鍵id也就是圖中的ID_Y;
  6. 對sort_buffer中的數據按照字段name做快速排序;
  7. 按照排序結果取前1000行返回給客戶端。

sort_buffer_size,就是MySQL爲排序開闢的內存(sort_buffer)的大小。如果要排序的數據量小於sort_buffer_size,排序就在內存中完成。但如果排序數據量太大,內存放不下,則不得不利用磁盤臨時文件輔助排序(歸併排序)。這裏會把文件分成多個

2.rowid排序

在上面這個算法過程裏面,只對原表的數據讀了一遍,剩下的操作都是在sort_buffer和臨時文件中執行的。但這個算法有一個問題,就是如果查詢要返回的字段很多的話,那麼sort_buffer裏面要放的字段數太多,這樣內存裏能夠同時放下的行數很少,要分成很多個臨時文件,排序的性能會很差。

所以如果單行很大,這個方法效率不夠好。

max_length_for_sort_data,是MySQL中專門控制用於排序的行數據的長度的一個參數。它的意思是,如果單行的長度超過這個值,MySQL就認爲單行太大,要換一個算法。

city、name、age 這三個字段的定義總長度是36,我把max_length_for_sort_data設置爲16

新的算法放入sort_buffer的字段,只有要排序的列(即name字段)和主鍵id。

但這時,排序的結果就因爲少了city和age字段的值,不能直接返回了,整個執行流程就變成如下所示的樣子:

  1. 初始化sort_buffer,確定放入兩個字段,即name和id;
  2. 從索引city找到第一個滿足city='杭州’條件的主鍵id,也就是圖中的ID_X;
  3. 到主鍵id索引取出整行,取name、id這兩個字段,存入sort_buffer中;
  4. 從索引city取下一個記錄的主鍵id;
  5. 重複步驟3、4直到不滿足city='杭州’條件爲止,也就是圖中的ID_Y;
  6. 對sort_buffer中的數據按照字段name進行排序;
  7. 遍歷排序結果,取前1000行,並按照id的值回到原表中取出city、name和age三個字段返回給客戶端。

rowid排序多訪問了一次表t的主鍵索引

3.全字段排序和rowid排序區別

如果MySQL實在是擔心排序內存太小,會影響排序效率,纔會採用rowid排序算法,這樣排序過程中一次可以排序更多行,但是需要再回到原表去取數據。

如果MySQL認爲內存足夠大,會優先選擇全字段排序,把需要的字段都放到sort_buffer中,這樣排序後就會直接從內存裏面返回查詢結果了,不用再回到原表去取數據。

這也就體現了MySQL的一個設計思想:如果內存夠,就要多利用內存,儘量減少磁盤訪問。

對於InnoDB表來說,rowid排序會要求回表多造成磁盤讀,因此不會被優先選擇。

並不是所有的order by語句,都需要排序操作的。從上面分析的執行過程,我們可以看到,MySQL之所以需要生成臨時表,並且在臨時表上做排序操作,其原因是原來的數據都是無序的。

我們可以在這個市民表上創建一個city和name的聯合索引

在這個索引裏面,我們依然可以用樹搜索的方式定位到第一個滿足city='杭州’的記錄,並且額外確保了,接下來按順序取“下一條記錄”的遍歷過程中,只要city的值是杭州,name的值就一定是有序的。

 

17章 臨時表的排序

 

 

18章 條件字段函數操作導致索引失效

案例一:條件字段函數操作

select count(*) from tradelog where month(t_modified)=7;

對索引字段做函數操作,可能會破壞索引值的有序性,因此優化器就決定放棄走樹搜索功能。

key="t_modified"表示的是,使用了t_modified這個索引;測試表數據中插入了10萬行數據,rows=100335,說明這條語句掃描了整個索引的所有值;Extra字段的Using index,表示的是使用了覆蓋索引。

也就是說,由於在t_modified字段加了month()函數操作,導致了全索引掃描。爲了能夠用上索引的快速定位能力,我們就要把SQL語句改成基於字段本身的範圍查詢。

select count(*) from tradelog where
    -> (t_modified >= '2016-7-1' and t_modified<'2016-8-1') or
    -> (t_modified >= '2017-7-1' and t_modified<'2017-8-1') or 
    -> (t_modified >= '2018-7-1' and t_modified<'2018-8-1');

案例二:隱式類型轉換

select * from tradelog where tradeid=110717;

交易編號tradeid這個字段上,本來就有索引,但是explain的結果卻顯示,這條語句需要走全表掃描。你可能也發現了,tradeid的字段類型是varchar(32),而輸入的參數卻是整型,所以需要做類型轉換。

對優化器來說 這個語句會變成

select * from tradelog where CAST(tradid AS signed int) = 110717;

觸發了我們上面說到的規則:對索引字段做函數操作,優化器會放棄走樹搜索功能

數據庫裏面類型這麼多,這種數據類型轉換規則更多,我怎麼判斷會不會發生類型轉換呢?

比如下面 id是一個int類型.我們怎麼判斷會不會隱式轉化成字符串再比較呢

select * from tradelog where id="83126";

這裏有個辦法,.就是我們直接在數據庫中執行 10>"9" 如果結果是1代表不會轉化,如果是0代表會轉化成字符串

因爲數據庫中"10">"9"是返回0 代表字符串中9比10大.同理上面的tradeid=110717 我們就利用"10">9結果是1代表是拿整形在比較,所以會轉化.

案例三:隱式字符編碼轉換

表的字符集設置不一致,很有可能導致優化器隱式加入字符編碼轉化的函數,導致本來有索引的字段不走索引.

1.修改字符集保持一致

2.如果數據量比較大,不方便做DDL操作,那就在參數上面加函數.

select d.* from tradelog l , trade_detail d where d.tradeid=CONVERT(l.tradeid USING utf8) and l.id=2;

索引字段不能進行函數操作,但是索引字段的參數可以用函數

 

第19章 爲什麼只查一行的語句,也執行這麼慢

執行“查一行”,可能會出現的被鎖住和執行慢的例子。這其中涉及到了表鎖、行鎖和一致性讀的概念

第一類:查詢長時間不返回

select * from t where id=1;

查詢結果長時間不返回。

一般碰到這種情況的話,大概率是表t被鎖住了。接下來分析原因的時候,一般都是首先執行一下show processlist命令,看看當前語句處於什麼狀態。

然後我們再針對每種狀態,去分析它們產生的原因、如何復現,以及如何處理。

 

第20章 幻讀 間隙鎖

這裏通過一個幻讀列子引出間隙鎖,建議複習看原文

  1. 在可重複讀隔離級別下,普通的查詢是快照讀,是不會看到別的事務插入的數據的。因此,幻讀在“當前讀”下才會出現。
  2. 上面session B的修改結果,被session A之後的select語句用“當前讀”看到,不能稱爲幻讀。幻讀僅專指“新插入的行”。

產生幻讀的原因是,行鎖只能鎖住行,但是新插入記錄這個動作,要更新的是記錄之間的“間隙”。因此,爲了解決幻讀問題,InnoDB只好引入新的鎖,也就是間隙鎖(Gap Lock)。

跟間隙鎖存在衝突關係的,是“往這個間隙中插入一個記錄”這個操作。間隙鎖之間都不存在衝突關係。

 

第21章 說說話這兩章被鎖搞的有點暈乎...後面得回頭看,先跳過

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章