MySQL實戰45講(16--20)-筆記
- 16 | “order by”是怎麼工作的?
- 17 | 如何正確地顯示隨機訊息?
- 18 | 為什麼這些SQL語句邏輯相同,效能卻差異巨大?
- 19 | 為什麼我只查一行的語句,也執行這麼慢?
- 20 | 幻讀是什麼,幻讀有什麼問題?
筆記做的不好,因為還是又不少地方沒有能夠理解。見諒,後面理解了在更新…………
16 | “order by”是怎麼工作的?
場景:
CREATE TABLE `t` ( `id` int(11) NOT NULL, `city` int(11) NOT NULL, `name` varchar(16) NOT NULL, `age` int(11) NOT NULL, `addr` varchar(128) DEFAULT NULL, PRIMARY KEY (`id`), KEY `city` (`city`) ) ENGINE=InnoDB; select city,name,age from t where city='杭州' order by name limit 1000 ;
全欄位排序
Extra 這個欄位中的“Using filesort”表示的就是需要排序,MySQL 會給每個執行緒分配一塊記憶體用於排序,稱為 sort_buffer。
圖中“按 name 排序”這個動作,可能在記憶體中完成,也可能需要使用外部排序,這取決於排序所需的記憶體和引數 sort_buffer_size。
可以用這個語句來確定一個排序語句是否使用了臨時檔案:
/* 開啟 optimizer_trace,只對本執行緒有效 */ SET optimizer_trace='enabled=on'; /* @a 儲存 Innodb_rows_read 的初始值 */ select VARIABLE_VALUE into @a from performance_schema.session_status where variable_name = 'Inndb' /* 執行語句 */ select city, name,age from t where city='杭州' order by name limit 1000; /* 檢視 OPTIMIZER_TRACE 輸出 */ SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G /* @b 儲存 Innodb_rows_read 的當前值 */ select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb' /* 計算 Innodb_rows_read 差值 */ select @b-@a number_of_tmp_files 表示的是,排序過程中使用的臨時檔案數。
MySQL會將需要排序的檔案分割成多份,小份單獨排序後合一。(sort_buffer_size 越小,需要分成的份數越多,
number_of_tmp_files 的值就越大。)
rowid 排序
MySQL 認為排序的單行長度太大 會使用 這個引數 max_length_for_sort_data 來提示MySQL要換一個演算法。
過程:因為多了一次索引,排序使用的欄位少了,所以資料量就下來了,相應的所需要的臨時檔案也變少了。
17 | 如何正確地顯示隨機訊息?
場景:
英語學習App 每天 根據使用者的級別推薦 隨機的單詞 隨著單詞表的變大,這個操作越來越慢 mysql> CREATE TABLE `words` ( `id` int(11) NOT NULL AUTO_INCREMENT, `word` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB;
記憶體臨時表
order by rand() 來實現這個邏輯。
mysql> select word from words order by rand() limit 3;
一個結論:對於 InnoDB 表來說,執行全欄位排序會減少磁碟訪問,因此會被優先選擇。
如果你建立的表沒有主鍵,或者把一個表的主鍵刪掉了,那麼 InnoDB 會自己生成一個長度為 6 位元組的 rowid 來作為主鍵。
這也就是排序模式裡面,rowid 名字的來歷。
實際上它表示的是:每個引擎用來唯一標識資料行的資訊。
- 對於有主鍵的 InnoDB 表來說,這個 rowid 就是主鍵 ID;
- 對於沒有主鍵的 InnoDB 表來說,這個 rowid 就是由系統生成的;
- MEMORY 引擎不是索引組織表。
在這個例子裡面,你可以認為它就是一個數組。因此,這個 rowid 其實就是陣列的下標。
order by rand() 使用了記憶體臨時表,記憶體臨時表排序的時候使用了 rowid 排序方法。
磁碟臨時表
不是所有的臨時表都是記憶體表。
tmp_table_size 這個配置限制了記憶體臨時表的大小,預設值是 16M。如果臨時表大小超過了 tmp_table_size,那麼記憶體臨時表就會轉成磁碟臨時表。
磁碟臨時表使用的引擎預設是 InnoDB,是由引數 internal_tmp_disk_storage_engine 控制的。
當使用磁碟臨時表的時候,對應的就是一個沒有顯式索引的 InnoDB 表的排序過程。
MySQL 5.6 版本引入的一個新的排序演算法,即:優先佇列排序演算法
但是如果超過了我設定的 sort_buffer_size 大小,所以只能使用歸併排序演算法,因為要維護的堆太大了。
隨機排序方法
mysql> select count(*) into @C from t;
set @Y1 = floor(@C * rand());
set @Y2 = floor(@C * rand());
set @Y3 = floor(@C * rand());
select * from t limit @Y1,1; // 在應用程式碼裡面取 Y1、Y2、Y3 值,拼出 SQL 後執行
select * from t limit @Y2,1;
select * from t limit @Y3,1;
18 | 為什麼這些SQL語句邏輯相同,效能卻差異巨大?
在 MySQL 中,有很多看上去邏輯相同,但效能卻差異巨大的 SQL 語句。對這些語句使用不當的話,就會不經意間導致整個資料庫的壓力變大。
案例一:條件欄位函式操作
更新中…………
小結
對索引欄位做函式操作,可能會破壞索引值的有序性,因此優化器就決定放棄走樹搜尋功能。
MySQL 的優化器確實有“偷懶”的嫌疑,即使簡單地把 where id+1=1000 改寫成 whereid=1000-1 就能夠用上索引快速查詢,也不會主動做這個語句重寫。
因此,每次你的業務程式碼升級時,把可能出現的、新的 SQL 語句 explain 一下,是一個很好的習慣。
19 | 為什麼我只查一行的語句,也執行這麼慢?
場景:
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
在裡面插入了 10 萬行記錄
第一類:查詢長時間不返回
mysql> select * from t where id=1;
一般碰到這種情況的話,大概率是表 t 被鎖住了。
接下來分析原因的時候,一般都是首先執行一下 show processlist 命令,看看當前語句處於什麼狀態。
然後我們再針對每種狀態,去分析它們產生的原因、如何復現,以及如何處理。
-
等 MDL 鎖
-
等 flush
-
等行鎖
第二類:查詢慢
20 | 幻讀是什麼,幻讀有什麼問題?
https://www.cnblogs.com/zwtblog/p/15134977.html
https://www.cnblogs.com/zwtblog/p/15132201.html#基本語法
幻讀
在同一個事務內,讀取到了別人插入的資料,導致前後讀出來結果不一致
如何解決幻讀?
現在你知道了,產生幻讀的原因是,行鎖只能鎖住行,
但是新插入記錄這個動作,要更新的是記錄之間的“間隙”。因此,為了解決幻讀問題,InnoDB 只好引入新的鎖,也就是間隙鎖 (GapLock)。
顧名思義,間隙鎖,鎖的就是兩個值之間的空隙。
間隙鎖的引入,可能會導致同樣的語句鎖住更大的範圍,這其實是影響了併發度的。