極客時間-MySQL實戰45講(實踐篇)-1


09 | 普通索引和唯一索引,應該怎么選擇?

查詢過程
InnoDB 的數據是按數據頁為單位來讀寫的。也就是說,當需要讀一條記錄的時候,並不是將這個記錄本身從磁盤讀出來,而是以頁為單位,將其整體讀入內存。在 InnoDB 中,每個數據頁的大小默認是 16KB。
更新過程
什么是change buffer?
從MySQL5.5版本開始,Insert buffer更名為change buffer,除了緩沖對二級索引的insert操作,還包括update/delete/后台purge操作,由參數innodb_change_buffering來控制。因此這里統一稱為change buffer
change Buffer和數據頁一樣,也是物理頁的一個組成部分,數據結構也是一顆B+樹
當需要更新一個數據頁時,如果數據頁在內存中就直接更新,而如果這個數據頁還沒有在內存中的話,在不影響數據一致性的前提下,InooDB 會將這些更新操作緩存在 change buffer 中,這樣就不需要從磁盤中讀入這個數據頁了。在下次查詢需要訪問這個數據頁的時候,將數據頁讀入內存,然后執行 change buffer 中與這個頁有關的操作。通過這種方式就能保證這個數據邏輯的正確性。
什么是merge?
將 change buffer 中的操作應用到原數據頁,得到最新結果的過程稱為 merge。除了訪問這個數據頁會觸發 merge 外,系統有后台線程會定期 merge。在數據庫正常關閉(shutdown)的過程中,也會執行 merge 操作。
merge 的執行流程是這樣的:
1.從磁盤讀入數據頁到內存(老版本的數據頁);
2.從 change buffer 里找出這個數據頁的 change buffer 記錄 (可能有多個),依次應用,得到新版數據頁;
3.寫 redo log。這個 redo log 包含了數據的變更和 change buffer 的變更。

什么條件下可以使用 change buffer 呢?
對於唯一索引來說,所有的更新操作都要先判斷這個操作是否違反唯一性約束。比如,要插入 (4,400) 這個記錄,就要先判斷現在表中是否已經存在 k=4 的記錄,而這必須要將數據頁讀入內存才能判斷。如果都已經讀入到內存了,那直接更新內存會更快,就沒必要使用 change buffer 了。
因此,唯一索引的更新就不能使用 change buffer,實際上也只有普通索引可以使用。

普通索引和唯一索引應該怎么選擇。其實,這兩類索引在查詢能力上是沒差別的,主要考慮的是對更新性能的影響。所以,我建議你盡量選擇普通索引。
change buffer 和 redo log?
如果要簡單地對比這兩個機制在提升更新性能上的收益的話,redo log 主要節省的是隨機寫磁盤的 IO 消耗(轉成順序寫),而 change buffer 主要節省的則是隨機讀磁盤的 IO 消耗。

10 | MySQL為什么有時候會選錯索引?

優化器選擇索引的目的,是找到一個最優的執行方案,並用最小的代價去執行語句。在數據庫里面,掃描行數是影響執行代價的因素之一。掃描的行數越少,意味着訪問磁盤數據的次數越少,消耗的 CPU 資源越少。
一個索引上不同的值的個數,我們稱之為“基數”(cardinality)
也就是說,這個基數越大,索引的區分度越好。
MySQL 是怎樣得到索引的基數的呢?---采樣統計
優化器沒有選擇正確的索引,force index 起到了“矯正”的作用

11 | 怎么給字符串字段加索引?

mysql> alter table SUser add index index2(email(6));
使用前綴索引,定義好長度,就可以做到既節省空間,又不用額外增加太多的查詢成本。
其他方式
第一種方式是使用倒序存儲。如果你存儲身份證號的時候把它倒過來存,每次查詢的時候,你可以這么寫:
mysql> select field_list from t where id_card = reverse('input_id_card_string');
由於身份證號的最后 6 位沒有地址碼這樣的重復邏輯,所以最后這 6 位很可能就提供了足夠的區分度。當然了,實踐中你不要忘記使用 count(distinct) 方法去做個驗證。
第二種方式是使用 hash 字段。你可以在表上再創建一個整數字段,來保存身份證的校驗碼,同時在這個字段上創建索引。
mysql> alter table t add id_card_crc int unsigned, add index(id_card_crc);
然后每次插入新記錄的時候,都同時用 crc32() 這個函數得到校驗碼填到這個新字段。由於校驗碼可能存在沖突,也就是說兩個不同的身份證號通過 crc32() 函數得到的結果可能是相同的,所以你的查詢語句 where 部分要判斷 id_card 的值是否精確相同。

12 | 為什么我的MySQL會“抖”一下?

