日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
警告!別再使用TIMESTAMP作為日期字段

在日常數(shù)據(jù)庫(kù)設(shè)計(jì)中,幾乎每張業(yè)務(wù)表都帶有一個(gè)日期列,用于記錄每條記錄產(chǎn)生和變更的時(shí)間。比如用戶表會(huì)有一個(gè)日期列記錄用戶注冊(cè)的時(shí)間、用戶最后登錄的時(shí)間。又比如,電商行業(yè)中的訂單表(核心業(yè)務(wù)表)會(huì)有一個(gè)訂單產(chǎn)生的時(shí)間列,當(dāng)支付時(shí)間超過(guò)訂單產(chǎn)生的時(shí)間,這個(gè)訂單可能會(huì)被系統(tǒng)自動(dòng)取消。

創(chuàng)新互聯(lián)于2013年創(chuàng)立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元天等做網(wǎng)站,已為上家服務(wù),為天等各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220

日期類型雖然常見(jiàn),但在表結(jié)構(gòu)設(shè)計(jì)中也容易犯錯(cuò),比如很多開(kāi)發(fā)同學(xué)都傾向使用整型存儲(chǔ)日期類型,同時(shí)也會(huì)忽略不同日期類型對(duì)于性能可能存在的潛在影響。所以你有必要認(rèn)真看看這篇文章,舉一反三,在自己的業(yè)務(wù)中做好日期類型的設(shè)計(jì)。

日期類型

MySQL 數(shù)據(jù)庫(kù)中常見(jiàn)的日期類型有 YEAR、DATE、TIME、DATETIME、TIMESTAMEP。因?yàn)闃I(yè)務(wù)絕大部分場(chǎng)景都需要將日期精確到秒,所以在表結(jié)構(gòu)設(shè)計(jì)中,常見(jiàn)使用的日期類型為DATETIME 和 TIMESTAMP。接下來(lái),我就帶你深入了解這兩種類型,以及它們?cè)谠O(shè)計(jì)中的應(yīng)用實(shí)戰(zhàn)。

DATETIME

類型 DATETIME 最終展現(xiàn)的形式為:YYYY-MM-DD HH:MM:SS,固定占用 8 個(gè)字節(jié)。

從 MySQL 5.6 版本開(kāi)始,DATETIME 類型支持毫秒,DATETIME(N) 中的 N 表示毫秒的精度。例如,DATETIME(6) 表示可以存儲(chǔ) 6 位的毫秒值。同時(shí),一些日期函數(shù)也支持精確到毫秒,例如常見(jiàn)的函數(shù) NOW、SYSDATE:

mysql> SELECT NOW(6);
+----------------------------+
| NOW(6) |
+----------------------------+
| 2020-09-14 17:50:28.707971 |
+----------------------------+
1 row in set (0.00 sec)

用戶可以將 DATETIME 初始化值設(shè)置為當(dāng)前時(shí)間,并設(shè)置自動(dòng)更新當(dāng)前時(shí)間的屬性。例如用戶表 User有register_date、last_modify_date兩個(gè)字段的定義:

CREATE TABLE User (
id BIGINT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
sex CHAR(1) NOT NULL,
password VARCHAR(1024) NOT NULL,
money INT NOT NULL DEFAULT 0,
register_date DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
last_modify_date DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
CHECK (sex = 'M' OR sex = 'F'),
PRIMARY KEY(id)
);

在上面的表 User 中,列 register_date 表示注冊(cè)時(shí)間,DEFAULT CURRENT_TIMESTAMP 表示記錄插入時(shí),若沒(méi)有指定時(shí)間,默認(rèn)就是當(dāng)前時(shí)間。

列 last_modify_date 表示當(dāng)前記錄最后的修改時(shí)間,DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) 表示每次修改都會(huì)修改為當(dāng)前時(shí)間。

這樣的設(shè)計(jì)保證當(dāng)用戶的金錢(qián)(money 字段)發(fā)生了變更,則 last_modify_date 能記錄最后一次用戶金錢(qián)發(fā)生變更時(shí)的時(shí)間。來(lái)看下面的例子:

mysql> SELECT name,money,last_modify_date FROM User WHERE name = 'David';
+-------+-------+----------------------------+
| name | money | last_modify_date |
+-------+-------+----------------------------+
| David | 100 | 2020-09-13 08:08:33.898593 |
+-------+-------+----------------------------+
1 row in set (0.00 sec)


