ntp时钟同步异常(提前、落后)对集群运行有何影响?直接修复到当前时刻有何风险?

【 TiDB 使用环境】生产环境 /测试/ Poc
【 TiDB 版本】Any
【遇到的问题:问题现象及影响】

TiDB是一套分布式数据库系统,需要节点间保证时间的同步,从而确保 ACID 模型的事务线性一致性。目前解决授时的普遍方案是采用 NTP 服务,可以通过互联网中的 pool.ntp.org 授时服务来保证节点的时间同步,也可以使用离线环境自己搭建的 NTP 服务来解决授时。

另外在官方多出文档提到ntp时钟的重要:
1.使用 pd-recover 修复元数据:
pd-recover 不会修改 TSO。因此,在执行此步骤之前,请确保本地时间晚于故障发生时间,并且确认故障前 PD 组件之间已开启 NTP 时钟同步服务。如果未开启,则需要将本地时钟调整到一个未来的时间,以确保 TSO 不会回退。”
2.通过 AS OF TIMESTAMP 语句使用 Stale Read功能来读取 TiDB 历史版本数据:
“使用 Stale Read 时需要为 TiDB 和 PD 节点部署 NTP 服务,防止 TiDB 指定的时间戳超过当前最新的 TSO 分配进度(如几秒后的时间戳),或者落后于 GC safe point 的时间戳。当指定的时间戳超过服务范围,TiDB 会返回错误。”

出现时钟问题的时候:
1.如果是tidb-server,如果出现 select now() 类似的获取当前系统时间的,可能会有异常?
2.如果是pd,集群里面和时钟紧密相关的是 pd leader ,它的TSO 包含 物理时钟+逻辑时钟,TSO用作事务id、数据提交时间等关键内容。 pd leader 的机器时钟有问题时如果是从未来调回到现在,可能会有 TSO 重复的问题?前后事务的id会一样导致数据错乱?
3. 如果是tikv机器,有没有类似的风险点?

2 个赞

这是一个好问题,值得深入思考。
虽然我还不是特别精通,但是阅读官方文档对TSO的描述:

下面示例展示了 TSO 时间戳的二进制细节:

0000011000101000111000010001011110111000110111000000000000000100  ← 该值是二进制形式的 443852055297916932
0000011000101000111000010001011110111000110111                    ← **前 46 位是物理时间戳**
                                              000000000000000100  ← 后 18 位是逻辑时间戳

TSO 时间戳由两部分组成:

  • 物理时间戳:自 1970 年 1 月 1 日以来的 UNIX 时间戳,单位为毫秒。
  • 逻辑时间戳:递增计数器,用于需要在一毫秒内使用多个时间戳的情况,或某些事件可能触发时钟进程逆转的情况。在这些情况下,物理时间戳会保持不变,而逻辑时间戳保持递增。该机制可以确保 TSO 时间戳的完整性,确保时间戳始终递增而不会倒退。

你可以通过 SQL 语句更深入地查看 TSO 时间戳,示例如下:

SELECT @ts, UNIX_TIMESTAMP(NOW(6)), (@ts >> 18)/1000, FROM_UNIXTIME((@ts >> 18)/1000), NOW(6), @ts & 0x3FFFF\G
*************************** 1. row ***************************
                            @ts: 443852055297916932
         UNIX_TIMESTAMP(NOW(6)): 1693161835.502954
               (@ts >> 18)/1000: 1693161221.6870
FROM_UNIXTIME((@ts >> 18)/1000): 2023-08-27 20:33:41.6870
                         NOW(6): 2023-08-27 20:43:55.502954
                  @ts & 0x3FFFF: 4
1 row in set (0.00 sec)

前 46 位是物理时间戳 那么时间回调是不是意味着会导致TSO 变小,从而导致重复的出现?
如果是的话,感觉还是比较危险的。
这让我想起Oracle 10g一个常见的bug,就是时间回调有可能会导致RAC节点重启。

1 个赞

可以往后调,往前调有风险

会造成数据同步风险吧。

如果差的时间不多的话可以停机调整时间

往后调没事,往前就有问题。

1 个赞

ntp时钟异常,这种误差可能导致计算机网络系统变得不稳定,进而影响到各种应用程序的正确运行。尤其是数据备份和恢复,如果时钟不同步,各节点数据不一致,很容易数据恢复故障

时间往前调,tso不会往前。
比如18:00时间调整到17:30,tso会在18点缓慢增长,每秒可以产生2.6亿个时间戳,当时间戳用完了,往前推动一个时间最小单位。不会像正常那样物理时间向前tso也会跟着向前。当时间从17:30恢复到tso用到的时间点,比如到了18:01:00分,tso会继续正常增长。

