<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          MySQL 盡量避免使用 TIMESTAMP!!

          共 7953字,需瀏覽 16分鐘

           ·

          2021-10-29 20:59

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          MySQL 中常見(jiàn)的時(shí)間類型有三種DATE, DATETIMETIMESTAMP,其中DATE類型用于表示日期,但是不會(huì)包含時(shí)間,格式為YYYY-MM-DD,而DATETIMETIMESTAMP用于表示日期和時(shí)間,常見(jiàn)的格式為YYYY-MM-DD HH:MM:SS,也可以帶6位小數(shù)來(lái)表示微秒。

          不同于DATETIMETIMESTAMP支持的時(shí)間范圍從1970-01-01 00:00:01.0000002038-01-19 03:14:07.999999,使用了TIMESTAMP的應(yīng)用很有可能在2038-01-19 03:14:07.999999之后宕機(jī),同樣面臨這個(gè)問(wèn)題的還有所有的類Unix系統(tǒng),因?yàn)樗麄兪褂昧?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">time_t這一32位數(shù)字來(lái)表示時(shí)間,這就是著名的2038年問(wèn)題

          因?yàn)闀r(shí)間問(wèn)題搞壞系統(tǒng)的例子可不少,在2016年曾經(jīng)爆出過(guò)一個(gè)iPhonebug,如果將iPhone的時(shí)間調(diào)整到1970-01-01 00:00:00,則會(huì)導(dǎo)致手機(jī)”變磚“,原因是IOS基于BSD這種Unix系統(tǒng)構(gòu)建,在將時(shí)間調(diào)整到1970-01-01 00:00:00后,如果手機(jī)需要展示之前的時(shí)間,例如之前收到過(guò)短信,則會(huì)導(dǎo)致整數(shù)溢出。

          對(duì)于2038問(wèn)題,Linux的解法是提供新的用戶接口:https://kernelnewbies.org/y2038.但是MySql至今還沒(méi)有相應(yīng)的公告。

          TIMESTAMP的設(shè)計(jì)之初是為了支持自動(dòng)時(shí)區(qū)轉(zhuǎn)換:

          mysql>?CREATE?TABLE?`employee`?(
          ????->??`entry_time`?timestamp?NOT?NULL?DEFAULT?CURRENT_TIMESTAMP?ON?UPDATE?CURRENT_TIMESTAMP
          ????->?)?ENGINE=InnoDB
          ????->?;
          Query?OK,?0?rows?affected?(0.01?sec)

          mysql>?INSERT?INTO?`employee`?(`entry_time`)?VALUES?(CURRENT_TIMESTAMP);
          Query?OK,?1?row?affected?(0.01?sec)

          mysql>?SELECT?*?FROM?`employee`;
          +---------------------+
          |?entry_time??????????|
          +---------------------+
          |?2021-05-09?08:14:08?|
          +---------------------+
          1?row?in?set?(0.00?sec)

          mysql>?SET?@@session.time_zone?=?'-05:00';?SELECT?*?FROM?`employee`;
          Query?OK,?0?rows?affected?(0.00?sec)

          +---------------------+
          |?entry_time??????????|
          +---------------------+
          |?2021-05-09?03:14:08?|
          +---------------------+
          1?row?in?set?(0.00?sec)

          但是TIMESTAMP的一些設(shè)計(jì)卻非常鬼畜,比如:

          • 如果表中包含TIMESTAMP的列,那么其建表語(yǔ)句有可能被系統(tǒng)篡改,取決于MySql的版本和參數(shù)設(shè)置。
          • 當(dāng)MySQL參數(shù)time_zone=system時(shí),高并發(fā)可能會(huì)引起CPU使用率暴漲,系統(tǒng)響應(yīng)變慢甚至假死
          • 如果存入超過(guò)范圍的時(shí)間,在非嚴(yán)格狀態(tài)下,MySql不會(huì)報(bào)錯(cuò),反而會(huì)插入'0000-00-00 00:00:00'

          新建一個(gè)包含TIMESTAMP的表可真難

          MySql 5.6.6版本引入了explicit_defaults_for_timestamp這個(gè)參數(shù),隨即被標(biāo)記為廢棄,這個(gè)參數(shù)主要影響表中類型為TIMESTAMP的那些列在新建表時(shí)的表現(xiàn)

          mysql>?show?variables?like?'explicit_defaults_for_timestamp';
          +---------------------------------+-------+
          |?Variable_name???????????????????|?Value?|
          +---------------------------------+-------+
          |?explicit_defaults_for_timestamp?|?OFF???|
          +---------------------------------+-------+

          mysql>?create?table?t1?
          ????->?(
          ????->?ts1?timestamp,
          ????->?ts2?timestamp,
          ????->?ts3?timestamp?default?'2010-01-01?00:00:00'
          ????->?);
          Query?OK,?0?rows?affected?(0.03?sec)

          mysql>?show?create?table?t1\G
          ***************************?1.?row?***************************
          ???????Table:?t1
          Create?Table:?CREATE?TABLE?`t1`?(
          ??`ts1`?timestamp?NOT?NULL?DEFAULT?CURRENT_TIMESTAMP?ON?UPDATE?CURRENT_TIMESTAMP,
          ??`ts2`?timestamp?NOT?NULL?DEFAULT?'0000-00-00?00:00:00',
          ??`ts3`?timestamp?NOT?NULL?DEFAULT?'2010-01-01?00:00:00'
          )?ENGINE=InnoDB?DEFAULT?CHARSET=utf8
          1?row?in?set?(0.00?sec)

          雖然我們輸入的建表語(yǔ)句很簡(jiǎn)單,但是MySql卻對(duì)于我們輸入的建表語(yǔ)句做了諸多的篡改:

          • 對(duì)于表中的第一個(gè)TIMESTAMP列,系統(tǒng)自動(dòng)加了NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,這些操作對(duì)于新建表的開(kāi)發(fā)者完全是不感知的。
          • 對(duì)于表中的第二個(gè)TIMESTAMP列,系統(tǒng)自動(dòng)加了一個(gè)默認(rèn)值0000-00-00 00:00:00,這個(gè)操作同樣對(duì)于新建表的開(kāi)發(fā)者完全不感知。

          在系統(tǒng)對(duì)我們的建表語(yǔ)句做了自動(dòng)修改之后,對(duì)表的插入操作可能就不會(huì)如開(kāi)發(fā)者預(yù)期的那樣:

          mysql>?insert?into?t1?values?(null,null,null);
          Query?OK,?1?row?affected?(0.00?sec)

          mysql>?select?*?from?t1;
          +---------------------+---------------------+---------------------+
          |?ts1?????????????????|?ts2?????????????????|?ts3?????????????????|
          +---------------------+---------------------+---------------------+
          |?2021-05-09?07:47:50?|?2021-05-09?07:47:50?|?2021-05-09?07:47:50?|
          +---------------------+---------------------+---------------------+
          1?row?in?set?(0.00?sec)

          可以看到,MySql的表現(xiàn)非常的鬼畜

          • 對(duì)于第一個(gè)TIMESTAMP列,建表語(yǔ)句中指定可以為null,但是插入null的時(shí)候存到表里的卻是當(dāng)前時(shí)間
          • 對(duì)于第二個(gè)TIMESTAMP列,雖然通過(guò)語(yǔ)句show create table t1\G查出來(lái)的建表語(yǔ)句指定的默認(rèn)值是'0000-00-00 00:00:00'但是存到表里的卻是當(dāng)前時(shí)間
          • 最奇怪的是第三個(gè)TIMESTAMP列,盡管我們顯式指定默認(rèn)值為'2010-01-01 00:00:00',但是落表的時(shí)間仍然是當(dāng)前時(shí)間

          這一切都是在參數(shù)explicit_defaults_for_timestamp被設(shè)置為OFF的時(shí)候發(fā)生的,但是遺憾的是OFF恰恰就是參數(shù)explicit_defaults_for_timestamp的默認(rèn)值。

          如果我們將explicit_defaults_for_timestamp的值改為ON,則事情會(huì)變得好很多
          mysql>?show?variables?like?'explicit_defaults_for_timestamp';
          +---------------------------------+-------+
          |?Variable_name???????????????????|?Value?|
          +---------------------------------+-------+
          |?explicit_defaults_for_timestamp?|?ON????|
          +---------------------------------+-------+
          mysql>?create?table?t2?
          ????->?(
          ????->?ts1?timestamp,
          ????->?ts2?timestamp,
          ????->?ts3?timestamp?default?'2010-01-01?00:00:00'
          ????->?);
          Query?OK,?0?rows?affected?(0.02?sec)

          mysql>?show?create?table?t2\G
          ***************************?1.?row?***************************
          ???????Table:?t2
          Create?Table:?CREATE?TABLE?`t2`?(
          ??`ts1`?timestamp?NULL?DEFAULT?NULL,
          ??`ts2`?timestamp?NULL?DEFAULT?NULL,
          ??`ts3`?timestamp?NULL?DEFAULT?'2010-01-01?00:00:00'
          )?ENGINE=InnoDB?DEFAULT?CHARSET=utf8
          1?row?in?set?(0.01?sec)

          mysql>?insert?into?t2?values?(null,null,null);
          Query?OK,?1?row?affected?(0.01?sec)

          mysql>?select?*?from?t2;
          +------+------+------+
          |?ts1??|?ts2??|?ts3??|
          +------+------+------+
          |?NULL?|?NULL?|?NULL?|
          +------+------+------+
          1?row?in?set?(0.00?sec)

          這一次,建表語(yǔ)句中那些奇怪的默認(rèn)值都沒(méi)有了,清爽了好多,而且TIMESTAMP的的列也可以插入NULL了,如果我們顯式指定了NOT NULLSTRICT_TRANS_TABLES被指定的情況下直接報(bào)錯(cuò),如果STRICT_TRANS_TABLES沒(méi)有被指定,那么會(huì)向該列中插入0000-00-00 00:00:00并且產(chǎn)生一個(gè)warning

          mysql>?create?table?t3?
          ????->?(
          ????->?ts1?timestamp,
          ????->?ts2?timestamp,
          ????->?ts3?timestamp?not?null
          ????->?);
          Query?OK,?0?rows?affected?(0.01?sec)

          mysql>?show?create?table?t3\G
          ***************************?1.?row?***************************
          ???????Table:?t3
          Create?Table:?CREATE?TABLE?`t3`?(
          ??`ts1`?timestamp?NULL?DEFAULT?NULL,
          ??`ts2`?timestamp?NULL?DEFAULT?NULL,
          ??`ts3`?timestamp?NOT?NULL
          )?ENGINE=InnoDB?DEFAULT?CHARSET=utf8
          1?row?in?set?(0.01?sec)

          mysql>?insert?into?t3?values?(null,null,null);
          ERROR?1048?(23000):?Column?'ts3'?cannot?be?null

          mysql>?insert?into?t3?(ts1,ts2)?values?(null,null);
          Query?OK,?1?row?affected,?1?warning?(0.01?sec)

          mysql>?show?warnings;
          +---------+------+------------------------------------------+
          |?Level???|?Code?|?Message??????????????????????????????????|
          +---------+------+------------------------------------------+
          |?Warning?|?1364?|?Field?'ts3'?doesn't?have?a?default?value?|
          +---------+------+------------------------------------------+

          mysql>?select?*?from?t3;
          +------+------+---------------------+
          |?ts1??|?ts2??|?ts3?????????????????|
          +------+------+---------------------+
          |?NULL?|?NULL?|?0000-00-00?00:00:00?|
          +------+------+---------------------+

          高并發(fā)環(huán)境下并不適合使用TIMESTAMP

          這一點(diǎn)MySql的文檔中有明確的說(shuō)明:

          Note

          If set to SYSTEM, every MySQL function call that requires a time zone calculation makes a system library call to determine the current system time zone. This call may be protected by a global mutex, resulting in contention.

          雖然通過(guò)TIMESTAMP可以自動(dòng)轉(zhuǎn)換時(shí)區(qū),代價(jià)是當(dāng)MySQL參數(shù)time_zone=system時(shí)每次都會(huì)嘗試獲取一個(gè)全局鎖,這在高并發(fā)的環(huán)境下無(wú)疑是致命的,可能會(huì)導(dǎo)致線程上下文頻繁切換,CPU使用率暴漲,系統(tǒng)響應(yīng)變慢甚至假死。點(diǎn)擊這個(gè)Java面試庫(kù)小程序可以在線刷題。

          時(shí)間范圍并不是強(qiáng)校驗(yàn)的

          如果我們嘗試往MySql中插入超過(guò)TIMESTAMP可表示的時(shí)間范圍的值,MySql在非嚴(yán)格模式下并不會(huì)報(bào)錯(cuò),僅會(huì)產(chǎn)生一個(gè)warning

          mysql> insert into t1 values ('2039-01-01 00:00:00',null,null);
          Query OK, 1 row affected, 1 warning (0.00 sec)

          mysql> show warnings;
          +---------+------+----------------------------------------------+
          | Level | Code | Message |
          +---------+------+----------------------------------------------+
          | Warning | 1264 | Out of range value for column 'ts1' at row 1 |
          +---------+------+----------------------------------------------+
          1 row in set (0.00 sec)

          mysql> select * from t1;
          +---------------------+---------------------+---------------------+
          | ts1 | ts2 | ts3 |
          +---------------------+---------------------+---------------------+
          | 2021-05-09 07:47:50 | 2021-05-09 07:47:50 | 2021-05-09 07:47:50 |
          | 0000-00-00 00:00:00 | 2021-05-09 08:09:06 | 2021-05-09 08:09:06 |
          +---------------------+---------------------+---------------------+
          2 rows in set (0.00 sec)

          總結(jié)

          現(xiàn)在用TIMESTAMP比較少了,的確也應(yīng)該盡量避免使用TIMESTAMPMySqlTIMESTAMP的設(shè)計(jì)上實(shí)在是蹩腳,如果你正在維護(hù)一個(gè)老的系統(tǒng),涉及到TIMESTAMP的改動(dòng)需要格外注意,盡量要在充分的測(cè)試后再上線。


          最后,關(guān)注公眾號(hào)Java技術(shù)棧,在后臺(tái)回復(fù):面試,可以獲取我整理的 Java/ MySQL 系列面試題和答案,非常齊全。

          參考資料

          [1]????https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html
          [2]????https://blog.csdn.net/weter_drop/article/details/89924451
          [3]????https://kernelnewbies.org/y2038
          [4]????https://segmentfault.com/a/1190000018818020
          [5]????https://www.ithome.com.tw/news/103902





          關(guān)注Java技術(shù)棧看更多干貨



          獲取 Spring Boot 實(shí)戰(zhàn)筆記!
          瀏覽 38
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日日骚av无码 | www.日韩一级 | 日本黄色视频网站大全 | 亚欧洲精品| 久久久久久夜色 |