mysql> UPDATE User SET money = money - 1 WHERE name = 'David';
Query OK, 1 row affected (0.06 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> SELECT name,money,last_modify_date FROM User WHERE name = 'David';
+-------+-------+----------------------------+
| name | money | last_modify_date |
+-------+-------+----------------------------+
| David | 99 | 2020-09-14 18:29:17.056327 |
+-------+-------+----------------------------+
1 row in set (0.00 sec)

可以看到,當(dāng)用戶金額發(fā)生修改時(shí),所對(duì)應(yīng)的字段 last_modify_date 也修改成發(fā)生變更的時(shí)間。

TIMESTAMP

除了 DATETIME,日期類型中還有一種 TIMESTAMP 的時(shí)間戳類型,其實(shí)際存儲(chǔ)的內(nèi)容為‘1970-01-01 00:00:00’到現(xiàn)在的毫秒數(shù)。在 MySQL 中,由于類型 TIMESTAMP 占用 4 個(gè)字節(jié),因此其存儲(chǔ)的時(shí)間上限只能到‘2038-01-19 03:14:07’。

同類型 DATETIME 一樣,從 MySQL 5.6 版本開(kāi)始,類型 TIMESTAMP 也能支持毫秒。與 DATETIME 不同的是,若帶有毫秒時(shí),類型 TIMESTAMP 占用 7 個(gè)字節(jié),而 DATETIME 無(wú)論是否存儲(chǔ)毫秒信息,都占用 8 個(gè)字節(jié)。

類型 TIMESTAMP 最大的優(yōu)點(diǎn)是可以帶有時(shí)區(qū)屬性,因?yàn)樗举|(zhì)上是從毫秒轉(zhuǎn)化而來(lái)。如果你的業(yè)務(wù)需要對(duì)應(yīng)不同的國(guó)家時(shí)區(qū),那么類型 TIMESTAMP 是一種不錯(cuò)的選擇。比如新聞?lì)惖臉I(yè)務(wù),通常用戶想知道這篇新聞發(fā)布時(shí)對(duì)應(yīng)的自己國(guó)家時(shí)間,那么 TIMESTAMP 是一種選擇。

另外,有些國(guó)家會(huì)執(zhí)行夏令時(shí)。根據(jù)不同的季節(jié),人為地調(diào)快或調(diào)慢 1 個(gè)小時(shí),帶有時(shí)區(qū)屬性的 TIMESTAMP 類型本身就能解決這個(gè)問(wèn)題。

參數(shù) time_zone 指定了當(dāng)前使用的時(shí)區(qū),默認(rèn)為 SYSTEM 使用操作系統(tǒng)時(shí)區(qū),用戶可以通過(guò)該參數(shù)指定所需要的時(shí)區(qū)。

如果想使用 TIMESTAMP 的時(shí)區(qū)功能,你可以通過(guò)下面的語(yǔ)句將之前的用戶表 User 的注冊(cè)時(shí)間字段類型從 DATETIME(6) 修改為 TIMESTAMP(6):

 ALTER TABLE User 
CHANGE register_date
register_date TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6);

這時(shí)通過(guò)設(shè)定不同的 time_zone,可以觀察到不同時(shí)區(qū)下的注冊(cè)時(shí)間:

mysql> SELECT name,regist er_date FROM User WHERE name = 'David';
+-------+----------------------------+
| name | register_date |
+-------+----------------------------+
| David | 2018-09-14 18:28:33.898593 |
+-------+----------------------------+
1 row in set (0.00 sec)

mysql> SET time_zone = '-08:00';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT name,register_date FROM User WHERE name = 'David';
+-------+----------------------------+
| name | register_date |
+-------+----------------------------+
| David | 2018-09-14 02:28:33.898593 |
+-------+----------------------------+
1 row in set (0.00 sec)

從上述例子中,你可以看到,中國(guó)的時(shí)區(qū)是 +08:00,美國(guó)的時(shí)區(qū)是 -08:00,因此改為美國(guó)時(shí)區(qū)后,可以看到用戶注冊(cè)時(shí)間比之前延遲了 16 個(gè)小時(shí)。當(dāng)然了,直接加減時(shí)區(qū)并不直觀,需要非常熟悉各國(guó)的時(shí)區(qū)表。在 MySQL 中可以直接設(shè)置時(shí)區(qū)的名字,如:

mysql> SET time_zone = 'America/Los_Angeles';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT NOW();
+---------------------+
| NOW() |
+---------------------+
| 2020-09-14 20:12:49 |
+---------------------+
1 row in set (0.00 sec)

