從網(wǎng)友對(duì) MySQL 手冊(cè)的一個(gè)疑問聊起
在 Go 語(yǔ)言中文網(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。我做了講解,但似乎他還是不太理解。干脆我寫篇文章詳細(xì)講解下,希望對(duì)不清楚的人能有所幫助,答案不是重點(diǎn),關(guān)鍵在于分析的過程。
01
講一個(gè)真實(shí)的事情。幾年前,某大廠的一個(gè) iOS 開發(fā),說(shuō)之前接觸過 PHP,了解一些后端,于是簡(jiǎn)單聊了聊(半面試形式)。聊到的一些點(diǎn),讓我感覺他的基礎(chǔ)很薄弱,于是我試探性的問:int 類型一般占多大空間?(這樣的問題我一般不會(huì)問,以免讓人感覺“鄙視”他)
他回答:32。我就知道他對(duì)這塊稀里糊涂的。于是追問一句:?jiǎn)挝皇鞘裁?他不確定的答道:是字節(jié)吧?!
回到 MySQL 中的問題,DATE 用 3 字節(jié)的整數(shù)類型存儲(chǔ),怎么存?如果不考慮空間,DATE 類型最簡(jiǎn)單的存儲(chǔ)方式可能是,直接將 YYYYMMDD 當(dāng)做整數(shù)存儲(chǔ),比如:20210128。這個(gè)數(shù)是否可以用 3 個(gè)字節(jié)存儲(chǔ)呢?
1 個(gè)字節(jié)(byte)是 8 位(bit),3 個(gè)字節(jié)有 24 位,如果表示無(wú)符號(hào)整數(shù),最大能表示 (2 << 24) - 1(移位操作優(yōu)先級(jí)低于減法),即 16777215。很顯然它比 20210128 小,因此我們不能直接使用 YYYYMMDD 當(dāng)做整數(shù)存儲(chǔ)。
如果直接把 YYYYMMDD 形式當(dāng)做整數(shù),中間會(huì)有很多“空洞”,也就是很多數(shù)字沒有用到,是不連續(xù)的,空間利用率太低。比如下圖,20210101 比它頭一天 20201231 大 8870。
因此,我們可以采用“壓縮”的方式,讓日期集合更“緊湊”。MySQL 這里采用的方法就是。具體為什么是 YYYY×16×32 + MM×32 + DD 呢?
02
日期 DD 的范圍是:1~31,用 5 個(gè)位就可以表示。月份的范圍是:1~12,用 4 個(gè)位就可以表示,但前面 5 個(gè)位被 DD 占用了,因此 MM 得從第 6 位開始,所以需要左移(<<) 5 位,即乘以 32,所以 MM×32 是這么來(lái)的。這樣一來(lái),MM 和 DD 一共占據(jù)了 5 + 4 = 9 位,3 個(gè)字節(jié)(24 位)還剩 15 位,也就是說(shuō)有 15 位可以存放年(YYYY),15 位能表示到 32767 年,遠(yuǎn)超 2021 年。因此,對(duì)于 YYYY 來(lái)說(shuō),需要左移 5 + 4 位,也就是 YYYY×32×16。
至于 TIME,因?yàn)橛妹氡硎镜闹悼梢源嫒?3 個(gè)字節(jié),因此直接將 TIME 轉(zhuǎn)為秒。即 DD×24×3600 + HH×3600 + MM×60 + SS
而 DATETIME 使用了 8 個(gè)字節(jié)存儲(chǔ),4 個(gè)字節(jié)存日期,4 個(gè)字節(jié)存時(shí)間。因?yàn)榭臻g比較充足,這里的“壓縮”沒有采用而二進(jìn)制位,而是直接用十進(jìn)制的方式。YYYY×10000 + MM×100 + DD,年月日不會(huì)重疊,而且沒有超過 4 字節(jié)能表示的范圍,時(shí)間一樣的道理。
本文轉(zhuǎn)載自微信公眾號(hào)「 polarisxu」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 polarisxu公眾號(hào)


























