概述
tiflash的升级与扩缩容已经有很多同学实践了,随着tidb的普及,这方面也不适合连篇累牍的描述,但是在这次测试6.0 on k8s的过程中也确实遇到了一些坎坷。本文首先讲解tiflash 6.0 on k8s扩容的注意事项,然后描述在新扩容的机器上的一些新特性实践。此次发布的新特性有不少处在实验特性的阶段,据说会在6.1 GA,生产环境可以等待6.1直接升级。
tiflash 6.0 on k8s扩容
我们有多个tidb环境,一个实验性tidb环境在k8s上,一个开发tidb环境用的虚拟机部署的,给研发的同学提供支撑,一个是生产环境。开发环境为了保持研发同学的一致性体验,暂时不升级6.0,使用实验性tidb环境进行测试。实验性tidb环境已经升级为6.0,但是没有部署tiflash,tidb on k8s的升级暂不描述。
扩容tiflash 6.0 on k8s
按照官方文档描述,在tc中增加tiflash的配置:
kubectl edit tc basic -n tidb-cluster
在spec.timezone: UTC之前增加tiflash配置:
tiflash:
baseImage: uhub.service.ucloud.cn/pingcap/tiflash
maxFailoverCount: 3
replicas: 3
storageClaims:
- resources:
requests:
storage: 10Gi
其中,需要注意的是tiflash的存储设置稍有不同,要配置成如下形式:
storageClaims:
- resources:
requests:
storage: 10Gi
如此配置是因为tiflash支持挂载多个pv,如果要为tiflash配置多个PV,可以在 tiflash.storageClaims下面配置多项,每一项可以分别配置storage request和storageClassName,例如:
tiflash:
baseImage: pingcap/tiflash
maxFailoverCount: 0
replicas: 1
storageClaims:
- resources:
requests:
storage: 100Gi
storageClassName: local-storage
- resources:
requests:
storage: 100Gi
storageClassName: local-storage
core dump设置过大错误
保存当前配置后,等待pod部署,此时会发现tifalsh起不来,日志报错如下:
Poco::Exception. Code: 1000, e.code() = 0, e.displayText() = Exception: Cannot set max size of core file to 1073741824, e.what() = Exception
从Rancher的console上看,如下图: 在社区求助获取大神的指点后,翻阅源码发现问题:
struct rlimit rlim;
if (getrlimit(RLIMIT_CORE, &rlim))
throw Poco::Exception("Cannot getrlimit");
/// 1 GiB by default. If more - it writes to disk too long.
rlim.rlim_cur = config().getUInt64("core_dump.size_limit", 1024 * 1024 * 1024);
if (setrlimit(RLIMIT_CORE, &rlim))
{
std::string message = "Cannot set max size of core file to " + std::to_string(rlim.rlim_cur);
#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER) && !defined(SANITIZER)
throw Poco::Exception(message);
#else
/// It doesn't work under address/thread sanitizer. http://lists.llvm.org/pipermail/llvm-bugs/2013-April/027880.html
std::cerr << message << std::endl;
#endif
}
简单说明就是tiflash启动时,需要设置core dump的大小为1G,经过一些调试,获取容器启动时默认的限制为:
[root@host ~]# ulimit -c
1024
由于超过了系统限制所以报错且无法启动。 此时需要在/etc/systemd/system/docker.service.d/中增加: limit-core.conf 内容:
[Service]
LimitCORE=infinity
然后执行如下命令:
systemctl daemon-reload
systemctl restart docker.service
默认配置格式错误
上述操作重启后,发现上述错误已经没有了,但是在日志中出现了新的错误: tiflash:
[2022/05/16 07:53:23.808 +00:00] [INFO] [mod.rs:118] ["encryption: none of key dictionary and file dictionary are found."]
[2022/05/16 07:53:23.808 +00:00] [INFO] [mod.rs:479] ["encryption is disabled."]
[2022/05/16 07:53:23.808 +00:00] [INFO] [server.rs:231] ["set raft-store proxy helper"]
[2022/05/16 07:53:23.808 +00:00] [INFO] [server.rs:231] ["wait for engine-store server to start"]
[2022/05/16 07:53:24.009 +00:00] [INFO] [server.rs:231] ["engine-store server is not running, make proxy exit"]
errorlog:
[2022/05/16 07:53:07.000 +00:00] [ERROR] [StorageConfigParser.cpp:73] ["Application:The configuration \"storage.raft.dir\" should be an array of strings. Please check your configuration file."] [thread_id=1]
[2022/05/16 07:53:07.100 +00:00] [ERROR] [<unknown>] ["Application:DB::Exception: The configuration \"storage.raft.dir\" should be an array of strings. Please check your configuration file."] [thread_id=1]
推断是configmap中的storage.raft.dir配置有问题,执行:
kubectl edit tc basic -n tidb-cluster
修改tiflash的配置如下:
tiflash:
baseImage: uhub.service.ucloud.cn/pingcap/tiflash
config:
config: |
[storage]
[storage.main]
dir = ["/data0/db"]
[storage.raft]
dir = ["/data0/kvstore"]
maxFailoverCount: 3
replicas: 3
storageClaims:
- resources:
requests:
storage: 10Gi
增加config,其中注意:
- 两次嵌套config
- storage下面如果有配置,需要全部在配置中指定,例如下面的配置,就会漏掉storage.main:
config:
config: |
[storage]
[storage.raft]
dir = ["/data0/kvstore"]
上述配置是错误示范,不要拷贝。 等待pod重新部署之后,tiflash成功启动。 查看实例,已经部署成功:
TiFlash 6.0 新特性解读
6.0版本下TiFlash新增特性如下:
- 新增按库构建 TiFlash 副本功能。用户仅需使用一条 SQL 即可对某一个数据库中所有的表添加 TiFlash 副本,极大地节约了运维成本。使用文档
- TiFlash MPP 引擎支持分区表的动态裁剪模式(实验特性)。使用文档 在该模式下,TiDB 也可以使用 TiFlash MPP 引擎读取和计算分区表的数据,从而大大提升分区表的查询性能。
- TiFlash 新增支持 zstd 压缩算法。使用文档 新增 profiles.default.dt_compression_method 和 profiles.default.dt_compression_level 两个参数,用户可根据对性能和容量的平衡,选择不同的压缩算法。
- TiFlash 默认开启支持所有 I/O 的校验 (Checksum)。使用文档 此项功能曾作为实验特性在 v5.4 释出。除增强了数据的正确性安全性外,对用户使用不产生明显的直接影响。
- TiFlash 引入异步 gRPC 和 Min-TSO 调度机制,更好的管理线程使用,防止线程数过高导致的系统崩溃。使用文档
其中,按库构建 TiFlash 副本,TiFlash MPP 引擎支持分区表正好符合了作者的需求,此文中做一些评测,加入数据和效果展示,方便各位评估。
新增按库构建 TiFlash 副本功能
按库构建的方便之处
我们设置数据层次如下所示: 其实从明细层开始,就已经是tiflash比较喜欢的宽表了,对于明细、汇总、萃取层的数据我们期望都用tidb体系替代,所以这三层中的表都希望有tiflash副本。
操作实践
测试环境描述
构建一个测试库,库中有23账表,其中最大的表有3千万数据,为非分区表,另外还有一张分区表,180万数据,7张空表,没有数据。
执行按库构建tiflash副本
ALTER DATABASE TESTDB SET TIFLASH REPLICA 1;
脚本交互过程长达30秒,时间并不短,看看副本同步情况: 所有的表都已经创建了tiflash副本,包括空表,部分大表已经开始同步,目前空表和部分小表的AVAILABLE和PROGRESS都为0,等待同步完成之后,再查看状态: 全部的表都建立了tiflash副本。 此时新建一张表:
create table adcm_p2 like adcm_p;
查看tiflash表的数量为24张,新建的表也创建了tiflash副本。 官方文档提示如下:
- 该命令实际是为用户执行一系列 DDL 操作,对资源要求比较高。如果在执行过程中出现中断,已经执行成功的操作不会回退,未执行的操作不会继续执行。
- 从命令执行开始到该库中所有表都已同步完成之前,不建议执行和该库相关的tiflash副本数量设置或其他DDL操作,否则最终状态可能非预期。非预期场景包括:
- 先设置tiflash副本数量为2,在库中所有的表都同步完成前,再设置tiflash副本数量为 1,不能保证最终所有表的tiflash副本数量都为 1 或都为 2。
- 在命令执行到结束期间,如果在该库下创建表,则可能会对这些新增表创建tiflash副本。
- 在命令执行到结束期间,如果为该库下的表添加索引,则该命令可能陷入等待,直到添加索引完成。
- 该命令会跳过系统表、视图、临时表以及包含了tiflash不支持字符集的表。
在实际操作过程中,要考虑以上提示信息。其中在命令执行到结束期间,如果在该库下创建表,则可能会对这些新增表创建tiflash副本;全部同步执行之后,再创建表,则确定会为这些新增表创建tiflash副本。
tiflash mpp引擎支持分区表的动态裁剪模式
支持分区表的方便之处
我们的很多事实表都会分区,分区在表的使用中会使用动态分区裁剪缩小数据扫描范围,有效的提高查询效率。在5.4.0版本中,分区表无法应用mpp计算,只是在tiflash完成下推的算子之后,把数据汇总到tidb中进行计算,因为这种计算方式还引起了一些错误,参考:tidb server的oom问题优化探索。
操作实践
操作环境介绍
设计一个实验用分区表cust_partiton,一个非分区表cust,一个维度表info,执行如下查询:
-- 非分区表
select
`info`.`code` as `c0`,
sum(`cust`.`sn`) as `m0`,
sum(`cust`.`cbn`) as `m2`
from
`cust` as `cust`,
`info` as `info`
where
`info`.`code` = '88888888'
and
`cust`.`id` = `info`.`id`
group by
`info`.`code`;
-- 分区表
select
`info`.`code` as `c0`,
sum(`cust`.`sn`) as `m0`,
sum(`cust`.`cbn`) as `m2`
from
`cust_partiton` as `cust`,
`info` as `info`
where
`info`.`code` = '88888888'
and
`cust`.`id` = `info`.`id`
group by
`info`.`code`;
按照如下顺序执行:
- 非分区表查询
- 默认参数分区表查询
- 开启动态剪裁分区表查询
实验结果如下:
非分区表查询
执行计划:
id |estRows |task
----------------------------------------------+---------+-----------------
Projection_9 |1.00 |root
└─TableReader_52 |1.00 |root
└─ExchangeSender_51 |1.00 |batchCop[tiflash]
└─Projection_47 |1.00 |batchCop[tiflash]
└─HashAgg_48 |1.00 |batchCop[tiflash]
└─ExchangeReceiver_50 |1.00 |batchCop[tiflash]
└─ExchangeSender_49 |1.00 |batchCop[tiflash]
└─HashAgg_14 |1.00 |batchCop[tiflash]
└─Projection_46 |137289.74|batchCop[tiflash]
└─HashJoin_40 |137289.74|batchCop[tiflash]
├─ExchangeReceiver_25(Build)|6436.00 |batchCop[tiflash]
│ └─ExchangeSender_24 |6436.00 |batchCop[tiflash]
│ └─Selection_23 |6436.00 |batchCop[tiflash]
│ └─TableFullScan_22 |6436.00 |batchCop[tiflash]
└─TableFullScan_26(Probe) |180388.00|batchCop[tiflash]
执行时间:54ms
默认参数分区表查询
执行计划如下(因分区太多,部分执行计划被折叠):
id |estRows |task
--------------------------------+---------+------------
Projection_51 |1.00 |root
└─HashAgg_54 |1.00 |root
└─Projection_60 |7242.04 |root
└─HashJoin_63 |7242.04 |root
├─TableReader_72(Build) |6436.00 |root
│ └─Selection_71 |6436.00 |cop[tiflash]
│ └─TableFullScan_70 |6436.00 |cop[tiflash]
└─PartitionUnion_75(Probe)|200388.00|root
├─TableReader_80 |10000.00 |root
│ └─TableFullScan_79 |10000.00 |cop[tiflash]
....................................
├─TableReader_232 |6480.00 |root
│ └─TableFullScan_231 |6480.00 |cop[tiflash]
└─TableReader_236 |10000.00 |root
└─TableFullScan_235 |10000.00 |cop[tiflash]
执行时间:146ms
开启动态剪裁分区表查询
开启动态剪裁模式,执行如下SQL:
set @@session.tidb_partition_prune_mode = 'dynamic';
执行计划如下:
id |estRows |task
----------------------------------------------+--------+-----------------
Projection_9 |1.00 |root
└─TableReader_52 |1.00 |root
└─ExchangeSender_51 |1.00 |batchCop[tiflash]
└─Projection_47 |1.00 |batchCop[tiflash]
└─HashAgg_48 |1.00 |batchCop[tiflash]
└─ExchangeReceiver_50 |1.00 |batchCop[tiflash]
└─ExchangeSender_49 |1.00 |batchCop[tiflash]
└─HashAgg_14 |1.00 |batchCop[tiflash]
└─Projection_46 |8045.00 |batchCop[tiflash]
└─HashJoin_40 |8045.00 |batchCop[tiflash]
├─ExchangeReceiver_25(Build)|6436.00 |batchCop[tiflash]
│ └─ExchangeSender_24 |6436.00 |batchCop[tiflash]
│ └─Selection_23 |6436.00 |batchCop[tiflash]
│ └─TableFullScan_22 |6436.00 |batchCop[tiflash]
└─TableFullScan_26(Probe) |10000.00|batchCop[tiflash]
执行时间:53ms
总结
可以看到非分区表的执行计划中出现了ExchangeSender和ExchangeReceiver,说明是走了mpp模式了,执行时间为53ms,分区表默认参数执行计划中,没有出现ExchangeSender和ExchangeReceiver,只是出现了cop[tiflash],说明走了tiflash扫描,但是没走mpp模式执行时间为146ms,修改为动态裁剪模式之后,执行计划与非分区表比较相似,因为在动态裁剪模式下,每个算子都支持直接访问多个分区,PartitionUnion消失了,所以执行计划更简洁,出现了ExchangeSender和ExchangeReceiver,说明走了mpp模式,执行时间53ms。在我的测试用例中,因为数据量和分区数量的关系,当增加where条件,减少分区扫描范围时没有获得执行效率上的明显提升,在此不做描述了。
展望
随着6.0的来临,tidb具备更成熟的HTAP与容灾能力,加入多项云原生数据库所需的基础特性,对我关注的分区表处理也更加成熟,包括修复了以前提的很多个关于分区剪裁方面的issue,相信tidb作为HTAP已经有了更宽广的适应场景,希望将来的一天tidb可以作为全场景、开箱即用的数据库产品创造辉煌。