数据导入最佳实践

最近更新时间:2025-11-18 10:10:22

我的收藏
本文给出了可以最大化利用系统资源,提高导入性能的方法,建议在开展数据导入前阅读以进行合理的导入方案规划和设计。

按序导入

按序导入是指数据按照主键从小到大的顺序去导入,数据库可以更高效地处理有序的数据。相对于乱序导入,按序导入性能提高20%~300%。
按序导入包含事务内部有序事务之间有序。在同时满足的情况下,则认为数据导入是完全有序,性能最佳。
事务内部有序:在一个 Bulk Load 事务内部,数据需要按照主键顺序排列。如果事务内部的数据无序,MyLoader 需要增加参数--enable-tdsql-bulk-load-allow-unsorted,否则会报错失败。在设置--enable-tdsql-bulk-load-allow-unsorted选项后,数据会先暂存在临时文件中,在提交阶段进行外部排序,然后再按序输出到 external SST 文件中。如果事务内部数据有序,则可直接存到 external SST 文件。因此事务内部无序会消耗更多的 CPU 和IO 资源。
事务之间有序:Bulk Load 事务和事务之间需要是“有序”的。这意味着每个事务所写入数据的 smallest_key 和 largest_key 作为这个事务的区间。如果事务的区间互相没有重叠,则认为事务之间是有序的。
否则,事务和事务之间生成的 external SST 文件会有区间重叠,会导致大量 external SST 被 ingest 到 L0 层,然后触发压缩。当 L0 SST 数量积累到超过一定的阈值,就会触发缓写。系统支持通过调整user region cf disable_write_stall_caused_by_slow_compaction=true 来屏蔽掉缓写。但是 L0 SST 数量无限制地增长下去,会让 get / scan 读操作变得非常慢,引发后续一系列问题,让系统变得不可控。
因此,当进入到缓写状态时,Bulk Load 会在提交 commit log,以及回放 commit log 时等待并重试,从而避免 L0 SST 数量一直增长。
建议在 Bulk Load 导数据之前,适当增大缓写状态阈值level0_stop_writes_trigger、停写状态阈值level0_slowdown_writes_trigger
1.1 使用root用户登录集群内任一节点。
1.2 进入 TDStore Client 工具所在目录。
cd /data/tdsql3_run/6008/SQLEngine/bin/
1.3 启动 TDStore Client 工具。
./tdstore_client_new_console
1.4 配置全局变量。
global
操作示例:

1.5 配置缓写状态阈值level0_stop_writes_trigger、停写状态阈值level0_slowdown_writes_trigger
update_td_options --cf_opt_user_region=level0_stop_writes_trigger=2048
update_td_options --cf_opt_user_region=level0_slowdown_writes_trigger=1024
操作示例:

1.6 待数据导入完成后,再恢复缓写状态阈值level0_stop_writes_trigger、停写状态阈值level0_slowdown_writes_trigger

并发导入

当磁盘性能较好时(NVMe 磁盘),Bulk Load 性能瓶颈点很可能在 CPU 而不在 IO,可以尝试适当增加导入线程的并发数(50~100)来提升导入效率。
系统支持通过tdstore_bulk_load_sorted_data_sst_compressiontdstore_bulk_load_unsorted_data_sst_compression调整 Bulk Load 产生的 external SST 文件所使用的压缩算法, 来平衡 CPU 和 IO、磁盘空间。该配置项适用于17.1.0及以后版本。
SET GLOBAL tdstore_bulk_load_sorted_data_sst_compression=zstd,snappy,lz4,auto,nocompression,...;

事务大小

与普通事务先把未提交数据暂存在内存的 Write Batch 不同,Bulk Load 事务会把未提交数据直接暂存到 external SST 文件中,或者临时 Chunk 文件中。因此可以避免大事务导致内存占用过多,从而降低内存溢出(OOM)的风险。
Bulk Load 事务不宜过小。如果 Bulk Load 事务过小,可能会导致以下问题:
产生较小的 SST 文件。
导入性能不佳。
因此,为了获得较好的性能,通常建议将一个 Bulk Load 事务的数据量设置在100MB以上。

INSERT INTO or LOAD DATA

使用LOAD DATA语句导入,要比INSERT INTO VALUES性能提升20%~100%。这是因为在 SQLEngine 层,对于INSERT INTO VALUES的 SQL 语法解析要比LOAD DATA消耗更多的 CPU。
使用LOAD DATA导入之前,请确认已开启local_infile
mysql> set global local_infile=ON;
使用 MyLoader LOAD DATA 导入时,每个数据文件会对应一个 Bulk Load 事务,工作线程会并发地读取数据文件进行导入。为了获得最佳性能,建议多个数据文件之间尽量划分均匀,每个数据文件大小在100MB~1GB。

二级索引

