MySQL 保存日期,用哪種數(shù)據(jù)類型合適?datetime?timestamp?還是 int?
日期算是我們在日常開發(fā)中經(jīng)常用到的數(shù)據(jù)類型,一般來說一張表都有 createTime 和 updateTime 字段,MySQL 中針對日期也提供了很多種不同的數(shù)據(jù)類型,如:
- datetime
- timestamp
- int
等等。甚至也有人直接將日期存為字符串的。
那么到底該用哪種類型來保存日期呢?
1. 字符串
在這些類型中,首先應(yīng)該排除掉的就是字符串了,很多新手小伙伴愛用字符串存儲日期,但實(shí)際上這并不是一個(gè)很好的方案。
使用字符串存儲日期,第一個(gè)顯而易見的問題就是無法使用 MySQL 中提供的日期函數(shù),這會(huì)為很多查詢帶來不便。
例如用戶表中有一個(gè)字段 birthday,表示用戶的生日,現(xiàn)在想要查詢 2001 出生的所有用戶,如果 birthday 是 日期類型,就可以使用 YEAR 函數(shù),但是如果 birthday 是字符串類型,這個(gè)問題就不太好處理了。
使用字符串存儲日期的第二個(gè)問題就是占用空間較大,例如存儲如下時(shí)間:
2021-01-01 00:00:00
- 如果使用字符串,需要 19 個(gè)字節(jié)。
- 如果使用 datetime 需要 8 個(gè)字節(jié)。
- 如果使用 timestamp 需要 4 個(gè)字節(jié)。
所以首先排除掉字符串。
2. DATETIME VS TIMESTAMEP
2.1 占用空間
DATETIME 在數(shù)據(jù)庫中存儲的形式為:YYYY-MM-DD hh:mm:ss,至于占用的字節(jié)數(shù),則看情況,我們來看一段來自 MySQL 官網(wǎng)的內(nèi)容:
可以看到,MySQL5.6.4 是一個(gè)分水嶺:
- 在 MySQL5.6.4 之前,DATETIME 固定占用 8 個(gè)字節(jié)。
- 從 MySQL5.6.4 開始,DATETIME 類型開始支持毫秒,DATETIME(N) 中的 N 表示毫秒的精度,例如,DATETIME(6) 表示可以存儲 6 位的毫秒值,那么此時(shí),DATETIME 占用的字節(jié)數(shù),就跟后面的毫秒數(shù)有關(guān)了,如果 DATETIME 沒有詳細(xì)到毫秒,那么占用 5 個(gè)字節(jié),如果詳細(xì)到毫秒了,那就看情況,根據(jù)毫秒的精度,占用不同的空間,毫秒精度小于等于 2 時(shí),總共占用 6 個(gè)字節(jié);毫秒精度小于等于 4 時(shí),總共占用 7 個(gè)字節(jié);毫秒精度小于等于 6 時(shí),總共占用 8 個(gè)字節(jié)。
同樣,由上圖我們也可以看出,在 MySQL5.6.4 之前,TIMESTAMEP 固定占用 4 個(gè)字節(jié),從 MySQL5.6.4 開始,依據(jù)毫秒的精度,TIMESTAMEP 占用的字節(jié)數(shù)介于 4 到 7 之間。
所以無論是 TIMESTAMEP,還是 DATETIME,都是比字符串節(jié)省空間的。
2.2 存儲范圍
DATETIME 的存儲范圍介于 1000-01-01 00:00:00 到 9999-12-31 23:59:59 之間。
TIMESTAMP 的存儲范圍則介于 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC 之間。
很明顯 DATETIME 的存儲范圍要更大一些。
2.3 底層存儲TIMESTAMP 類型最大的優(yōu)勢在于自帶時(shí)區(qū)屬性,因?yàn)樗举|(zhì)上是從毫秒轉(zhuǎn)化而來。如果你的業(yè)務(wù)需要對應(yīng)不同的國家時(shí)區(qū),那么類型 TIMESTAMP 是一種不錯(cuò)的選擇,TIMESTAMP 類型字段的值會(huì)隨著服務(wù)器時(shí)區(qū)的變化而變化,自動(dòng)換算成相應(yīng)的時(shí)間,說簡單點(diǎn)就是在不同時(shí)區(qū),查詢到同一個(gè)條記錄此字段的值會(huì)不一樣。
舉個(gè) TIMESTAMP 的使用場景例子:
新聞?lì)惖臉I(yè)務(wù),通常用戶想知道這篇新聞發(fā)布時(shí)對應(yīng)的自己國家時(shí)間,那么 TIMESTAMP 是一種不錯(cuò)的選擇。
TIMESTAMP 會(huì)隨著時(shí)區(qū)的變化而自動(dòng)調(diào)整,而 DATETIME 不會(huì)。
我舉個(gè)例子:假設(shè)我數(shù)據(jù)庫目前的時(shí)區(qū)是 Asia/Shanghai:
現(xiàn)在有一個(gè) user 表,數(shù)據(jù)如下:
其中,createTime 字段是 DATETIME,而 updateTime 是 TIMESTAMP,現(xiàn)在我修改一下數(shù)據(jù)庫時(shí)區(qū),我們再來查看:
小伙伴們可以看到,我把時(shí)區(qū)設(shè)置為東京,東京比我們快一個(gè)小時(shí),此時(shí) updateTime 自動(dòng)變了,而 DATETIME 不變。
時(shí)區(qū)的問題一定要謹(jǐn)慎,不過時(shí)區(qū)問題也并非一定要在數(shù)據(jù)庫中解決,也可以在前端或者服務(wù)端用代碼處理下。
2.4 性能比較
從毫秒數(shù)轉(zhuǎn)換到 TIMESTAMP 并不費(fèi)事,但是當(dāng)要進(jìn)行時(shí)區(qū)轉(zhuǎn)換的時(shí)候,需要調(diào)用操作系統(tǒng)底層系統(tǒng)函數(shù),而這個(gè)函數(shù)需要額外的加鎖操作,以確保這時(shí)操作系統(tǒng)時(shí)區(qū)沒有修改,一加鎖,效率就低了。
對于這個(gè)問題,只存在于 TIMESTAMP 中,因?yàn)?DATETIME 不存在時(shí)區(qū)轉(zhuǎn)化問題。
對于 TIMESTAMP,建議使用顯式的時(shí)區(qū),而不是操作系統(tǒng)時(shí)區(qū)。
3. int
字符串費(fèi)空間,TIMESTAMP 和 DATETIME 如果沒有吃透則總感覺亂亂的,所以也有人存時(shí)間戳,存一個(gè) int 類型的數(shù)值,用一個(gè)時(shí)間戳來表示時(shí)間。
用 int 保存時(shí)間的話,當(dāng)我們需要進(jìn)行日期排序以及按照日期范圍查詢的時(shí)候,就變成了普通的數(shù)字比較了,那么效率肯定是杠杠滴。
不過 int 有一個(gè)致命的問題就是可讀性太差,所以用不用 int 就要仔細(xì)斟酌看情況了。
好啦,小伙伴們留言說說你日常開發(fā)日期用的哪種類型呢?出于什么樣的考慮用了該類型?