Stale Read(陈旧读)是数据库系统中一种读取历史数据版本的机制,用户可以指定一个具体的历史时间点或者距离当前时间的一个具体时间值来读取历史数据。Stale Read 可提升系统资源利用率,在牺牲一定数据实时性的前提下可降低读取数据的延迟。
TDSQL 提供了语句级别、事务级别、会话级别三种级别的 Stale Read 功能,并且允许用户依据业务情况选择读取数据的副本角色。
数据准备
1. 准备表
t1
,并完成数据准备。desc t1;+-------+------+------+-----+---------+-------+| Field | Type | Null | Key | Default | Extra |+-------+------+------+-----+---------+-------+| id | int | NO | PRI | NULL | || num | int | NO | | NULL | |+-------+------+------+-----+---------+-------+--并且预置下列数据:select * from t1;+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 |+----+-----+
2. 将 Stale Read 的读取节点设置成 Follower 节点。
0
:表示 Leader 节点。1
:表示 Follower 节点。set persist tdsql_stale_read_role=1;
语法级别
提供
AS OF TIMESTAMP <datetime>
语法支持语句级的查询。--查询当前数据select * from t1;+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 |+----+-----+--查询当前时间select now();+---------------------+| now() |+---------------------+| 2025-02-16 17:44:26 |+---------------------+--插入新的数据并查询insert into t1 values(4,40);select * from t1;+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 || 4 | 40 |+----+-----+--查询历史数据select * from t1 as of timestamp '2025-02-16 17:44:26';+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 |+----+-----+
会话级别及事务级别
提供 Session 变量
tdsql_read_staleness_t
,支持会话级别和事务级别的查询。--查询当前数据+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 || 4 | 40 |+----+-----+set tdsql_read_staleness_t='-10'; #读取10秒前的数据--插入新的数据insert into t1 values (5,50);--查看当前时间select now();+---------------------+| now() |+---------------------+| 2025-02-16 17:57:50 |+---------------------+--查询当前数据select * from t1; # 返回历史数据+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 || 4 | 40 |+----+-----+--等待一段时间--查看当前时间select now();+---------------------+| now() |+---------------------+| 2025-02-16 17:58:01 |+---------------------+--再次查询当前数据select * from t1; # 返回新数据+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 || 4 | 40 || 5 | 50 |+----+-----+
设置
tdsql_read_staleness_t
后,还可以开始事务查询数据。--查询当前数据select * from t1;+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 || 4 | 40 || 5 | 50 |+----+-----+--插入新的数据insert into t1 values (6,60);--开启事务begin;select * from t1; # 返回历史数据+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 || 4 | 40 || 5 | 50 |+----+-----+select sum(num) as total_num from t1;+-----------+| total_num |+-----------+| 150 |+-----------+rollback;--等待一段时间--开启事务begin;select * from t1; # 返回新数据+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 || 4 | 40 || 5 | 50 || 6 | 60 |+----+-----+select sum(num) as total_num from t1;+-----------+| total_num |+-----------+| 210 |+-----------+rollback;
注意事项
当前如果查询的历史数据跨越了 DDL,则查询报错。
--先为表t1增加一个新列,并查询数据alter table t1 add column age int not null;select * from t1;+----+-----+-----+| id | num | age |+----+-----+-----+| 1 | 10 | 0 || 2 | 20 | 0 || 3 | 30 | 0 || 4 | 40 | 0 || 5 | 50 | 0 || 6 | 60 | 0 |+----+-----+-----+--查询当前时间select now();+---------------------+| now() |+---------------------+| 2025-02-16 18:10:08 |+---------------------+--移除新加的列,并查询数据alter table t1 drop column age;select * from t1;+----+-----+| id | num |+----+-----+| 1 | 10 || 2 | 20 || 3 | 30 || 4 | 40 || 5 | 50 || 6 | 60 |+----+-----+--查询历史数据select * from t1 as of timestamp '2025-02-16 18:10:08'; # 执行DDL之前的时间--返回错误信息Table or index did not exist at the transaction's timestamp. May be caused by Stale Read or DDL.