长久以来,MySQL数据库里的时间戳都是用的int(10) unsigned类型,后来看了些别人的代码,发现用timestamp好像也蛮不错的。MySQL的timestamp类型其实内部也是用的一个int来存储的从1970年1月1日至今的秒数,所以无论是索引性能还是存储空间都没什么区别,MySQL文档也有说明,当使用select from_unixtime(timestamp)时,是直接访问的内部秒数,而不是挨个格式化后再反格式化。尽管大体一样,而timestamp类型带来了两点明显的好处:
对于js代码中,new Date(timestamp * 1000)这种乘以1000的写法,总是让人遗忘,而数据库返回的结果直接就是格式化好的,js就只需要做截断或者字符替换操作,不容易出错得多。
于是我将系统全更换成timestamp类型了,然而好景不长,我又全折腾回int unsigned了,因为面对它所带来的两大坏处,它带来的两点好处显得微不足道了:
时区问题,我想了好久,也无法绕过去。中国时区是CST,也就是UTC+8,UTC作为世界统一时间,其实完全可以理解为GMT时间,如果只是针对中国用户,那使用CST时间没有问题,但是如果跨时区了,就必须存储UTC时间,然后各个客户端拿到这一绝对时间秒数后再转换成当地时区所对应的时间,特别像是美国及很多国家,不仅有时区问题,还有夏令时问题,这就更要依靠客户端的时区设置了。像纽约夏令时比北京时间慢12小时,冬令时则慢13小时,每年在切换的前两天,广播、电视就到处通知,要换了要换了,感觉还是中国全国统一比较省事。之前跟印度项目合作,还有相差7.5小时的情况,如果服务端来处理,简直要疯。
其实timestamp内部的int也是使用的UTC绝对秒数,但是在select的时候,会转换成服务器所在时区的时间,这时问题就麻烦了,客户端拿到时间之后,正确的做法是先按服务器所在时区反格式化为绝对秒数,再根据当前时区设置格式化对应时间,这显然非常麻烦。
另一个问题就是性能问题,默认MySQL的时区设置为SYSTEM,即,使用系统时区设置:
select @@time_zone;
+-------------+
| @@time_zone |
+-------------+
| SYSTEM |
+-------------+
这意味着select的每一条数据,都需要查询一下系统的时区设置,然后进行格式化,据说查询的过程还要获取全局锁,这就不仅有CPU的计算耗时问题,还会有阻塞问题。有建议的做法是设置@time_zone='+8',来避免频繁对系统时区的查询,但很显然,这在布署新环境时,会是很容易忘记的事。
很多时候,折腾了一番,才发现此前长期有效的方法反而是最实用的。