了解 MySQL的數據行、行溢出機制嗎?


文章公號 首發!連載中!關注微信公號回復:“抽獎” 還可參加抽📖活動

在前面的文章中,白日夢曾不止一次的提及到:InnoDB從磁盤中讀取數據的最小單位是數據頁。

而你想得到的id = xxx的數據,就是這個數據頁眾多行中的一行。

這篇文章我們就一起來看一下數據行設計的多么巧妙。


一、行 有哪些格式?

你可以像下面這樣看一下你的MySQL行格式設置。

其實MySQL的數據行有兩種格式,一種就是圖中的 Compact格式,還有一種是Redundant格式。

Compact是一種緊湊的行格式,設計的初衷就是為了讓一個數據頁中可以存放更多的數據行。

你品一品,讓一個數據頁中可以存放更多的數據行是一個多么激動人心的事,MySQL以數據頁為單位從磁盤中讀數據,如果能做到讓一個數據頁中有更多的行,那豈不是使用的空間變少了,且整體的效率直線飆升?

官網介紹:Compact能比Redundant格式節約20%的存儲。

Compact從MySQL5.0引入,MySQL5.1之后,行格式默認設置成 Compact 。所以本文描述的也是Compact格式。


二、緊湊的行格式長啥樣?

你肯定曉得表中有的列允許為null,有的列是變長的varchar類型。

那Compact行格式是如何組織描述這些信息的呢?如下圖:

img

每部分包含的數據可能要比我上面標注的1、2、3還要多。

為了給大家更直觀的感受和理解我只是挑了一部分展示給大家看。


三、MySQL單行能存多大體量的數據?

在MySQL的設定中,單行數據最大能存儲65535byte的數據(注意是byte,而不是字符)

但是當你像下面這樣創建一張數據表時卻發生了錯誤:

MySQL不允許創建一個長度為65535byte的列,因為數據頁中每一行中都有我們上圖提到的隱藏列。

所以將varchar的長度降低到65532byte即可成功創建該表

注意這里的65535指的是字節,而不是字符。

所以如果你將charset換成utf8這種編碼格式,那varchar(N)中的N其實指的N個字符,而不是N個byte。所以如果你像下面這樣創建表就會報錯。

假如encode=utf8時三個byte表示一個字符。那么65535 / 3 = 21845個字符。


四、Compact格式是如何做到緊湊的?

MySQL每次進行隨機的IO讀

默認情況下,數據頁的大小為16KB。數據頁中存儲着數行。

那就意味着一個數據頁中能存儲越多的數據行,MySQL整體的進行的IO次數就越少?性能就越快?

Compact格式的實現思路是:當列的類型為VARCHAR、 VARBINARY、 BLOB、TEXT時,該列超過768byte的數據放到其他數據頁中去。

如下圖:

看到這里來龍去脈是不是很清晰了呢?

MySQL這樣做,有效的防止了單個varchar列或者Text列太大導致單個數據頁中存放的行記錄過少而讓IO飆升的窘境且占內存的。


五、什么是行溢出?

那什么是行溢出呢?

如果數據頁默認大小為16KB,換算成byte: 16*1024 = 16384 byte

那你有沒有發現,單頁能存儲的16384byte和單行最大能存儲的 65535byte 差了好幾倍呢?

也就是說,假如你要存儲的數據行很大超過了65532byte那么你是寫入不進去的。假如你要存儲的單行數據小於65535byte但是大於16384byte,這時你可以成功insert,但是一個數據頁又存儲不了你插入的數據。這時肯定會行溢出!

其實在MySQL的設定中,發生行溢出並不是達到16384byte邊緣才會發生。

對於varchar、text等類型的行。當這種列存儲的長度達到幾百byte時就會發生行溢。


六、行 如何溢出?

還是看這張圖:

在MySQL設定中,當varchar列長度達到768byte后,會將該列的前768byte當作當作prefix存放在行中,多出來的數據溢出存放到溢出頁中,然后通過一個偏移量指針將兩者關聯起來,這就是行溢出機制。


七、思考一個問題

不知道你有沒有想過這樣一個問題:

首先你肯定知道,MySQL使用的是B+Tree的聚簇索引,在這棵B+Tree中非葉子節點是只存索引不存數據,葉子節點中存儲着真實的數據。同時葉子結點指向數據頁。

那當單行存不下的時候,為啥不存儲在兩個數據頁中呢?就像下圖這樣~。

單個節點存儲下,我用多個節點存總行吧!說不定這樣我的B+Tee還能變大長高(這其實是錯誤的想法)

這個錯誤的描述對應的腦圖如下:

那MySQL不這樣做的原因如下:

MySQL想讓一個數據頁中能存放更多的數據行,至少也得要存放兩行數據。否則就失去了B+Tree的意義。B+Tree也退化成一個低效的鏈表。

你可以品一下這句藍色的話,他說的每個數據頁至少要存放兩行數據的意思不是說 數據頁不能只存一行。你確確實實可以只往里面寫一行數據,然后去吃個飯,干點別的。一直讓這個數據頁中只有一行數據。

這句話的意思是,當你往這個數據頁中寫入一行數據時,即使它很大將達到了數據頁的極限,但是通過行溢出機制。依然能保證你的下一條數據還能寫入到這個數據頁中。

正確的腦圖如下:


參考:

https://dev.mysql.com/doc/refman/5.7/en/innodb-row-format.html

https://dev.mysql.com/doc/refman/5.7/en/column-count-limit.html


推薦閱讀

  1. 大家常說的基數是什么?(已發布)
  2. 講講什么是慢查!如何監控?如何排查?(已發布)
  3. 對NotNull字段插入Null值有啥現象?(已發布)
  4. 能談談 date、datetime、time、timestamp、year的區別嗎?(已發布)
  5. 了解數據庫的查詢緩存和BufferPool嗎?談談看!(已發布)
  6. 你知道數據庫緩沖池中的LRU-List嗎?(已發布)
  7. 談談數據庫緩沖池中的Free-List?(已發布)
  8. 談談數據庫緩沖池中的Flush-List?(已發布)
  9. 了解臟頁刷回磁盤的時機嗎?(已發布)
  10. 用十一張圖講清楚,當你CRUD時BufferPool中發生了什么!以及BufferPool的優化!(已發布)
  11. 聽說過表空間沒?什么是表空間?什么是數據表?(已發布)
  12. 談談MySQL的:數據區、數據段、數據頁、數據頁究竟長什么樣?了解數據頁分裂嗎?談談看!(已發布)
  13. 談談MySQL的行記錄是什么?長啥樣?(已發布)
  14. 了解MySQL的行溢出機制嗎?(已發布)
  15. 說說fsync這個系統調用吧! (已發布)
  16. 簡述undo log、truncate、以及undo log如何幫你回滾事物! (已發布)
  17. 我勸!這位年輕人不講MVCC,耗子尾汁! (已發布)
  18. MySQL的崩潰恢復到底是怎么回事? (已發布)
  19. MySQL的binlog有啥用?誰寫的?在哪里?怎么配置 (已發布)
  20. MySQL的bin log的寫入機制 (已發布)


面試官都關注了!你還在猶豫什么呢?


免責聲明!

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



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