MySQL 尽量避免使用 TIMESTAMP!!

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的一些设计却非常鬼畜,比如:

  • 如果表中包含TIMESTAMP的列,那么其建表语句有可能被系统篡改,取决于MySql的版本和参数设置。

  • MySQL参数time_zone=system时,高并发可能会引起CPU使用率暴涨,系统响应变慢甚至假死

  • 如果存入超过范围的时间,在非严格状态下,MySql不会报错,反而会插入'0000-00-00 00:00:00'

新建一个包含TIMESTAMP的表可真难


MySql 5.6.6版本引入了explicit_defaults_for_timestamp这个参数,随即被标记为废弃,这个参数主要影响表中类型为TIMESTAMP的那些列在新建表时的表现

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)

虽然我们输入的建表语句很简单,但是MySql却对于我们输入的建表语句做了诸多的篡改:

  • 对于表中的第一个TIMESTAMP列,系统自动加了NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,这些操作对于新建表的开发者完全是不感知的。

  • 对于表中的第二个TIMESTAMP列,系统自动加了一个默认值0000-00-00 00:00:00,这个操作同样对于新建表的开发者完全不感知。

在系统对我们的建表语句做了自动修改之后,对表的插入操作可能就不会如开发者预期的那样:

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的表现非常的鬼畜

  • 对于第一个TIMESTAMP列,建表语句中指定可以为null,但是插入null的时候存到表里的却是当前时间

  • 对于第二个TIMESTAMP列,虽然通过语句show create table t1\G查出来的建表语句指定的默认值是'0000-00-00 00:00:00'但是存到表里的却是当前时间

  • 最奇怪的是第三个TIMESTAMP列,尽管我们显式指定默认值为'2010-01-01 00:00:00',但是落表的时间仍然是当前时间

这一切都是在参数explicit_defaults_for_timestamp被设置为OFF的时候发生的,但是遗憾的是OFF恰恰就是参数explicit_defaults_for_timestamp的默认值。

如果我们将 explicit_defaults_for_timestamp 的值改为 ON ,则事情会变得好很多

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)

这一次,建表语句中那些奇怪的默认值都没有了,清爽了好多,而且TIMESTAMP的的列也可以插入NULL了,如果我们显式指定了NOT NULLSTRICT_TRANS_TABLES被指定的情况下直接报错,如果STRICT_TRANS_TABLES没有被指定,那么会向该列中插入0000-00-00 00:00:00并且产生一个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 |

±-----±-----±--------------------+

高并发环境下并不适合使用TIMESTAMP


这一点MySql的文档中有明确的说明:

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.

虽然通过TIMESTAMP可以自动转换时区,代价是当MySQL参数time_zone=system时每次都会尝试获取一个全局锁,这在高并发的环境下无疑是致命的,可能会导致线程上下文频繁切换,CPU使用率暴涨,系统响应变慢甚至假死。点击这个Java面试库小程序可以在线刷题。

时间范围并不是强校验的


如果我们尝试往MySql中插入超过TIMESTAMP可表示的时间范围的值,MySql在非严格模式下并不会报错,仅会产生一个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)

相关推荐
阿巴斯甜17 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker17 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952718 小时前
Andorid Google 登录接入文档
android
黄林晴19 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android