當內存數據頁跟磁盤數據頁內容不一致的時候,我們稱這個內存頁為“臟頁”。內存數據寫入到磁盤后,內存和磁盤上的數據頁的內容就一致了,稱為“干凈頁”。
什么情況會引發數據庫的 flush 過程?
*1.InnoDB 的 redo log 寫滿了
*2.系統內存不足。當需要新的內存頁,而內存不夠用的時候,就要淘汰一些數據頁,空出內存給別的數據頁使用
*3.MySQL 認為系統“空閑”的時候。
InnoDB 刷臟頁的控制策略
一個是臟頁比例,你就要合理地設置 innodb_io_capacity 的值,並且平時要多關注臟頁比例,不要讓它經常接近 75%。
一個是 redo log 寫盤速度。

13 | 為什么表數據刪掉一半,表文件大小不變?

一個 InnoDB 表包含兩部分,即:表結構定義和數據。
表數據既可以存在共享表空間里,也可以是單獨的文件。這個行為是由參數 innodb_file_per_table 控制的。
從 MySQL 5.6.6 版本開始,它的默認值就是 ON 了。
表中的數據被刪除了,但是表空間卻沒有被回收。?
delete 命令其實只是把記錄的位置,或者數據頁標記為了“可復用”,但磁盤文件的大小是不會變的。也就是說,通過 delete 命令是不能回收表空間的。這些可以復用,而沒有被使用的空間,看起來就像是“空洞”
經過大量增刪改的表,都是可能是存在空洞的。所以,如果能夠把這些空洞去掉,就能達到收縮表空間的目的。
而重建表 ---可以釋放表空間。
小結
如果要收縮一個表,只是 delete 掉表里面不用的數據的話,表文件的大小是不會變的,你還要通過 alter table 命令重建表,才能達到表文件變小的目的。我跟你介紹了重建表的兩種實現方式,
Online DDL 的方式是可以考慮在業務低峰期使用的,而 MySQL 5.5 及之前的版本,這個命令是會阻塞 DML 的,這個你需要特別小心。


14 | count(*)這么慢,我該怎么辦?

count(*) 的實現方式

  • MyISAM 引擎把一個表的總行數存在了磁盤上,因此執行 count(*) 的時候會直接返回這個數,效率很高;
  • 而 InnoDB 引擎就麻煩了,它執行 count(*) 的時候,需要把數據一行一行地從引擎里面讀出來,然后累積計數。
    到這里我們小結一下:
  • MyISAM 表雖然 count(*) 很快,但是不支持事務;
  • show table status 命令雖然返回很快,但是不准確;
  • InnoDB 表直接 count(*) 會遍歷全表,雖然結果准確,但會導致性能問題。

不同的 count 用法?

對於 count(主鍵 id) 來說,InnoDB 引擎會遍歷整張表,把每一行的 id 值都取出來,返回給 server 層。server 層拿到 id 后,判斷是不可能為空的,就按行累加。
對於 count(1) 來說,InnoDB 引擎遍歷整張表,但不取值。server 層對於返回的每一行,放一個數字“1”進去,判斷是不可能為空的,按行累加。
單看這兩個用法的差別的話,你能對比出來,count(1) 執行得要比 count(主鍵 id) 快。因為從引擎返回 id 會涉及到解析數據行,以及拷貝字段值的操作。
對於 count(字段) 來說:
如果這個“字段”是定義為 not null 的話,一行行地從記錄里面讀出這個字段,判斷不能為 null,按行累加;
如果這個“字段”定義允許為 null,那么執行的時候,判斷到有可能是 null,還要把值取出來再判斷一下,不是 null 才累加。
也就是前面的第一條原則,server 層要什么字段,InnoDB 就返回什么字段。
但是 count() 是例外,並不會把全部字段取出來,而是專門做了優化,不取值。count() 肯定不是 null,按行累加。
看到這里,你一定會說,優化器就不能自己判斷一下嗎,主鍵 id 肯定非空啊,為什么不能按照 count() 來處理,多么簡單的優化啊。
當然,MySQL 專門針對這個語句進行優化,也不是不可以。但是這種需要專門優化的情況太多了,而且 MySQL 已經優化過 count(
) 了,你直接使用這種用法就可以了。
所以結論是:* 按照效率排序的話,count(字段)<count(主鍵 id)<count(1)≈count(),所以我建議你,盡量使用 count()。*


16 | “order by”是怎么工作的?

全字段排序
MySQL 會給每個線程分配一塊內存用於排序,稱為 sort_buffer。
MySQL 將需要排序的數據分成 12 份,每一份單獨排序后存在這些臨時文件中。然后把這 12 個有序文件再合並成一個有序的大文件
rowid 排序
覆蓋索引是指,索引上的信息足夠滿足查詢請求,不需要再回到主鍵索引上去取數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM