前言:mysql
在使用 MySQL 的過程當中,你可能會遇到時區相關問題,好比說時間顯示錯誤、時區不是東八區、程序取得的時間和數據庫存儲的時間不一致等等問題。其實,這些問題都與數據庫時區設置有關,本篇文章將從數據庫參數入手,逐步介紹時區相關內容。linux
首先說明下log_timestamps
參數並不影響時區,只是設置不一樣會影響某些日誌記錄的時間。該參數主要是控制 error log、slow log、genera log 日誌文件中的顯示時間,但不會影響 general log 和 slow log 寫到表 (mysql.general_log, mysql.slow_log) 中的顯示時間。web
log_timestamps 是全局參數,可動態修改,默認使用 UTC 時區,這樣會使得日誌中記錄的時間比北京時間慢 8 個小時,致使查看日誌不方便。能夠修改成 SYSTEM 變成使用系統時區。下面簡單測試下該參數的做用及修改方法:sql
# 查看參數值
mysql> show global variables like 'log_timestamps'; +----------------+-------+ | Variable_name | Value | +----------------+-------+ | log_timestamps | UTC | +----------------+-------+ 1 row in set (0.00 sec) # 產生慢日誌 mysql> select sleep(10),now(); +-----------+---------------------+ | sleep(10) | now() | +-----------+---------------------+ | 0 | 2020-06-24 17:12:40 | +-----------+---------------------+ 1 row in set (10.00 sec) # 慢日誌文件記錄內容 發現時間是UTC時間 # Time: 2020-06-24T09:12:50.555348Z # User@Host: root[root] @ localhost [] Id: 10 # Query_time: 10.000354 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 1 SET timestamp=1592989960; select sleep(10),now(); # 修改參數值 再次測試 mysql> set global log_timestamps = SYSTEM; Query OK, 0 rows affected (0.00 sec) mysql> select sleep(10),now(); +-----------+---------------------+ | sleep(10) | now() | +-----------+---------------------+ | 0 | 2020-06-24 17:13:44 | +-----------+---------------------+ 1 row in set (10.00 sec) # 慢日誌文件記錄內容 時間是對的 # Time: 2020-06-24T17:13:54.514413+08:00 # User@Host: root[root] @ localhost [] Id: 10 # Query_time: 10.000214 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 1 SET timestamp=1592990024; select sleep(10),now(); 複製代碼
time_zone
參數用來設置每一個鏈接會話的時區,該參數分爲全局和會話級別,能夠動態修改。默認值爲 SYSTEM,此時使用的是全局參數 system_time_zone 的值,而 system_time_zone 默認繼承自當前系統的時區,即默認狀況下 MySQL 時區和系統時區相同。數據庫
時區設置主要影響時區敏感的時間值的顯示和存儲。包括一些函數(如 now()、curtime())顯示的值,以及存儲在 TIMESTAMP 類型中的值,但不影響 DATE、TIME 和 DATETIME 列中的值,由於這些數據類型在存取時未進行時區轉換,而 TIMESTAMP 類型存入數據庫的實際是 UTC 的時間,查詢顯示時會根據具體的時區來顯示不一樣的時間。centos
下面咱們來測試下 time_zone 參數修改產生的影響:併發
# 查看linux系統時間及時區
[root@centos ~]# date Sun Jun 28 14:29:10 CST 2020 # 查看MySQL當前時區、時間 mysql> show global variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | CST | | time_zone | SYSTEM | +------------------+--------+ 2 rows in set (0.00 sec) mysql> select now(); +---------------------+ | now() | +---------------------+ | 2020-06-28 14:31:12 | +---------------------+ 1 row in set (0.00 sec) # 建立測試表、插入部分數據 mysql> CREATE TABLE `time_zone_test` ( -> `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主鍵', -> `dt_col` datetime DEFAULT NULL COMMENT 'datetime時間', -> `ts_col` timestamp DEFAULT NULL COMMENT 'timestamp時間', -> PRIMARY KEY (`id`) -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='time_zone測試表'; Query OK, 0 rows affected, 1 warning (0.07 sec) mysql> insert into time_zone_test (dt_col,ts_col) values ('2020-06-01 17:30:00','2020-06-01 17:30:00'),(now(),now()); Query OK, 2 rows affected (0.01 sec) Records: 2 Duplicates: 0 Warnings: 0 mysql> select * from time_zone_test; +----+---------------------+---------------------+ | id | dt_col | ts_col | +----+---------------------+---------------------+ | 1 | 2020-06-01 17:30:00 | 2020-06-01 17:30:00 | | 2 | 2020-06-28 14:34:55 | 2020-06-28 14:34:55 | +----+---------------------+---------------------+ # 改成UTC時區 並從新鏈接 發現timestamp存儲的時間會隨時區變化 mysql> set global time_zone='+0:00'; Query OK, 0 rows affected (0.00 sec) mysql> set time_zone='+0:00'; Query OK, 0 rows affected (0.00 sec) mysql> show global variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | CST | | time_zone | +00:00 | +------------------+--------+ 2 rows in set (0.00 sec) mysql> select now(); +---------------------+ | now() | +---------------------+ | 2020-06-28 06:36:16 | +---------------------+ 1 row in set (0.00 sec) mysql> select * from time_zone_test; +----+---------------------+---------------------+ | id | dt_col | ts_col | +----+---------------------+---------------------+ | 1 | 2020-06-01 17:30:00 | 2020-06-01 09:30:00 | | 2 | 2020-06-28 14:34:55 | 2020-06-28 06:34:55 | +----+---------------------+---------------------+ 2 rows in set (0.00 sec) # 改回東八時區,恢復正常 mysql> set global time_zone='+8:00'; Query OK, 0 rows affected (0.00 sec) mysql> set time_zone='+8:00'; Query OK, 0 rows affected (0.00 sec) mysql> show global variables like '%time_zone%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | system_time_zone | CST | | time_zone | +08:00 | +------------------+--------+ 2 rows in set (0.00 sec) mysql> select now(); +---------------------+ | now() | +---------------------+ | 2020-06-28 14:39:14 | +---------------------+ 1 row in set (0.00 sec) mysql> select * from time_zone_test; +----+---------------------+---------------------+ | id | dt_col | ts_col | +----+---------------------+---------------------+ | 1 | 2020-06-01 17:30:00 | 2020-06-01 17:30:00 | | 2 | 2020-06-28 14:34:55 | 2020-06-28 14:34:55 | +----+---------------------+---------------------+ 2 rows in set (0.00 sec) 複製代碼
若是須要永久生效,還需寫入配置文件中。例如將時區改成東八區,則須要在配置文件[mysqld]部分增長一行:default_time_zone = '+8:00'。編輯器
時區設置不妥可能會產生各類問題,下面咱們列舉下幾個常見的問題及解決方法:函數
3.1 MySQL 內部時間不是北京時間性能
遇到這類問題,首先檢查下系統時間及時區是否正確,而後看下 MySQL 的 time_zone,建議將 time_zone 改成'+8:00'。
3.2 Java 程序存取的時間與數據庫中的時間相差 8 小時
出現此問題的緣由大機率是程序時區與數據庫時區不一致致使的。咱們能夠檢查下兩邊的時區,若是想統一採用北京時間,則能夠在 jdbc 鏈接串中增長 serverTimezone=Asia/Shanghai
,而且 MySQL 方面也能夠將 time_zone 改成'+8:00'。
3.3 程序時間與數據庫時間相差 13 小時或 14 小時
若是說相差 8 小時不夠讓人驚訝,那相差 13 小時可能會讓不少人摸不着頭腦。出現這個問題的緣由是 JDBC 與 MySQL 對 「CST」 時區協商不一致。由於 CST 時區是一個很混亂的時區,有四種含義:
MySQL 中,若是 time_zone 爲默認的 SYSTEM 值,則時區會繼承爲系統時區 CST,MySQL 內部將其認爲是 UTC+08:00。而 jdbc 會將 CST 認爲是美國中部時間,這就致使會相差 13 小時,若是處在冬令時還會相差 14 個小時。
解決此問題的方法也很簡單,咱們能夠明確指定 MySQL 數據庫的時區,不使用引起誤解的 CST,能夠將 time_zone 改成'+8:00',同時 jdbc 鏈接串中也能夠增長 serverTimezone=Asia/Shanghai。
3.4 如何避免出現時區問題
如何避免上述時區問題,可能你內心也有了些方法,簡要總結幾點以下:
可能有的同窗說了,咱們數據庫中 time_zone 參數選擇的是默認的 SYSTEM 值,也沒有發生程序時間和數據庫時間不一致的問題。此時是否須要將 time_zone 改成'+8:00'?在這種狀況下仍是建議將 time_zone 改成'+8:00',特別是常常查詢 TIMESTAMP 字段,由於當 time_zone=system 的時候,查詢 timestamp 字段會調用系統的時區作時區轉換,有全局鎖__libc_lock_lock 的保護,可能致使線程併發環境下系統性能受限。而改成'+8:00'則不會觸發系統時區轉換,使用 MySQL 自身轉換,大大提升了性能。
總結:
讀完本篇文章,你是否對數據庫時區有了更深入的認識呢。但願這篇文章對你有所幫助,特別是想了解 MySQL 時區相關內容時,能夠拿來多讀讀。若是你遇到過其餘時區相關問題,歡迎留言討論。
本文使用 mdnice 排版
- END -