mysql> SET time_zone = 'Asia/Shanghai';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT NOW();
+---------------------+
| NOW() |
+---------------------+
| 2020-09-15 11:12:55 |
+---------------------+
1 row in set (0.00 sec)

講到這兒,想必你已經(jīng)了解了時(shí)間字段類型,接下來(lái)我將分享在真實(shí)業(yè)務(wù)設(shè)計(jì)中如何使用好時(shí)間類型。

業(yè)務(wù)表結(jié)構(gòu)設(shè)計(jì)實(shí)戰(zhàn)

DATETIME vs TIMESTAMP vs INT,怎么選?

在做表結(jié)構(gòu)設(shè)計(jì)時(shí),對(duì)日期字段的存儲(chǔ),開(kāi)發(fā)人員通常會(huì)有 3 種選擇:DATETIME、TIMESTAMP、INT。

INT 類型就是直接存儲(chǔ) '1970-01-01 00:00:00' 到現(xiàn)在的毫秒數(shù),本質(zhì)和 TIMESTAMP 一樣,因此用 INT 不如直接使用 TIMESTAMP。

當(dāng)然,有些同學(xué)會(huì)認(rèn)為 INT 比 TIMESTAMP 性能更好。但是,由于當(dāng)前每個(gè) CPU 每秒可執(zhí)行上億次的計(jì)算,所以無(wú)須為這種轉(zhuǎn)換的性能擔(dān)心。更重要的是,在后期運(yùn)維和數(shù)據(jù)分析時(shí),使用 INT 存儲(chǔ)日期,是會(huì)讓 DBA 和數(shù)據(jù)分析人員發(fā)瘋的,INT的可運(yùn)維性太差。

也有的同學(xué)會(huì)熱衷用類型 TIMESTEMP 存儲(chǔ)日期,因?yàn)轭愋?TIMESTAMP 占用 4 個(gè)字節(jié),比 DATETIME 小一半的存儲(chǔ)空間。

但若要將時(shí)間精確到毫秒,TIMESTAMP 要 7 個(gè)字節(jié),和 DATETIME 8 字節(jié)差不太多。另一方面,現(xiàn)在距離 TIMESTAMP 的最大值‘2038-01-19 03:14:07’已經(jīng)很近,這是需要開(kāi)發(fā)同學(xué)好好思考的問(wèn)題。

總的來(lái)說(shuō),我建議你使用類型 DATETIME。 對(duì)于時(shí)區(qū)問(wèn)題,可以由前端或者服務(wù)這里做一次轉(zhuǎn)化,不一定非要在數(shù)據(jù)庫(kù)中解決。

不要忽視 TIMESTAMP 的性能問(wèn)題

前面已經(jīng)提及,TIMESTAMP 的上限值 2038 年很快就會(huì)到來(lái),那時(shí)業(yè)務(wù)又將面臨一次類似千年蟲(chóng)的問(wèn)題。另外,TIMESTAMP 還存在潛在的性能問(wèn)題。

雖然從毫秒數(shù)轉(zhuǎn)換到類型 TIMESTAMP 本身需要的 CPU 指令并不多,這并不會(huì)帶來(lái)直接的性能問(wèn)題。但是如果使用默認(rèn)的操作系統(tǒng)時(shí)區(qū),則每次通過(guò)時(shí)區(qū)計(jì)算時(shí)間時(shí),要調(diào)用操作系統(tǒng)底層系統(tǒng)函數(shù) __tz_convert(),而這個(gè)函數(shù)需要額外的加鎖操作,以確保這時(shí)操作系統(tǒng)時(shí)區(qū)沒(méi)有修改。所以,當(dāng)大規(guī)模并發(fā)訪問(wèn)時(shí),由于熱點(diǎn)資源競(jìng)爭(zhēng),會(huì)產(chǎn)生兩個(gè)問(wèn)題。

  • 性能不如 DATETIME:DATETIME 不存在時(shí)區(qū)轉(zhuǎn)化問(wèn)題。
  • 性能抖動(dòng):海量并發(fā)時(shí),存在性能抖動(dòng)問(wèn)題。

為了優(yōu)化 TIMESTAMP 的使用,強(qiáng)烈建議你使用顯式的時(shí)區(qū),而不是操作系統(tǒng)時(shí)區(qū)。比如在配置文件中顯示地設(shè)置時(shí)區(qū),而不要使用系統(tǒng)時(shí)區(qū):