1 个赞

而且存储层已经存储了很多数据,这些数据的提交信息、各种ID和key可能都包含有 TSO 信息,回调后就有可能导致重复内容,出现一些异常问题。如果涉及到系统的关键元数据,不确定会不会直接导致集群不可用。就是想到了这些,就提出这个问题和大家一起探讨探讨。

我看tidb的说明中已考虑到了时间回调的情况。只有物理时间比老的物理时间大,才会更新。所以应该是理论上物理时间也不会回调。

2 个赞

在单机测试环境测试了一下,无论是时间往前调还是往后调,TSO都是递增的。


MySQL [(none)]> select version();
+--------------------+
| version()          |
+--------------------+
| 8.0.11-TiDB-v7.5.0 |
+--------------------+
1 row in set (0.01 sec)

MySQL [(none)]> 

MySQL [(none)]> BEGIN; SET @ts := @@tidb_current_ts; ROLLBACK;
Query OK, 0 rows affected (0.21 sec)

Query OK, 0 rows affected (0.55 sec)

Query OK, 0 rows affected (0.02 sec)

MySQL [(none)]> SELECT @ts;
+--------------------+
| @ts                |
+--------------------+
| 446974111762087941 |
+--------------------+
1 row in set (0.20 sec)

MySQL [(none)]> SELECT @ts, UNIX_TIMESTAMP(NOW(6)), (@ts >> 18)/1000, FROM_UNIXTIME((@ts >> 18)/1000), NOW(6), @ts & 0x3FFFF\G
*************************** 1. row ***************************
                            @ts: 446974111762087941
         UNIX_TIMESTAMP(NOW(6)): 1705070942.989248
               (@ts >> 18)/1000: 1705070921.9440
FROM_UNIXTIME((@ts >> 18)/1000): 2024-01-12 22:48:41.9440
                         NOW(6): 2024-01-12 22:49:02.989248
                  @ts & 0x3FFFF: 5
1 row in set (0.75 sec)

MySQL [(none)]> exit
Bye
[root@localhost ~]# date -s 20240122
Mon Jan 22 00:00:00 CST 2024
[root@localhost ~]# mylogin
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 507513162
Server version: 8.0.11-TiDB-v7.5.0 TiDB Server (Apache License 2.0) Community Edition, MySQL 8.0 compatible

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> BEGIN; SET @ts := @@tidb_current_ts; ROLLBACK;
Query OK, 0 rows affected (0.29 sec)

Query OK, 0 rows affected (0.11 sec)

Query OK, 0 rows affected (0.11 sec)

MySQL [(none)]> SELECT @ts, UNIX_TIMESTAMP(NOW(6)), (@ts >> 18)/1000, FROM_UNIXTIME((@ts >> 18)/1000), NOW(6), @ts & 0x3FFFF\G
*************************** 1. row ***************************
                            @ts: 447179080218968065
         UNIX_TIMESTAMP(NOW(6)): 1705852821.146757
               (@ts >> 18)/1000: 1705852814.5560
FROM_UNIXTIME((@ts >> 18)/1000): 2024-01-22 00:00:14.5560
                         NOW(6): 2024-01-22 00:00:21.146757
                  @ts & 0x3FFFF: 1
1 row in set (0.13 sec)

MySQL [(none)]> exit
Bye
[root@localhost ~]# date -s 20230101
Sun Jan  1 00:00:00 CST 2023
[root@localhost ~]# mylogin
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 507513164
Server version: 8.0.11-TiDB-v7.5.0 TiDB Server (Apache License 2.0) Community Edition, MySQL 8.0 compatible

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> BEGIN; SET @ts := @@tidb_current_ts; ROLLBACK;
Query OK, 0 rows affected (0.08 sec)

