MySQL关于timestamp和mysqldump的一个“bug”

本文详细解析了在使用mysqldump导出含有时间戳数据的表时,因时区差异导致数据无法正确导出的问题,并提供了两种解决方案。通过设置--tz-utc=0参数或使用时间戳作为where条件,确保了数据在不同时区间的正确导出。

复现

来源于一个同事在做数据转储碰到的的问题,简化如下:

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)

 

2dump“出错”

  mysqldump   -Srun/mysql.sock -uroot   test tb   --where='c="2012-12-14 00:42:45"' | grep INSERT

返回为空,也就是说导不到数据。

 

 

分析

从上面的结论看上去,似乎是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');

 

可以看到,这个貌似就是我们要的结果,导出的结果也很合理。

 

进一步

如果这个这么好,为什么mysqldump的开发者不把—tz-utc=0作为默认行为呢?也就是说哦这样做有什么风险?

实际上是因为要防止跨时区导数据。假设你把中国一个机器上的数据导入到美国的一个mysqld(想起@plinux 说的b2b就有这种情况),若不显式地设置一个时区,在导入时就会出错了。因为都用系统默认的时区,相同的字符串值会得到不同的时间戳。如我们前面说的, 时间戳是以整型方式存储的。

 

解决2

所以上面的--tz-utc=0存在风险。当然如果你确定源和目标系统时区没变,是ok的。我们讨论看看有没有更保险的方法。

既然是时间戳是保险的,其实可以考虑,用时间戳来做where条件。

mysql> select unix_timestamp(c) from tb;

+-------------------+

| unix_timestamp(c) |

+-------------------+

|        1355416965 |

+-------------------+

 

按照表里的这个值,我们的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');

 

首页/大数据 宝塔MySQL8.0.36有时无法访问(大约15秒左右),目前CPU占用1,如何解决? mysql 数据库 java 求社区用户再支个招,感谢! 宝塔MySQL8.0.36每隔 5 分钟左右没有请求时,再次请求必卡 15 s,之后一切正常。当没有数据请求后隔5分钟左右再次请求会出现上述的问题,目前没有部署项目CPU占用1%,内存为50%。 MySQL配置文件 [client] #password = your_password port = 3306 socket = /tmp/mysql.sock [mysqld] binlog_cache_size = 128K thread_stack = 256K join_buffer_size = 2048K max_heap_table_size = 128M port = 3306 socket = /tmp/mysql.sock datadir = /www/server/data default_storage_engine = InnoDB skip-external-locking key_buffer_size = 128M max_allowed_packet = 100G table_open_cache = 384 sort_buffer_size = 1024K net_buffer_length = 4K read_buffer_size = 1024K read_rnd_buffer_size = 768K myisam_sort_buffer_size = 16M thread_cache_size = 128 tmp_table_size = 128M lower_case_table_names = 1 default_authentication_plugin = mysql_native_password sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES explicit_defaults_for_timestamp = true skip-name-resolve max_connections = 300 max_connect_errors = 100 open_files_limit = 65535 log-bin=mysql-bin binlog_format=mixed server-id = 1 binlog_expire_logs_seconds = 600000 slow_query_log=1 slow-query-log-file=/www/server/data/mysql-slow.log long_query_time=3 #log_queries_not_using_indexes=on early-plugin-load = "" wait_timeout = 300 interactive_timeout = 300 innodb_data_home_dir = /www/server/data innodb_data_file_path = ibdata1:10M:autoextend innodb_log_group_home_dir = /www/server/data innodb_buffer_pool_size = 256M innodb_log_file_size = 128M innodb_log_buffer_size = 32M innodb_flush_log_at_trx_commit = 1 innodb_lock_wait_timeout = 50 innodb_max_dirty_pages_pct = 90 innodb_read_io_threads = 4 innodb_write_io_threads = 4 [mysqldump] quick max_allowed_packet = 500M [mysql] no-auto-rehash [myisamchk] key_buffer_size = 64M sort_buffer_size = 1M read_buffer = 2M write_buffer = 2M [mysqlhotcopy] interactive-timeout = 600 # bt_mysql_set = None # bt_mem_size = None # bt_query_cache_size = None # bt_mysql_set = None # bt_mem_size = None # bt_query_cache_size = None 轻量云4核4G centOs7 宝塔 MySQL8.0.36 若依springboot项目 已试过 my.cnf、TCP keepalive、wait_timeout、interactive_timeout,均无效,求社区用户支支招 希望能够支支招解决每隔五分钟不使用就会卡的问题
最新发布
10-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值