本文用于指导 DataSail JDBC 全量同步任务选择切分键和切分模式。
如果没有特殊需求,大表优先选择有索引的整型主键,并使用 accurate 模式。
全量同步一张大表时,如果不做切分,DataSail 会使用一个读取任务从头到尾查询整张表。表越大,单次查询持续时间越长,同步速度也越容易受单个任务限制。
切分键就是用来“拆分这张表”的字段。DataSail 根据切分键的值,把一张表拆成多个互不重叠的数据范围,再交给多个读取任务并行同步。
例如,一张订单表使用 id 作为切分键:
范围 1:id >= 1 且 id < 10001 范围 2:id >= 10001 且 id < 20001 范围 3:id >= 20001 且 id < 30001 ……
这样做的主要作用是:
如果不配置切分键,影响如下:
影响对象 | 具体表现 |
|---|---|
同步速度 | 单表、单实例通常只有一个有效读取任务,大表同步时间明显变长。 |
读取并发 | 即使提高读取并发,也无法把同一张表拆成多个范围。 |
源库查询 | 整张表由一个查询持续读取,单次查询和连接保持时间更长。 |
失败重试 | 失败后需要重新读取的范围更大。 |
PostgreSQL/Greenplum | 长时间读取可能带来长事务和 vacuum 延迟风险。 |
说明
id。如果没有合适的切分键,不要随意选择无索引或低基数字段。配置项 | 作用 | 不配置时 | 建议 |
|---|---|---|---|
| 切分键 | 不切分 | 大表必须配置。 |
| 切分模式 | 通常为 | 建议显式配置。 |
| 切分粒度 + JDBC fetch size | 未配置时运行时使用 | 从 |
优先级 | 字段类型 | 推荐程度 | 原因 |
|---|---|---|---|
1 | 单列整型主键或唯一索引 | 强烈推荐 | 索引效率高,通常基数高,范围查询稳定。 |
2 | 单列高基数普通索引 | 推荐 | 可以进行范围扫描,但要确认重复值和分布。 |
3 | 时间戳索引列 | 谨慎使用 | 适合时间分布均匀的数据,热点集中时容易倾斜。 |
4 | 字符串索引列 | 不优先 | 受数据库类型、排序规则和切分模式限制。 |
5 | 无索引字段 | 不推荐 | 可能造成全表扫描、排序和较高源库压力。 |
检查项 | 合适的表现 | 不合适的表现 |
|---|---|---|
索引 | 单列索引,或复合索引第一列。 | 无索引,或复合索引非第一列。 |
类型 | 整型优先,其次是时间戳。 | 小数、未验证的字符串 |
基数 | 不同值多,重复值少。 | 布尔、状态码、枚举等低基数列 |
分布 | 各值域数据量接近。 | 大量空洞、热点集中、严重倾斜 |
模式 | 怎么切 | 切分数量由什么决定 | 优点 | 主要风险 | 推荐场景 |
|---|---|---|---|---|---|
| 根据真实数据位置,每约 | 数据量和 | 各切分数据量较均匀,能适应主键空洞和数据倾斜。 | 规划阶段需要多次查询切分边界。 | 大多数生产大表,默认优先选择。 |
| 从最小值开始,按固定键值跨度切分。 | 键值范围和 | 规划速度快,查询边界次数少。 | 键值跨度越大,切片数越多;稀疏键还会产生大量空切片。 | 连续、稠密的自增整型键。 |
| 将最小值到最大值的范围切成约“读取并发数 × 5”份。 | 读取并发数 | 主动减少并控制切片数,使切片数与读取并发保持固定比例。 | 不按真实行数切,数据不均时少数范围可能读取很久,并形成长事务。 | 希望减少切片数,并让切片数与读取并发匹配。 |
| 不按切分键拆分,一张物理表作为一个读取范围。 | 表和物理分片数量 | 源库并发压力最低,配置简单。 | 整表查询持续时间长,大表容易形成长事务。 | 小表、无合适切分键、源库需要低并发。 |
以下 SQL 用 id 作为切分键进行说明。<字段列表>、<表名> 和 <其他条件> 会替换成任务的实际内容;没有额外条件时会省略对应的 WHERE 或 AND;Oracle、SQL Server 等数据库的分页语法会有所不同。
accurate第一步,查询切分键的最小值和最大值:
SELECT MIN(id) FROM <表名> WHERE <其他条件>; SELECT MAX(id) FROM <表名> WHERE <其他条件>;
第二步,从最小值开始,反复查询后续约 reader_fetch_size 条真实数据的最大 ID,用它作为下一个切片边界:
SELECT MAX(id) FROM ( SELECT id FROM <表名> WHERE <其他条件> AND id > ? ORDER BY id LIMIT <reader_fetch_size> ) t;
假设 reader_fetch_size=5000,这条边界 SQL 会反复执行,直到找到整张表的全部切片边界。因此 accurate 生成的切片,数据行数通常接近 5000 条,但每个切片的 ID 跨度可能不同。
第三步,正式读取每个切片:
SELECT <字段列表> FROM <表名> WHERE <其他条件> AND id >= ? AND id < ?;
quick第一步,只查询一次最小值和最大值:
SELECT MIN(id) FROM <表名> WHERE <其他条件>; SELECT MAX(id) FROM <表名> WHERE <其他条件>;
第二步,不再查询真实数据位置,直接在本地按固定跨度计算边界。假设最小值为 1、最大值为 100000、reader_fetch_size=5000,会生成:
[1, 5001) [5001, 10001) [10001, 15001) ……
第三步,正式读取每个切片:
SELECT <字段列表> FROM <表名> WHERE <其他条件> AND id >= ? AND id < ?;
quick 的切片数约等于“ID 总跨度 ÷ reader_fetch_size”。ID 跨度很大时,会生成很多切片;ID 存在空洞时,部分 SQL 可能查不到数据。
parallelism第一步,同样只查询一次最小值和最大值:
SELECT MIN(id) FROM <表名> WHERE <其他条件>; SELECT MAX(id) FROM <表名> WHERE <其他条件>;
第二步,不再查询真实数据位置,而是在本地把整个 ID 范围切成约“读取并发数 × 5”份。假设读取并发数为 2,会生成约 10 个等宽范围。
第三步,正式读取每个切片:
SELECT <字段列表> FROM <表名> WHERE <其他条件> AND id >= ? AND id < ?;
parallelism 和 quick 的正式读取 SQL 相同,区别在于边界的计算方式:
对比项 |
|
|
|---|---|---|
边界计算 | 每隔一个 | 根据读取并发数生成约 5 倍数量的等宽范围。 |
切片数 | 由 ID 总跨度决定,可能非常多。 | 由读取并发数决定,数量可控。 |
是否需要反复查询边界 | 不需要 | 不需要 |
nosplitnosplit 不查询最小值、最大值,也不生成范围条件,直接执行整表读取:
SELECT <字段列表> FROM <表名> WHERE <其他条件>;
单表、单实例下通常只有这一条长时间运行的读取 SQL。表越大,查询和事务保持时间越长。
reader_fetch_size 在不同模式下的含义 模式 |
|
|---|---|
| 每个切分大约包含多少条数据;值越大,切分越少。 |
| 每个切分的键值跨度,不是数据行数。 |
| 不决定切分数量,但仍影响 JDBC 每次拉取数据的行为。 |
| 只影响 JDBC 拉取数据的行为。 |
上表介绍了 reader_fetch_size 在不同切分模式下的含义。基于此,您可结合下表进一步评估增大或减小该参数可能带来的影响,从而选择更适合当前业务场景的配置方式。
调整方式 | 可能收益 | 可能代价 |
|---|---|---|
增大 |
| 单个范围更大,失败重读量和任务长尾可能增加。 |
减小 | 单个范围更小,调度更灵活。 |
|
切分会给源库带来两类操作:
下表介绍了不同数据切分模式在切分范围确定阶段和正式读取阶段的行为差异,并总结了各模式对源库的整体影响,便于您结合业务场景选择合适的同步方式。
模式 | 确定切分范围时 | 正式读取时 | 对源库的主要影响 |
|---|---|---|---|
| 需要多次查询,逐个寻找真实数据边界。 | 各范围数据量较均匀,单个查询通常不会特别慢。 | 前期边界查询较多,但正式读取更均衡,通常不容易出现单个超慢范围。 |
| 只查询最小值和最大值,之后本地计算范围。 | 可能执行很多空范围或数据量差异很大的查询。 | 前期压力小;键值稀疏时会产生无效查询,键值倾斜时会出现慢范围。 |
| 只查询最小值和最大值,之后按读取并发数计算范围。 | 总切片数约为读取并发数的 5 倍,切片和调度数量可控。 | 减少切片元数据和调度开销;但数据倾斜时少数范围仍可能长时间占用连接并形成长事务。 |
| 不需要计算多个范围。 | 使用一个查询长时间读取整张表。 | 并发连接少,但整表查询容易形成长事务;大表失败后的重读范围也最大。 |
在实际使用中,切分键索引、读取并发数以及源库类型等因素,也会对最终效果产生影响。
无论使用哪种模式,切分键有索引都非常重要。没有索引时,边界查询和正式范围查询都可能退化为全表扫描或排序。
实际同时运行的查询数量取决于读取并发数,不等于总切分数。总切分数可以很多,但读取任务会分批处理这些范围。
对于 PostgreSQL、Greenplum 等数据库,建议重点关注长事务风险及其可能带来的后续影响,如下:
模式 | 长事务风险 |
|---|---|
| 数据分布不均时,少数范围可能包含大量数据。该范围查询持续时间越长,事务保持时间也越长。 |
| 整张表由一个查询读取,大表读取期间事务会一直保持,长事务风险最明显。 |
长事务可能阻碍旧版本数据清理,造成 vacuum 延迟和表膨胀。全量过程中如果源表仍在持续写入,多个读取连接也不天然代表一个全局一致快照。