Query OK, 0 rows affected (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

MySQL [(none)]> SELECT @ts, UNIX_TIMESTAMP(NOW(6)), (@ts >> 18)/1000, FROM_UNIXTIME((@ts >> 18)/1000), NOW(6), @ts & 0x3FFFF\G
*************************** 1. row ***************************
                            @ts: 447179087651799401
         UNIX_TIMESTAMP(NOW(6)): 1672502421.509376
               (@ts >> 18)/1000: 1705852842.9100
FROM_UNIXTIME((@ts >> 18)/1000): 2024-01-22 00:00:42.9100
                         NOW(6): 2023-01-01 00:00:21.509376
                  @ts & 0x3FFFF: 361
1 row in set (0.01 sec)

MySQL [(none)]> select now();
+---------------------+
| now()               |
+---------------------+
| 2023-01-01 00:02:54 |
+---------------------+
1 row in set (0.00 sec)

MySQL [(none)]> exit
[root@localhost ~]# date -s 20240122
Mon Jan 22 00:00:00 CST 2024
[root@localhost ~]# date -s 22:55
Mon Jan 22 22:55:00 CST 2024
[root@localhost ~]# mylogin
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 507513206
Server version: 8.0.11-TiDB-v7.5.0 TiDB Server (Apache License 2.0) Community Edition, MySQL 8.0 compatible

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> BEGIN; SET @ts := @@tidb_current_ts; ROLLBACK;
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

MySQL [(none)]> SELECT @ts, UNIX_TIMESTAMP(NOW(6)), (@ts >> 18)/1000, FROM_UNIXTIME((@ts >> 18)/1000), NOW(6), @ts & 0x3FFFF\G
*************************** 1. row ***************************
                            @ts: 447200705026457602
         UNIX_TIMESTAMP(NOW(6)): 1705935308.874718
               (@ts >> 18)/1000: 1705935306.6500
FROM_UNIXTIME((@ts >> 18)/1000): 2024-01-22 22:55:06.6500
                         NOW(6): 2024-01-22 22:55:08.874718
                  @ts & 0x3FFFF: 2
1 row in set (0.00 sec)

3 个赞

PD 发生时间回退时,TSO不会回退,会推进逻辑位,如果短期内 TSO 请求太多,会导致逻辑位使用不够的问题,这个时候需要等一个 interval 更新物理位,对应到 TSO 的请求延时会高一下。

Tikv和pd需要测试下

2 个赞

这种问题深究原理的话,建议读下分布式系统 相关的书籍。比如关于时间 时钟 事件顺序章节

时间回调虽然不会导致TSO回退,但是还是有可能引发一些逻辑问题,比如下面的实验,我将时间手工调整到2024年1月22日(实验当天正确时间为2024年1月14日),然后再回调回来,发现TSO时间大幅增加后,再做快照备份就出错了。
参考实验

MySQL [(none)]> SELECT TIDB_PARSE_TSO(447201033313058816);
+------------------------------------+
| TIDB_PARSE_TSO(447201033313058816) |
+------------------------------------+
| 2024-01-22 23:15:58.964000         |
+------------------------------------+
1 row in set (0.00 sec)

MySQL [(none)]> exit
Bye
[root@localhost ~]# date
Sun Jan 14 22:53:16 CST 2024
[root@localhost ~]# tiup br backup full \
>     --pd "192.168.0.100:2379" \
>     --backupts '2024-01-14 22:46:00' \
>     --storage "local:///tmp/backup5" \
>     --ratelimit 128 \
>     --log-file backupfull20240114.log
tiup is checking updates for component br ...
Starting component `br`: /root/.tiup/components/br/v7.5.0/br backup full --pd 192.168.0.100:2379 --backupts 2024-01-14 22:46:00 --storage local:///tmp/backup5 --ratelimit 128 --log-file backupfull20240114.log

Detail BR log in backupfull20240114.log 
[2024/01/14 22:53:41.761 +08:00] [WARN] [backup.go:312] ["setting `--ratelimit` and `--concurrency` at the same time, ignoring `--concurrency`: `--ratelimit` forces sequential (i.e. concurrency = 1) backup"] [ratelimit=134.2MB/s] [concurrency-specified=4]
[2024/01/14 22:53:46.255 +08:00] [INFO] [collector.go:77] ["Full Backup failed summary"] [total-ranges=0] [ranges-succeed=0] [ranges-failed=0]
Error: GC safepoint 447201033313058816 exceed TS 447019367792640000: [BR:Backup:ErrBackupGCSafepointExceeded]backup GC safepoint exceeded
[root@localhost ~]# 

1 个赞

看着像是 GC safepoint 已经推进到未来的时间了 2024-01-22 15:15:58.964000,其比当前时间 2024-01-14 14:46:00.000000 还要早,所以出现了备份异常的问题。

可以确认下整个集群系统的GC 时间,看看是不是已经推到未来的一个时间点去了

1 个赞

可以配置ntp慢慢追评

命中率的问题,有风险。

pd的ntpd同步也应该是个监控项。

往后调有个已知问题:https://github.com/pingcap/tidb/issues/38248

2 个赞

太难了,前后都有问题

是啊,这个问题感觉如果控制不好,容易出问题