使用 TiFlash

    TiFlash 部署完成后并不会自动同步数据,而需要手动指定需要同步的表。

    用户可以使用 TiDB 或者 TiSpark 读取 TiFlash,TiDB 适合用于中等规模的 OLAP 计算,而 TiSpark 适合大规模的 OLAP 计算,用户可以根据自己的场景和使用习惯自行选择。具体参见:

    TiFlash 接入 TiKV 集群后,默认不会开始同步数据。可通过 MySQL 客户端向 TiDB 发送 DDL 命令来为特定的表建立 TiFlash 副本:

    该命令的参数说明如下:

    • count 表示副本数,0 表示删除。

    对于相同表的多次 DDL 命令,仅保证最后一次能生效。例如下面给出的操作 表的两条 DDL 命令中,只有第二条删除副本的命令能生效:

    为表建立 2 个副本:

    1. ALTER TABLE `tpch50`.`lineitem` SET TIFLASH REPLICA 2

    删除副本:

    1. ALTER TABLE `tpch50`.`lineitem` SET TIFLASH REPLICA 0

    注意事项:

    • 假设有一张表 t 已经通过上述的 DDL 语句同步到 TiFlash,则通过以下语句创建的表也会自动同步到 TiFlash:

      1. CREATE TABLE table_name like t
    • 如果集群版本 \< v4.0.6,若先对表创建 TiFlash 副本,再使用 TiDB Lightning 导入数据,会导致数据导入失败。需要在使用 TiDB Lightning 成功导入数据至表后,再对相应的表创建 TiFlash 副本。

    • 如果集群版本以及 TiDB Lightning 版本均 >= v4.0.6,无论一个表是否已经创建 TiFlash 副本,你均可以使用 TiDB Lightning 导入数据至该表。但注意此情况会导致 TiDB Lightning 导入数据耗费的时间延长,具体取决于 TiDB Lightning 部署机器的网卡带宽、TiFlash 节点的 CPU 及磁盘负载、TiFlash 副本数等因素。

    • 不推荐同步 1000 张以上的表,这会降低 PD 的调度性能。这个限制将在后续版本去除。

    查看表同步进度

    可通过如下 SQL 语句查看特定表(通过 WHERE 语句指定,去掉 WHERE 语句则查看所有表)的 TiFlash 副本的状态:

    1. SELECT * FROM information_schema.tiflash_replica WHERE TABLE_SCHEMA = '<db_name>' and TABLE_NAME = '<table_name>'

    查询结果中:

    • AVAILABLE 字段表示该表的 TiFlash 副本是否可用。1 代表可用,0 代表不可用。副本状态为可用之后就不再改变,如果通过 DDL 命令修改副本数则会重新计算同步进度。
    • PROGRESS 字段代表同步进度,在 0.0~1.0 之间,1 代表至少 1 个副本已经完成同步。

    TiDB 提供三种读取 TiFlash 副本的方式。如果添加了 TiFlash 副本,而没有做任何 engine 的配置,则默认使用 CBO 方式。

    对于创建了 TiFlash 副本的表,TiDB 优化器会自动根据代价估算选择是否使用 TiFlash 副本。具体有没有选择 TiFlash 副本,可以通过 descexplain analyze 语句查看,例如:

    1. desc select count(*) from test.t;
    1. explain analyze select count(*) from test.t;
    1. +--------------------------+---------+---------+--------------+---------------+----------------------------------------------------------------------+--------------------------------+-----------+------+
    2. +--------------------------+---------+---------+--------------+---------------+----------------------------------------------------------------------+--------------------------------+-----------+------+
    3. | StreamAgg_9 | 1.00 | 1 | root | | time:83.8372ms, loops:2 | funcs:count(1)->Column#4 | 372 Bytes | N/A |
    4. | └─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 |
    5. | └─TableFullScan_16 | 1.00 | 1 | cop[tiflash] | table:t | time:43ms, loops:1 | keep order:false, stats:pseudo | N/A | N/A |
    6. +--------------------------+---------+---------+--------------+---------------+----------------------------------------------------------------------+--------------------------------+-----------+------+

    cop[tiflash] 表示该任务会发送至 TiFlash 进行处理。如果没有选择 TiFlash 副本,可尝试通过 analyze table 语句更新统计信息后,再查看 explain analyze 结果。

    需要注意的是,如果表仅有单个 TiFlash 副本且相关节点无法服务,智能选择模式下的查询会不断重试,需要指定 Engine 或者手工 Hint 来读取 TiKV 副本。

    Engine 隔离是通过配置变量来指定所有的查询均使用指定 engine 的副本,可选 engine 为 “tikv”、”tidb” 和 “tiflash”(其中 “tidb” 表示 TiDB 内部的内存表区,主要用于存储一些 TiDB 系统表,用户不能主动使用),分别有 2 个配置级别:

      1. [isolation-read]
      2. engines = ["tikv", "tidb", "tiflash"]

      实例级别的默认配置为 ["tikv", "tidb", "tiflash"]

    1. 会话级别,即 SESSION 级别。设置语句:

      1. set @@session.tidb_isolation_read_engines = "逗号分隔的 engine list";

      或者

      1. set SESSION tidb_isolation_read_engines = "逗号分隔的 engine list";

      会话级别的默认配置继承自 TiDB 实例级别的配置。

    最终的 engine 配置为会话级别配置,即会话级别配置会覆盖实例级别配置。比如实例级别配置了 “tikv”,而会话级别配置了 “tiflash”,则会读取 TiFlash 副本。当 engine 配置为 “tikv, tiflash”,即可以同时读取 TiKV 和 TiFlash 副本,优化器会自动选择。

    如果查询中的表没有对应 engine 的副本,比如配置了 engine 为 “tiflash” 而该表没有 TiFlash 副本,则查询会报该表不存在该 engine 副本的错。

    手工 Hint 可以在满足 engine 隔离的前提下,强制 TiDB 对于某张或某几张表使用指定的副本,使用方法为:

    如果在查询语句中对表设置了别名,在 Hint 语句中必须使用别名才能使 Hint 生效。比如:

    1. 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 语句的语法可以参考 。

    如果 Hint 指定的表在指定的引擎上不存在副本,则 Hint 会被忽略,并产生 warning。另外 Hint 必须在满足 engine 隔离的前提下才会生效,如果 Hint 中指定的引擎不在 engine 隔离列表中,Hint 同样会被忽略,并产生 warning。

    上述三种读取 TiFlash 副本的方式中,Engine 隔离规定了总的可使用副本 engine 的范围,手工 Hint 可以在该范围内进一步实现语句级别及表级别的细粒度的 engine 指定,最终由 CBO 在指定的 engine 范围内根据代价估算最终选取某个 engine 上的副本。

    使用 TiSpark 读取 TiFlash

    TiSpark 目前提供类似 TiDB 中 engine 隔离的方式读取 TiFlash,方式是通过配置参数 。参数值默认为 tikv,tiflash,表示根据 CBO 自动选择从 TiFlash 或从 TiKV 读取数据。如果将该参数值设置成 tiflash,表示强制从 TiFlash 读取数据。

    可以使用以下任意一种方式进行设置:

    1. spark-defaults.conf 文件中添加:

      1. spark.tispark.isolation_read_engines tiflash
    2. 在启动 Spark shell 或 Thrift server 时,启动命令中添加 --conf spark.tispark.isolation_read_engines=tiflash

    3. Spark shell 中实时设置:spark.conf.set("spark.tispark.isolation_read_engines", "tiflash")

    4. Thrift server 通过 beeline 连接后实时设置:set spark.tispark.isolation_read_engines=tiflash

    TiFlash 支持部分算子的下推,支持的算子如下:

    • TableScan:该算子从表中读取数据
    • Selection:该算子对数据进行过滤
    • HashAgg:该算子基于 Hash Aggregation 算法对数据进行聚合运算
    • StreamAgg:该算子基于 算法对数据进行聚合运算。StreamAgg 仅支持不带 GROUP BY 条件的列。
    • TopN:该算子对数据求 TopN 运算
    • Limit:该算子对数据进行 limit 运算
    • Project:该算子对数据进行投影运算
    • HashJoin:该算子基于 Hash Join 算法对数据进行连接运算,但有以下使用条件:
      • 只有在 下才能被下推
      • 必须带有等值的 join 条件

    在 TiDB 中,算子之间会呈现树型组织结构。一个算子能下推到 TiFlash 的前提条件,是该算子的所有子算子都能下推到 TiFlash。因为大部分算子都包含有表达式计算,当且仅当一个算子所包含的所有表达式均支持下推到 TiFlash 时,该算子才有可能下推给 TiFlash。目前 TiFlash 支持下推的表达式包括:

    1. +, -, /, *, >=, <=, =, !=, <, >, ifnull, isnull, bitor, in, bitand, or, and, like, not, case when, month, substr, timestampdiff, date_format, from_unixtime, json_length, if, bitneg, bitxor,
    2. round without fraction, cast(int as decimal), date_add(datetime, int), date_add(datetime, string), min, max, sum, count, avg, approx_count_distinct

    其中,castdate_add 的下推默认不开启,若需要手动开启,请参考优化规则及表达式下推的黑名单

    另外,所有包含 Time/Bit/Set/Enum/Geometry 类型的表达式均不能下推到 TiFlash。

    如查询遇到不支持的下推计算,则需要依赖 TiDB 完成剩余计算,可能会很大程度影响 TiFlash 加速效果。对于暂不支持的算子/表达式,将会在后续版本中陆续提供支持,也可以联系官方沟通。

    使用 MPP 模式

    TiFlash 支持 MPP 模式的查询执行,即在计算中引入跨节点的数据交换(data shuffle 过程)。MPP 模式默认开启,如需关闭可将全局/会话变量 的值设为 0OFF

    1. set @@session.tidb_allow_mpp=0

    MPP 模式目前支持的物理算法有:Broadcast Hash Join、Shuffled Hash Join 和 Shuffled Hash Aggregation。算法的选择由优化器自动判断。通过 EXPLAIN 语句可以查看具体的查询执行计划。

    以 TPC-H 测试集中的表结构为例:

    1. mysql> explain select count(*) from customer c join nation n on c.c_nationkey=n.n_nationkey;
    2. +------------------------------------------+------------+-------------------+---------------+----------------------------------------------------------------------------+
    3. | id | estRows | task | access object | operator info |
    4. +------------------------------------------+------------+-------------------+---------------+----------------------------------------------------------------------------+
    5. | HashAgg_23 | 1.00 | root | | funcs:count(Column#16)->Column#15 |
    6. | └─TableReader_25 | 1.00 | root | | data:ExchangeSender_24 |
    7. | └─ExchangeSender_24 | 1.00 | batchCop[tiflash] | | ExchangeType: PassThrough |
    8. | └─HashAgg_12 | 1.00 | batchCop[tiflash] | | funcs:count(1)->Column#16 |
    9. | └─HashJoin_17 | 3000000.00 | batchCop[tiflash] | | inner join, equal:[eq(tpch.nation.n_nationkey, tpch.customer.c_nationkey)] |
    10. | ├─ExchangeReceiver_21(Build) | 25.00 | batchCop[tiflash] | | |
    11. | └─ExchangeSender_20 | 25.00 | batchCop[tiflash] | | ExchangeType: Broadcast |
    12. | └─TableFullScan_18 | 25.00 | batchCop[tiflash] | table:n | keep order:false |
    13. +------------------------------------------+------------+-------------------+---------------+----------------------------------------------------------------------------+
    14. 9 rows in set (0.00 sec)

    在执行计划中,出现了 ExchangeReceiverExchangeSender 算子。该执行计划表示 nation 表读取完毕后,经过 ExchangeSender 算子广播到各个节点中,与 customer 表先后进行 HashJoinHashAgg 操作,再将结果返回至 TiDB 中。

    TiFlash 提供了两个全局/会话变量决定是否选择 Broadcast Hash Join,分别为:

    • tidb_broadcast_join_threshold_size,单位为 bytes。如果表大小(字节数)小于该值,则选择 Broadcast Hash Join 算法。否则选择 Shuffled Hash Join 算法。
    • ,单位为行数。如果 join 的对象为子查询,优化器无法估计子查询结果集大小,在这种情况下通过结果集行数判断。如果子查询的行数估计值小于该变量,则选择 Broadcast Hash Join 算法。否则选择 Shuffled Hash Join 算法。

    TiFlash 目前尚不支持的一些功能,与原生 TiDB 可能存在不兼容的问题,具体如下:

    • TiFlash 计算层:

      • 不支持检查溢出的数值。例如将两个 BIGINT 类型的最大值相加 9223372036854775807 + 9223372036854775807,该计算在 TiDB 中预期的行为是返回错误 ERROR 1690 (22003): BIGINT value is out of range,但如果该计算在 TiFlash 中进行,则会得到溢出的结果 -2 且无报错。
      • 不支持窗口函数。
      • 不支持从 TiKV 读取数据。
      • 目前 TiFlash 中的 sum 函数不支持传入字符串类型的参数,但 TiDB 在编译时无法检测出这种情况。所以当执行类似于 select sum(string_col) from t 的语句时,TiFlash 会报错 [FLASH:Coprocessor:Unimplemented] CastStringAsReal is not supported.。要避免这类报错,需要手动把 SQL 改写成 select sum(cast(string_col as double)) from t
      • TiFlash 目前的 Decimal 除法计算和 TiDB 存在不兼容的情况。例如在进行 Decimal 相除的时候,TiFlash 会始终按照编译时推断出来的类型进行计算,而 TiDB 则在计算过程中采用精度高于编译时推断出来的类型。这导致在一些带有 Decimal 除法的 SQL 语句在 TiDB + TiKV 上的执行结果会和 TiDB + TiFlash 上的执行结果不一样,示例如下:

        以上示例中,在 TiDB 和 TiFlash 中,a/b 在编译期推导出来的类型都为 Decimal(7,4),而在 Decimal(7,4) 的约束下,a/b 返回的结果应该为 0.0000。但是在 TiDB 中,a/b 运行期的精度比 Decimal(7,4) 高,所以原表中的数据没有被 where a/b 过滤掉。而在 TiFlash 中 a/b 在运行期也是采用 Decimal(7,4) 作为结果类型,所以原表中的数据被 where a/b 过滤掉了。

      • 在配置项 new_collations_enabled_on_first_bootstrap 的值为 true 时,MPP 不支持 join 的连接键类型为字符串或 group by 聚合运算时列类型为字符串的情况。在处理这两类查询时,默认不选择 MPP 模式。