对于表上存在二级索引的场景,表上的二级索引越多,Bulk Load 性能会变差。这是因为二级索引数据一定是无序的,需要进行额外的排序操作,从而增加了导入过程中的计算开销。
针对二级索引,优化建议适当增大 Bulk Load 事务的大小。通常一个 Bulk Load 事务设置在几百 MB 为宜。

分区表

考虑一个 Bulk Load 事务跨多个分区表的场景:
对于 Range 分区,且源端数据按序导入:一个 Bulk Load 事务中的数据通常会集中在一个 Range 分区上,或者少量 Range 相邻的分区上。此时一个 Bulk Load 事务通常不会涉及太多的分区表,跟普通表的场景类似,不需要做额外处理。
对于 Hash 分区,且源端数据按序导入:一个 Bulk Load 事务中的数据会被均匀地打散到各个 Hash 分区上。此时一个 Bulk Load 事务会几乎涉及所有的分区表。此时需要把 Bulk Load 事务大小调大,从而保证一个 Bulk Load 事务中分配到每个 Hash 分区上的数据量不要太少。假设一共 N 个 Hash 分区,那么推荐 Bulk Load 事务大小比普通表场景扩大N倍。目前在分成16个 Hash 分区的场景下,性能要比不进行 Hash 分区(普通表)损失15%左右。
二级分区:需要考虑一个 Bulk Load 事务中的数据会涉及多少个二级分区,最终分配到每个二级分区上的数据量不要过少。
总而言之,一个 Bulk Load 事务涉及的分区数量越多,性能越差。从 TDStore 层去看,其实就是一个 Bulk Load 事务涉及了过多的 RG / region 作为事务参与者。普通事务也会有类似的问题,涉及的分区越多,RG 层面的参与者越多,性能越差。

HDD 机器集群

当集群采用 HDD 机器作为存储节点机型时,IO更容易成为 Bulk Load 导入的性能瓶颈。而 IO 频繁打满,会导致 TDStore 进入 write stall(缓写)状态,而compaction 跟导入线程竞争 IO 资源,可能会让缓写加剧,从而降低导入性能。
基于测试数据,TDSQL Boundless 沉淀出以下 HDD 机器集群 Bulk Load 数据导入最佳实践:
调整block_size:将block_size调整为 256KB(默认为 64KB)。
block_based_table_factory={cache_index_and_filter_blocks=true;cache_index_and_filter_blocks_with_high_priority = true;pin_l0_filter_and_index_blocks_in_cache = true;checksum=kCRC32c;block_size=65536;filter_policy=bloomfilter:10:false;whole_key_filtering=true;}
对于 HDD 机器而言,除了 Bulk Load 导数据场景,对于正常的数据读写,调大block_size也会有一定性能提升。
部署单独的导入机:建议部署单独的导入机(即 MyLoader 将数据发往的目标节点),在预规划 RG 分布时,导入机上不放置 RG。导入完毕后,可以通过RG 迁移(打开 MC 自动均衡)将 RG 副本重新平衡到导入机上,相当于把导入机重新作为一个普通的存算混合节点;也可以下线裁撤掉导入机。这是因为HDD 机器 IO 资源紧张,导入节点直接生成 external SST 文件需要 IO 资源,而普通的存算混合节点在做 compaction 时也需要 IO 资源,两者在同一机器上会加剧该节点的 IO 资源竞争,拖慢性能。尤其是存在二级索引数据,或者导入的主键数据无序时,无序数据会被大量 ingest 到 LSM-tree 的 L0 层,然后逐层向下 compaction,占用大量 IO。
控制并发线程:导入的并发线程不宜过大,一般10~20线程即可。因为 HDD 机器的随机写性能要差很多,并发线程数量再继续增大的话反而性能会下降。SSD 机器则通常可以50~100。
调整 Bulk Load 事务大小:Bulk Load 事务过大或者过小都可能导致性能不佳。尤其是存在二级索引或者主键数据无序时,如果事务过小,会产生大量的过小 SST 并 ingest 到 L0 层,导致频繁进入缓写状态;如果事务过大,由于无序数据需要写入临时文件进行外部归并排序,这部分会频繁读写临时文件,挤占 IO 资源。而写入临时文件的数据目前是不带压缩的,会进一步加剧竞争。因此事务的数据量可以控制在200MB~4GB(未压缩),估算方式可以用一行记录的平均大小乘以行数。另外,由于 HDD 环境下导入线程数通常不大,可以适当调大无序数据的归并排序缓存大小 tdstore_bulk_load_merge_chunk_size = 268435456; (默认为64MB;内存足够的情况下建议调整为256MB)和tdstore_bulk_load_total_merge_buffer_size = 536870912; (默认为128MB;内存足够的情况下可以调整到512MB),从而降低外部排序时读写临时文件次数。