從網友對 MySQL 手冊的一個疑問聊起
在 Go 語言中文網微信群有人提出了這樣的疑問,如下圖(文檔地址:https://dev.mysql.com/doc/internals/en/date-and-time-data-type-representation.html)
不理解為什么 DATE 是通過 YYYY×16×32 + MM×32 + DD 表示,主要不理解為什么 16、32。我做了講解,但似乎他還是不太理解。干脆我寫篇文章詳細講解下,希望對不清楚的人能有所幫助,答案不是重點,關鍵在于分析的過程。
01
講一個真實的事情。幾年前,某大廠的一個 iOS 開發,說之前接觸過 PHP,了解一些后端,于是簡單聊了聊(半面試形式)。聊到的一些點,讓我感覺他的基礎很薄弱,于是我試探性的問:int 類型一般占多大空間?(這樣的問題我一般不會問,以免讓人感覺“鄙視”他)
他回答:32。我就知道他對這塊稀里糊涂的。于是追問一句:單位是什么?他不確定的答道:是字節吧?!
回到 MySQL 中的問題,DATE 用 3 字節的整數類型存儲,怎么存?如果不考慮空間,DATE 類型最簡單的存儲方式可能是,直接將 YYYYMMDD 當做整數存儲,比如:20210128。這個數是否可以用 3 個字節存儲呢?
1 個字節(byte)是 8 位(bit),3 個字節有 24 位,如果表示無符號整數,最大能表示 (2 << 24) - 1(移位操作優先級低于減法),即 16777215。很顯然它比 20210128 小,因此我們不能直接使用 YYYYMMDD 當做整數存儲。
如果直接把 YYYYMMDD 形式當做整數,中間會有很多“空洞”,也就是很多數字沒有用到,是不連續的,空間利用率太低。比如下圖,20210101 比它頭一天 20201231 大 8870。
因此,我們可以采用“壓縮”的方式,讓日期集合更“緊湊”。MySQL 這里采用的方法就是。具體為什么是 YYYY×16×32 + MM×32 + DD 呢?
02
日期 DD 的范圍是:1~31,用 5 個位就可以表示。月份的范圍是:1~12,用 4 個位就可以表示,但前面 5 個位被 DD 占用了,因此 MM 得從第 6 位開始,所以需要左移(<<) 5 位,即乘以 32,所以 MM×32 是這么來的。這樣一來,MM 和 DD 一共占據了 5 + 4 = 9 位,3 個字節(24 位)還剩 15 位,也就是說有 15 位可以存放年(YYYY),15 位能表示到 32767 年,遠超 2021 年。因此,對于 YYYY 來說,需要左移 5 + 4 位,也就是 YYYY×32×16。
至于 TIME,因為用秒表示的值可以存入 3 個字節,因此直接將 TIME 轉為秒。即 DD×24×3600 + HH×3600 + MM×60 + SS
而 DATETIME 使用了 8 個字節存儲,4 個字節存日期,4 個字節存時間。因為空間比較充足,這里的“壓縮”沒有采用而二進制位,而是直接用十進制的方式。YYYY×10000 + MM×100 + DD,年月日不會重疊,而且沒有超過 4 字節能表示的范圍,時間一樣的道理。
本文轉載自微信公眾號「 polarisxu」,可以通過以下二維碼關注。轉載本文請聯系 polarisxu公眾號