title | summary |
---|---|
使用 TiDB 读取 TiFlash |
了解如何使用 TiDB 读取 TiFlash 副本。 |
本文档介绍如何使用 TiDB 读取 TiFlash 副本。
TiDB 提供三种读取 TiFlash 副本的方式。如果添加了 TiFlash 副本,而没有做任何 engine 的配置,则默认使用 CBO 方式。
对于创建了 TiFlash 副本的表,TiDB 优化器会自动根据代价估算选择是否使用 TiFlash 副本。具体有没有选择 TiFlash 副本,可以通过 desc
或 explain analyze
语句查看,例如:
{{< copyable "sql" >}}
desc select count(*) from test.t;
+--------------------------+---------+--------------+---------------+--------------------------------+
| id | estRows | task | access object | operator info |
+--------------------------+---------+--------------+---------------+--------------------------------+
| StreamAgg_9 | 1.00 | root | | funcs:count(1)->Column#4 |
| └─TableReader_17 | 1.00 | root | | data:TableFullScan_16 |
| └─TableFullScan_16 | 1.00 | cop[tiflash] | table:t | keep order:false, stats:pseudo |
+--------------------------+---------+--------------+---------------+--------------------------------+
3 rows in set (0.00 sec)
{{< copyable "sql" >}}
explain analyze select count(*) from test.t;
+--------------------------+---------+---------+--------------+---------------+----------------------------------------------------------------------+--------------------------------+-----------+------+
| id | estRows | actRows | task | access object | execution info | operator info | memory | disk |
+--------------------------+---------+---------+--------------+---------------+----------------------------------------------------------------------+--------------------------------+-----------+------+
| StreamAgg_9 | 1.00 | 1 | root | | time:83.8372ms, loops:2 | funcs:count(1)->Column#4 | 372 Bytes | N/A |
| └─TableReader_17 | 1.00 | 1 | root | | time:83.7776ms, loops:2, rpc num: 1, rpc time:83.5701ms, proc keys:0 | data:TableFullScan_16 | 152 Bytes | N/A |
| └─TableFullScan_16 | 1.00 | 1 | cop[tiflash] | table:t | tiflash_task:{time:43ms, loops:1, threads:1}, tiflash_scan:{...} | keep order:false, stats:pseudo | N/A | N/A |
+--------------------------+---------+---------+--------------+---------------+----------------------------------------------------------------------+--------------------------------+-----------+------+
cop[tiflash]
表示该任务会发送至 TiFlash 进行处理。如果没有选择 TiFlash 副本,可尝试通过 analyze table
语句更新统计信息后,再查看 explain analyze
结果。
需要注意的是,如果表仅有单个 TiFlash 副本且相关节点无法服务,智能选择模式下的查询会不断重试,需要指定 Engine 或者手工 Hint 来读取 TiKV 副本。
Engine 隔离是通过配置变量来指定所有的查询均使用指定 engine 的副本,可选 engine 为 "tikv"、"tidb" 和 "tiflash"(其中 "tidb" 表示 TiDB 内部的内存表区,主要用于存储一些 TiDB 系统表,用户不能主动使用),分别有 2 个配置级别:
-
TiDB 实例级别,即 INSTANCE 级别。在 TiDB 的配置文件添加如下配置项:
[isolation-read] engines = ["tikv", "tidb", "tiflash"]
实例级别的默认配置为
["tikv", "tidb", "tiflash"]
。 -
会话级别,即 SESSION 级别。设置语句:
{{< copyable "sql" >}}
set @@session.tidb_isolation_read_engines = "逗号分隔的 engine list";
或者
{{< copyable "sql" >}}
set SESSION tidb_isolation_read_engines = "逗号分隔的 engine list";
会话级别的默认配置继承自 TiDB 实例级别的配置。
最终的 engine 配置为会话级别配置,即会话级别配置会覆盖实例级别配置。比如实例级别配置了 "tikv",而会话级别配置了 "tiflash",则会读取 TiFlash 副本。当 engine 配置为 "tikv, tiflash",即可以同时读取 TiKV 和 TiFlash 副本,优化器会自动选择。
注意:
由于 TiDB Dashboard 等组件需要读取一些存储于 TiDB 内存表区的系统表,因此建议实例级别 engine 配置中始终加入 "tidb" engine。
如果查询中的表没有对应 engine 的副本,比如配置了 engine 为 "tiflash" 而该表没有 TiFlash 副本,则查询会报该表不存在该 engine 副本的错。
手工 Hint 可以在满足 engine 隔离的前提下,强制 TiDB 对于某张或某几张表使用指定的副本,使用方法为:
{{< copyable "sql" >}}
select /*+ read_from_storage(tiflash[table_name]) */ ... from table_name;
如果在查询语句中对表设置了别名,在 Hint 语句中必须使用别名才能使 Hint 生效。比如:
{{< copyable "sql" >}}
select /*+ read_from_storage(tiflash[alias_a,alias_b]) */ ... from table_name_1 as alias_a, table_name_2 as alias_b where alias_a.column_1 = alias_b.column_2;
其中 tiflash[]
是提示优化器读取 TiFlash 副本,亦可以根据需要使用 tikv[]
来提示优化器读取 TiKV 副本。更多关于该 Hint 语句的语法可以参考 READ_FROM_STORAGE。
如果 Hint 指定的表在指定的引擎上不存在副本,则 Hint 会被忽略,并产生 warning。另外 Hint 必须在满足 engine 隔离的前提下才会生效,如果 Hint 中指定的引擎不在 engine 隔离列表中,Hint 同样会被忽略,并产生 warning。
注意:
MySQL 命令行客户端在 5.7.7 版本之前默认清除了 Optimizer Hints。如果需要在这些早期版本的客户端中使用
Hint
语法,需要在启动客户端时加上--comments
选项,例如mysql -h 127.0.0.1 -P 4000 -uroot --comments
。
上述三种读取 TiFlash 副本的方式中,Engine 隔离规定了总的可使用副本 engine 的范围,手工 Hint 可以在该范围内进一步实现语句级别及表级别的细粒度的 engine 指定,最终由 CBO 在指定的 engine 范围内根据代价估算最终选取某个 engine 上的副本。
注意:
- TiDB 4.0.3 版本之前,在非只读 SQL 语句中(比如
INSERT INTO ... SELECT
、SELECT ... FOR UPDATE
、UPDATE ...
、DELETE ...
)读取 TiFlash,行为是未定义的。- TiDB 4.0.3 到 6.2.0 之间的版本,TiDB 内部会对非只读 SQL 语句忽略 TiFlash 副本以保证数据写入、更新、删除的正确性。对应的,如果使用了智能选择的方式,TiDB 会自动选择非 TiFlash 副本;如果使用了 Engine 隔离的方式指定仅读取 TiFlash 副本,则查询会报错;而如果使用了手工 Hint 的方式,则 Hint 会被忽略。
- TiDB 6.3.0 到 7.0.0 之间的版本,如果 TiFlash 副本被允许使用,你可以通过变量
tidb_enable_tiflash_read_for_write_stmt
控制是否允许 TiDB 在处理非只读 SQL 语句时使用 TiFlash 副本。- 从 TiDB 7.1.0 起,如果 TiFlash 副本被允许使用且当前会话的 SQL 模式为非严格模式(即
sql_mode
值不包含STRICT_TRANS_TABLES
和STRICT_ALL_TABLES
),TiDB 在处理非只读 SQL 语句时会根据代价估算自动选择是否使用 TiFlash 副本。