工作经历中见过一些平时很难注意到的细节,从而导致故障的场景。所以新开 这也能造成故障 系列,防微杜渐,安全生产无小事,要敬畏每一行代码。
datetime类型毫秒级四舍五入引发的问题,可能会造成数据写入后,实际读出的时间会多一秒。从而在大流量且对于时间匹配的场景下,极有可能造成资损类型的故障。下面我们来回顾如何发生的?
建立测试MySQL数据表如下:
mysql> describe test_date;
+
| Field | Type | Null | Key | Default | Extra |
+
| id | int(11) | NO | PRI | NULL | |
| date_0 | datetime | YES | | NULL | |
| date_3 | datetime(3) | YES | | NULL | |
| date_6 | datetime(6) | YES | | NULL | |
+
插入测试数据:
insert into test_date values (1, '2022-09-30 23:59:59', '2022-09-30 23:59:59','2022-09-30 23:59:59');
insert into test_date values (2, '2022-09-30 23:59:59.000', '2022-09-30 23:59:59.000','2022-09-30 23:59:59.000');
insert into test_date values (3, '2022-09-30 23:59:59.499', '2022-09-30 23:59:59.499','2022-09-30 23:59:59.499');
insert into test_date values (4, '2022-09-30 23:59:59.500', '2022-09-30 23:59:59.500','2022-09-30 23:59:59.500');
insert into test_date values (5, '2022-09-30 23:59:59.998', '2022-09-30 23:59:59.998','2022-09-30 23:59:59.998');
insert into test_date values (6, '2022-09-30 23:59:59.9999', '2022-09-30 23:59:59.9999','2022-09-30 23:59:59.9999');
insert into test_date values (7, '2022-09-30 23:59:59.999989', '2022-09-30 23:59:59.999989','2022-09-30 23:59:59.999989');
insert into test_date values (8, '2022-09-30 23:59:59.999999', '2022-09-30 23:59:59.999999','2022-09-30 23:59:59.999999');
insert into test_date values (9, '2022-09-30 23:59:59.9999899', '2022-09-30 23:59:59.9999899','2022-09-30 23:59:59.9999899');
insert into test_date values (10, '2022-09-30 23:59:59.9999999', '2022-09-30 23:59:59.9999999','2022-09-30 23:59:59.9999999');
再次查询,结果如下:
mysql> select * from test_date;
+
| id | date_0 | date_3 | date_6 |
+
| 1 | 2022-09-30 23:59:59 | 2022-09-30 23:59:59.000 | 2022-09-30 23:59:59.000000 |
| 2 | 2022-09-30 23:59:59 | 2022-09-30 23:59:59.000 | 2022-09-30 23:59:59.000000 |
| 3 | 2022-09-30 23:59:59 | 2022-09-30 23:59:59.499 | 2022-09-30 23:59:59.499000 |
| 4 | 2022-10-01 00:00:00 | 2022-09-30 23:59:59.500 | 2022-09-30 23:59:59.500000 |
| 5 | 2022-10-01 00:00:00 | 2022-09-30 23:59:59.998 | 2022-09-30 23:59:59.998000 |
| 6 | 2022-10-01 00:00:00 | 2022-10-01 00:00:00.000 | 2022-09-30 23:59:59.999900 |
| 7 | 2022-10-01 00:00:00 | 2022-10-01 00:00:00.000 | 2022-09-30 23:59:59.999989 |
| 8 | 2022-10-01 00:00:00 | 2022-10-01 00:00:00.000 | 2022-09-30 23:59:59.999999 |
| 9 | 2022-10-01 00:00:00 | 2022-10-01 00:00:00.000 | 2022-09-30 23:59:59.999990 |
| 10 | 2022-10-01 00:00:00 | 2022-10-01 00:00:00.000 | 2022-10-01 00:00:00.000000 |
+
对比测试用例和结果,就很明显了:发生了四舍五入的进位,从而导致插入的时间与查询的时间不匹配。
那么在实际应用中,可能只会具体到时分秒来匹配查询,但是查出来的时间却不一样,这样就会导致后续链路产生问题。
我们再看下 MySQL 文档中对此的描述: 官方文档链接如下:5.7 插入时间数据的小数位大于字段的精度时,静默四舍五入。所以以后需要注意此类问题。
当然你可能会问:为什么很少见到这个问题呢?
刚才也提到了,大流量且对时间敏感的场景,才有可能发生这个问题。
本次故障引发的逻辑就是: MySQL中字段为 Datetime(0),不包含小数位。且从时间数值是由前端一路经过各种服务,未经额外加工头传到后端的,一直未出现问题。 但是当前端更换组件时(或调整逻辑),导致了传递的时间参数中毫秒不再是0。这就变成了导火索。
是不是很可怕,前端同学仅仅改了下组件,毫秒是不是0又有什么关系呢?但是最终却造成了问题。当然不会是前端背锅,但是这个问题提醒了我们: 往往故障都是由小细节引发的,且经过链路的放大,最终可能升级为大的资损故障 。
敬畏每一行代码,每一项变更。
|