1. 程式人生 > 其它 >MySQL實戰45講(16--20)-筆記

MySQL實戰45講(16--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)。

顧名思義,間隙鎖,鎖的就是兩個值之間的空隙。

間隙鎖的引入,可能會導致同樣的語句鎖住更大的範圍,這其實是影響了併發度的。