程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> MySQL綜合教程 >> MySQL關於timestamp和mysqldump的“bug”

MySQL關於timestamp和mysqldump的“bug”

編輯:MySQL綜合教程

MySQL關於timestamp和mysqldump的“bug”   復現  www.2cto.com   來源於一個同事在做數據轉儲碰到的的問題,簡化如下: 1、建表 drop table if exists tb; CREATE TABLE tb (   c timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ) ENGINE=InnoDB DEFAULT CHARSET=gbk; insert into tb values(now()); select * from tb;   返回 mysql> select * from tb; +---------------------+ | c                   | +---------------------+ | 2012-12-14 00:42:45 | +---------------------+ 1 row in set (0.00 sec)   2、dump“出錯”   mysqldump   -Srun/mysql.sock -uroot   test tb   --where='c="2012-12-14 00:42:45"' | grep INSERT 返回為空,也就是說導不到數據。    www.2cto.com     分析 從上面的結論看上去,似乎是mysqldump的”bug”,看得到的數據都導不出來。 如果我們先不加where條件, mysqldump   -Srun/mysql.sock -uroot   test tb    |grep INSERT                          INSERT INTO `tb` VALUES ('2012-12-13 16:42:45');   接下來我們要說說關於timestamp這個字段類型。 首先,從大小上你可以看出來,它不是個字符串,實際上是一個整型。所以當我們執行 where c=” 2012-12-14 00:42:45”的時候,需要將其轉換為整型。這就涉及到轉換規則。也就是說,對於相同的時間戳,在不同的時區顯示的結果是不一樣的。反過來也一樣,相同的字符串,在不同的時區解釋下,會得到不同的時間戳。 我們來看一下整個mysqldump的結果。在文件頭部,可以看到 /*!40103 SET TIME_ZONE='+00:00' */; 字樣,說明mysqldump在默認情況下,是按’+00:00’(中時區). 而mysql客戶端的默認值呢: mysql> select @@time_zone; +-------------+ | @@time_zone | +-------------+ | SYSTEM      | +-------------+   這個SYSTEM表示MySQL取操作系統的默認時區,因此是東8區。如果我們設置為與mysqldump相同時區, mysql> set time_zone='+00:00';   Query OK, 0 rows affected (0.00 sec)   mysql> select * from tb; +---------------------+ | t                   | +---------------------+ | 2012-12-13 16:42:45 | +---------------------+ 1 row in set (0.00 sec) 就跟我們上面看到的全表導出的結果一樣了。 也就是說,這個問題是因為mysqldump強行設置了時區為中時區造成的。   解決1 從mysqldump的代碼中我們看到,可以用 --tz-utc=0 參數去掉前面的設置時區的動作。這樣用的也是默認時區。 mysqldump  --tz-utc=0 -Srun/mysql.sock -uroot   test tb   --where='c="2012-12-14 00:42:45"'  |grep INSERT INSERT INTO `tb` VALUES ('2012-12-14 00:42:45');    www.2cto.com   可以看到,這個貌似就是我們要的結果,導出的結果也很合理。   進一步 如果這個這麼好,為什麼mysqldump的開發者不把—tz-utc=0作為默認行為呢?也就是說哦這樣做有什麼風險? 實際上是因為要防止跨時區導數據。假設你把中國一個機器上的數據導入到美國的一個mysqld(想起@plinux 說的b2b就有這種情況),若不顯式地設置一個時區,在導入時就會出錯了。因為都用系統默認的時區,相同的字符串值會得到不同的時間戳。如我們前面說的, 時間戳是以整型方式存儲的。   解決2 所以上面的--tz-utc=0存在風險。當然如果你確定源和目標系統時區沒變,是ok的。我們討論看看有沒有更保險的方法。 既然是時間戳是保險的,其實可以考慮,用時間戳來做where條件。 mysql> select unix_timestamp(c) from tb; +-------------------+ | unix_timestamp(c) | +-------------------+ |        1355416965 | +-------------------+    www.2cto.com   按照表裡的這個值,我們的dump命令改成 mysqldump   -Srun/mysql.sock -uroot   test tb   --where=' unix_timestamp(c)=1355416965' | grep INSERT INSERT INTO `tb` VALUES ('2012-12-13 16:42:45'); 這次對了,而且與是否使用 --tz-utc=0  無關,都能得到結果,區別只是顯示問題。   不過對MySQL比較熟悉的同學就知道,這個寫法還是存在一個問題:用不上索引,因為我們在字段上做了unix_timestamp這個操作。有時候我們在這種表上為了導出方便有一個索引專門建在timestamp字段上。   因此想到用逆函數 mysqldump   -Srun/mysql.sock -uroot   test tb   --where='c= from_unixtime(1355416965)'  | grep INSERT          INSERT INTO `tb` VALUES ('2012-12-13 16:42:45');  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved