Follower Read
- tidb_replica_read
tidb_replica_read: 用于控制TiDB读取数据的位置
tidb_replica_read: 用于控制TiDB读取数据的位置,通常用leader上读,这个参数控制可以在flower上读。
在 TiDB 当中,数据是以 Region 为单位,分散在集群中所有的节点上进行存储的。一个 Region 可以存在多个副本,副本又分为一个 leader 和多个 follower。当 leader 上的数据发生变化时,TiDB 会将数据同步更新到 follower。
默认情况下,TiDB 只会在同一个 Region 的 leader 上读写数据。当系统中存在读取热点 Region 导致 leader 资源紧张成为整个系统读取瓶颈时,启用 Follower Read 功能可明显降低 leader 的负担,并且通过在多个 follower 之间均衡负载,显著地提升整体系统的吞吐能力。
优化读热点
你可以在 TiDB Dashboard 流量可视化页面当中通过可视化的方法分析你的应用程序是否存在热点 Region。
如果读取热点的确无法避免或者改动的成本很大,你可以尝试通过 Follower Read 功能将读取请求更好的负载均衡到 follower region。
开启 Follower Read
在 SQL 中,你可以将变量 tidb_replica_read 的值(默认为 leader)设置为 follower、leader-and-follower、prefer-leader、closest-replicas 或 closest-adaptive 开启 TiDB 的 Follower Read 功能:
SET [GLOBAL|session] tidb_replica_read = 'follower';
# 查看当前在哪读,默认是leader
SELECT @@tidb_replica_read;
模拟本地读的效果
mysql> select @@tidb_replica_read;
+---------------------+
| @@tidb_replica_read |
+---------------------+
| leader |
+---------------------+
1 row in set (0.00 sec)
mysql> set session tidb_replica_read = 'follower';
Query OK, 0 rows affected (0.01 sec)
mysql>
mysql>
mysql> select @@tidb_replica_read;
+---------------------+
| @@tidb_replica_read |
+---------------------+
| follower |
+---------------------+
1 row in set (0.00 sec)
mysql> select count(*),max(t_mark),min(t_mark),now() from test.t_west;
+----------+---------------------+---------------------+---------------------+
| count(*) | max(t_mark) | min(t_mark) | now() |
+----------+---------------------+---------------------+---------------------+
| 81920 | 2023-07-05 06:41:49 | 2023-07-05 06:41:31 | 2023-07-05 06:43:26 |
+----------+---------------------+---------------------+---------------------+
Stale Read
语句级别
- 闪回表查询
`AS OF TIMESTAMP <datetime>`
AS OF TIMESTAMP NOW() - INTERVAL 10 SECOND 表示读取 10 秒前最新的数据。
/* 10 seconds before*/
# 这个操作是闪回表的操作,查询10秒之前的表数据
select * from test.t_west as of timestamp(now()-10);
SELECT id, title, type, price FROM books AS OF TIMESTAMP '2022-04-20 15:20:00' ORDER BY published_at DESC LIMIT 5;
- tidb_bounded_staleness
函数 tidb_bounded_staleness: 在指定时间的范围内尽可能地读取最新数据,函数TIDB_BOUNDED_STALENESS: 在时间范围内尽可能地读取最新数据。例如由于跨地域带宽的问题,数据可能不一致,此时从follow上读的时候,时间允许的范围。
AS OF TIMESTAMP TIDB_BOUNDED_STALENESS('2016-10-08 16:45:26', '2016-10-08 16:45:29'): 表示读取在 2016 年 10 月 8 日 16 点 45 分 26 秒到 29 秒的时间范围内尽可能新的数据。
AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(NOW() - INTERVAL 20 SECOND, NOW()): 表示读取 20 秒前到现在的时间范围内尽可能新的数据。
- 注意事项
- 设定的时间戳或时间戳的范围不能过早 或 晚于当前时间。
- 过期的数据在 TiDB 当中会由垃圾回收器进行回收,数据在被清除之前会被保留一小段时间,这段时间被称为 GC Life Time (默认 10 分钟)。每次进行 GC 时,将以当前时间减去该时间周期的值作为 GC Safe Point。
- 如果尝试读取 GC Safe Point 之前数据,TiDB 会报如下错误:
ERROR 9006 (HY000): GC life time is shorter than transaction duration…
如果给出的时间戳是一个未来的时间节点,TiDB 会报如下错误:
ERROR 9006 (HY000): cannot set read timestamp to a future time.
语句级别示例
由于follower region可能不是最新数据,此时又想通过查询follower的region来降低热点的频率,此时可以通过设置TIDB_BOUNDED_STALENESS 来读取指定时间范围当中的最新数据。
set session tidb_replica_read = 'follower';
# 前20秒<= time < 前5秒 ,读取设个时间范围内最新的数据
select * from test.t_west as of timestamp TIDB_BOUNDED_STALENESS(now()-20,now()-5)
# 设置当前会话在 flower中读。
SET SESSION tidb_replica_read = 'follower';
/* Between 20 seconds before and 10 seconds before */
AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(@FLOOR, @CEIL);
mysql> SET @FLOOR = (select min(t_mark) from test.t_west);
Query OK, 0 rows affected (0.10 sec)
mysql> SET @CEIL = (select max(t_mark) from test.t_west);
Query OK, 0 rows affected (0.09 sec)
SELECT count(*), max(t_mark), min(t_mark), now()
FROM test.t_west
AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(@FLOOR, @CEIL);
mysql> SELECT count(*), max(t_mark), min(t_mark), now()
-> FROM test.t_west
-> AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(@FLOOR, @CEIL);
+----------+---------------------+---------------------+---------------------+
| count(*) | max(t_mark) | min(t_mark) | now() |
+----------+---------------------+---------------------+---------------------+
| 40960 | 2023-07-05 06:41:46 | 2023-07-05 06:41:31 | 2023-07-05 06:46:40 |
+----------+---------------------+---------------------+---------------------+
1 row in set (0.05 sec)
mysql> SELECT count(*), max(t_mark), min(t_mark), now()
-> FROM test.t_west
-> AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(NOW()-10, NOW());
+----------+---------------------+---------------------+---------------------+
| count(*) | max(t_mark) | min(t_mark) | now() |
+----------+---------------------+---------------------+---------------------+
| 81920 | 2023-07-05 06:41:49 | 2023-07-05 06:41:31 | 2023-07-05 06:47:15 |
+----------+---------------------+---------------------+---------------------+
1 row in set (0.00 sec)
事务级别
通过 START TRANSACTION READ ONLY AS OF TIMESTAMP
语句,可以开启一个基于历史时间的只读事务,该事务基于所提供的历史时间来读取历史数据。
示例如下:
START TRANSACTION READ ONLY AS OF TIMESTAMP NOW() - INTERVAL 5 SECOND;
通过 SET TRANSACTION READ ONLY AS OF TIMESTAMP
语句,可以将当前事务或下一个事务设置为基于指定历史时间的只读事务。该事务将会基于所提供的历史时间来读取历史数据。
通过下面这个 SQL 将已开启的事务切换到只读模式,通过 AS OF TIMESTAMP 语句开启能够读取 5 秒前的历史数据 Stale Read 功能。
SET TRANSACTION READ ONLY AS OF TIMESTAMP NOW() - INTERVAL 5 SECOND;
会话级别
变量 tidb_read_staleness 用于设置当前会话允许读取的历史数据范围,其数据类型为 int,作用域为 SESSION。
在会话中开启 Stale Read:
SET @@tidb_read_staleness="-5";
如果该变量的值设置为 -5,TiDB 会在 5 秒时间范围内,保证 TiKV 或者 TiFlash 拥有对应历史版本数据的情况下,选择尽可能新的一个时间戳。
关闭会话当中的 Stale Read:
set @@tidb_read_staleness="";