[mysqld]
time_zone = "+08:00"

最后,通過(guò)命令 mysqlslap 來(lái)測(cè)試 TIMESTAMP、DATETIME 的性能,命令如下:

# 比較time_zone為System和Asia/Shanghai的性能對(duì)比

mysqlslap -uroot --number-of-queries=1000000 --concurrency=100 --query='SELECT NOW()'

最后的性能對(duì)比如下:

從表中可以發(fā)現(xiàn),顯式指定時(shí)區(qū)的性能要遠(yuǎn)遠(yuǎn)好于直接使用操作系統(tǒng)時(shí)區(qū)。所以,日期字段推薦使用 DATETIME,沒(méi)有時(shí)區(qū)轉(zhuǎn)化。即便使用 TIMESTAMP,也需要在數(shù)據(jù)庫(kù)中顯式地配置時(shí)區(qū),而不是用系統(tǒng)時(shí)區(qū)。

表結(jié)構(gòu)設(shè)計(jì)規(guī)范:每條記錄都要有一個(gè)時(shí)間字段

在做表結(jié)構(gòu)設(shè)計(jì)規(guī)范時(shí),強(qiáng)烈建議你每張業(yè)務(wù)核心表都增加一個(gè) DATETIME 類型的 last_modify_date 字段,并設(shè)置修改自動(dòng)更新機(jī)制, 即便標(biāo)識(shí)每條記錄最后修改的時(shí)間。

例如,在前面的表 User 中的字段 last_modify_date,就是用于表示最后一次的修改時(shí)間:

CREATE TABLE User (
id BIGINT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
sex CHAR(1) NOT NULL,
password VARCHAR(1024) NOT NULL,
money INT NOT NULL DEFAULT 0,
register_date DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
last_modify_date DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
CHECK (sex = 'M' OR sex = 'F'),
PRIMARY KEY(id)
);

通過(guò)字段 last_modify_date 定義的 ON UPDATE CURRENT_TIMESTAMP(6),那么每次這條記錄,則都會(huì)自動(dòng)更新 last_modify_date 為當(dāng)前時(shí)間。

這樣設(shè)計(jì)的好處是:用戶可以知道每個(gè)用戶最近一次記錄更新的時(shí)間,以便做后續(xù)的處理。比如在電商的訂單表中,可以方便對(duì)支付超時(shí)的訂單做處理;在金融業(yè)務(wù)中,可以根據(jù)用戶資金最后的修改時(shí)間做相應(yīng)的資金核對(duì)等。

在后面的內(nèi)容中,我們也會(huì)談到 MySQL 數(shù)據(jù)庫(kù)的主從邏輯數(shù)據(jù)核對(duì)的設(shè)計(jì)實(shí)現(xiàn),也會(huì)利用到last_modify_date 字段。

總結(jié)

日期類型通常就是使用 DATETIME 和 TIMESTAMP 兩種類型,然而由于類型 TIMESTAMP 存在性能問(wèn)題,建議你還是盡可能使用類型 DATETIME。我總結(jié)一下今天的重點(diǎn)內(nèi)容:

  • MySQL 5.6 版本開(kāi)始 DATETIME 和 TIMESTAMP 精度支持到毫秒;
  • DATETIME 占用 8 個(gè)字節(jié),TIMESTAMP 占用 4 個(gè)字節(jié),DATETIME(6) 依然占用 8 個(gè)字節(jié),TIMESTAMP(6) 占用 7 個(gè)字節(jié);
  • TIMESTAMP 日期存儲(chǔ)的上限為 2038-01-19 03:14:07,業(yè)務(wù)用 TIMESTAMP 存在風(fēng)險(xiǎn);
  • 使用 TIMESTAMP 必須顯式地設(shè)置時(shí)區(qū),不要使用默認(rèn)系統(tǒng)時(shí)區(qū),否則存在性能問(wèn)題,推薦在配置文件中設(shè)置參數(shù) time_zone = '+08:00';
  • 推薦日期類型使用 DATETIME,而不是 TIMESTAMP 和 INT 類型;
  • 表結(jié)構(gòu)設(shè)計(jì)時(shí),每個(gè)核心業(yè)務(wù)表,推薦設(shè)計(jì)一個(gè) last_modify_date 的字段,用以記錄每條記錄的最后修改時(shí)間。

名稱欄目:警告!別再使用TIMESTAMP作為日期字段
網(wǎng)址分享:http://www.5511xx.com/article/dhsiogp.html