PingKai Logo下载

Stale Read

Stale Read 是 TiDB 应用的一种机制,用于读取存储在 TiDB 中的历史版本数据。通过该机制,你可以在特定时间或指定时间范围内读取对应的历史数据,从而节省存储节点之间数据复制带来的延迟。当你使用 Stale Read 时,TiDB 会随机选择一个副本进行数据读取,这意味着所有副本都可以用于数据读取。

在实际应用中,应根据 usage scenarios 小心考虑是否启用 Stale Read。若你的应用不能容忍读取非实时数据,则不应启用。

TiDB 提供三种级别的 Stale Read:语句级、事务级和会话级。

介绍

在 Bookshop 应用中,你可以通过以下 SQL 语句查询最新发布的图书及其价格:

SELECT id, title, type, price FROM books ORDER BY published_at DESC LIMIT 5;

查询结果如下:

+------------+------------------------------+-----------------------+--------+
| id         | title                        | type                  | price  |
+------------+------------------------------+-----------------------+--------+
| 3181093216 | The Story of Droolius Caesar | Novel                 | 100.00 |
| 1064253862 | Collin Rolfson               | Education & Reference |  92.85 |
| 1748583991 | The Documentary of cat       | Magazine              | 159.75 |
|  893930596 | Myrl Hills                   | Education & Reference | 356.85 |
| 3062833277 | Keven Wyman                  | Life                  | 477.91 |
+------------+------------------------------+-----------------------+--------+
5 rows in set (0.02 sec)

此时(2022-04-20 15:20:00),“The Story of Droolius Caesar”的价格为 100.0。

同时,卖家发现该书非常受欢迎,通过以下 SQL 语句将价格提升至 150.0:

UPDATE books SET price = 150 WHERE id = 3181093216;

执行结果如下:

Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

通过查询最新图书列表,可以看到该书的价格已变为 150.00。

+------------+------------------------------+-----------------------+--------+
| id         | title                        | type                  | price  |
+------------+------------------------------+-----------------------+--------+
| 3181093216 | The Story of Droolius Caesar | Novel                 | 150.00 |
| 1064253862 | Collin Rolfson               | Education & Reference |  92.85 |
| 1748583991 | The Documentary of cat       | Magazine              | 159.75 |
|  893930596 | Myrl Hills                   | Education & Reference | 356.85 |
| 3062833277 | Keven Wyman                  | Life                  | 477.91 |
+------------+------------------------------+-----------------------+--------+
5 rows in set (0.01 sec)

如果不需要使用最新数据,可以通过 Stale Read 查询,可能返回过时数据,以避免在强一致性读取期间由数据复制引起的延迟。

假设在 Bookshop 应用中,图书的实时价格在图书列表页并不必要,只在图书详情和订单页面需要。使用 Stale Read 可以提升应用的吞吐量。

Statement level

要在特定时间之前查询图书价格,在上述查询语句中添加 AS OF TIMESTAMP <datetime> 子句。

SELECT id, title, type, price FROM books AS OF TIMESTAMP '2022-04-20 15:20:00' ORDER BY published_at DESC LIMIT 5;

查询结果如下:

+------------+------------------------------+-----------------------+--------+
| id         | title                        | type                  | price  |
+------------+------------------------------+-----------------------+--------+
| 3181093216 | The Story of Droolius Caesar | Novel                 | 100.00 |
| 1064253862 | Collin Rolfson               | Education & Reference |  92.85 |
| 1748583991 | The Documentary of cat       | Magazine              | 159.75 |
|  893930596 | Myrl Hills                   | Education & Reference | 356.85 |
| 3062833277 | Keven Wyman                  | Life                  | 477.91 |
+------------+------------------------------+-----------------------+--------+
5 rows in set (0.01 sec)

除了指定具体时间外,还可以使用以下方式:

  • AS OF TIMESTAMP NOW() - INTERVAL 10 SECOND 查询 10 秒前的最新数据。
  • AS OF TIMESTAMP TIDB_BOUNDED_STALENESS('2016-10-08 16:45:26', '2016-10-08 16:45:29') 查询在 2016-10-08 16:45:26 和 2016-10-08 16:45:29 之间的最新数据。
  • AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(NOW() -INTERVAL 20 SECOND, NOW()) 查询在 20 秒范围内的最新数据。

注意,指定的时间戳或时间间隔不能早于或晚于当前时间。此外,NOW() 默认为秒级精度。若需更高精度,可添加参数,例如 NOW(3) 表示毫秒级。更多信息请参见 MySQL documentation。

过期数据会由 TiDB 中的 Garbage Collection 回收,数据会在短时间内保留后被清除。该时间段称为 GC Life Time (default 10 minutes)。当 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.

通过 SET TRANSACTION READ ONLY AS OF TIMESTAMP 语句,可以将开启的事务或下一次事务设置为基于指定历史时间的只读事务。事务将读取基于提供的历史时间的历史数据。

例如,可以使用以下 AS OF TIMESTAMP 语句,将正在进行的事务切换为只读模式,并读取 5 秒前的历史数据。

SET TRANSACTION READ ONLY AS OF TIMESTAMP NOW() - INTERVAL 5 SECOND;

Session level

为了支持读取历史数据,TiDB 自 v5.4 版本起引入了新的系统变量 tidb_read_staleness,你可以用它设置当前会话允许读取的历史数据范围。其数据类型为 int,作用域为 SESSION。

在会话中启用 Stale Read:

SET @@tidb_read_staleness="-5";

例如,如果设置为 -5,且 TiKV 或 TiFlash 存在对应的历史数据,TiDB 会在 5 秒的时间范围内选择尽可能接近当前时间的时间戳。

在会话中禁用 Stale Read:

set @@tidb_read_staleness="";

阅读更多

需要帮助?

在 Discord 或 Slack 上向社区提问,或 提交支持工单。