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