一、迁移背景与选型对比
1.1 原有架构痛点
笔者业务使用 PostgreSQL Citus + PostGIS 存储海量点位、多边形地理数据,通过 create_distributed_table 将带空间坐标的业务表按用户 ID 分片,数据打散到多个 Worker 节点,解决单表数据量大的问题。长期运行后暴露不少痛点:
- 跨分片分布式事务支持弱:Citus 仅支持分片内强事务,跨分片事务一致性无法保障,业务代码必须规避跨分片写,侵入性很高。
- 扩容成本高:新增 Worker 节点需要手动执行分片重平衡 Rebalance,数据搬迁耗时长,业务风险大。
- 空间查询有短板:GiST 空间索引只在单个 Worker 分片内生效;大范围空间查询需要协调节点聚合所有分片结果,跨分片空间检索性能衰减明显。
- 运维负担重:分片元数据管理、节点故障转移、分片热点排查流程复杂。
1.2 迁移 TiDB 的核心优势
TiDB 原生分布式数据库,用来替代 Citus 分片方案,解决原有架构痛点:
- 原生支持分布式事务,RC/SI 隔离级别,业务不再需要规避跨分片读写。
- 在线横向扩容,无需人工重分片,PD 自动完成 Region 分裂、调度与负载均衡。
- 兼容 MySQL 生态,配套 DM、Lightning、TiCDC 等迁移工具,运维体系成熟。
- 自带完善监控面板、故障自愈能力,大幅降低分布式数据库运维成本。
核心挑战:原有业务强依赖 PostGIS 的 geometry 字段与 GiST 空间索引,这是本次迁移最大的兼容性难点。
二、Citus 与 TiDB 分布式核心差异(迁移关键理论)
很多迁移失败的核心原因:直接照搬 Citus 分片逻辑到 TiDB,造成读写热点、空间查询不可用等问题。
2.1 Citus 分片机制
Citus 属于中间件层分片:指定 Shard Key,利用哈希算法将数据拆分到不同 Worker 节点。
SELECT create_distributed_table('user_gps_point','user_id');
空间能力完全依赖底层 PostGIS 扩展,在每个 Worker 分片本地创建 GiST 空间索引,加速ST_DWithin、ST_Intersects这类空间查询。 缺点:底层 PG 单节点仍是单点瓶颈;分片粒度固定,无法动态自适应;跨分片空间查询无法使用全局空间索引。
2.2 TiDB 分布式机制
TiDB 是存储层原生分片(Region),不需要业务层手动定义分片键,PD 会根据数据量、访问热度自动分裂、迁移、均衡 Region。
迁移核心优化思路:废弃 Citus 人工分片逻辑,利用 TiDB 自动 Region 调度能力;但必须正视 TiDB 空间索引能力边界,不能直接平移 PostGIS 的空间查询方案。
三、整体迁移方案设计
本次采用全量导出 + 增量回放 + 业务灰度切流的稳妥方案,保障数据零丢失、业务不停机。
- 结构适配:PG 表结构、字段类型、索引、PostGIS geometry 字段适配 TiDB;
- 全量数据:pg_dump 导出 Citus 全量数据,清洗后导入 TiDB;
- 增量同步:解析 PostgreSQL WAL 日志,持续回放增量变更;
- 数据校验:全表行数校验、抽样业务数据 + 空间坐标一致性比对;
- 灰度切流:先迁移读流量,稳定运行一段时间后,再切换写流量。
四、详细实操步骤
4.1 表结构适配(基础字段改造)
Citus (PG) 与 TiDB 字段类型存在差异,迁移前批量适配:
- PG
timestamp→ TiDBdatetime - PG
bigserial自增 → TiDBbigint auto_increment - PG 无主键表 → TiDB 必须补显式主键(TiDB 强烈建议所有业务表设置主键,也是 CDC 同步的基础)
- 删除所有 Citus 专属分布式表属性、分片元数据、Citus 系统表相关定义
适配原则:放弃人工分片字段,依托 TiDB 主键 + Btree 索引实现数据天然打散。
4.2 空间坐标字段迁移:Citus+PostGIS 和 TiDB 空间索引能力对比
原 Citus 集群业务表使用 PostGIS 扩展存储几何数据(Point、Polygon 等),在 Worker 分片上创建 GiST 索引,做邻近点检索、多边形相交判断。这是本次迁移最关键的难点。
4.2.1 Citus+PostGIS 空间能力
- 数据类型:
geometry平面坐标、geography球面 WGS84 经纬度,支持 Point、Polygon、MultiPolygon 等 OGC 几何对象。 - 索引:GiST(R 树)为主,也支持 SP-GiST、BRIN 索引,加速全套 ST 空间算子。
- 分片特点:空间索引是每个 Worker 分片本地独立索引,分片内空间查询性能优秀;跨分片空间查询无法使用全局空间索引,需要协调节点聚合所有分片结果,性能差。
- 函数生态:PostGIS 提供数百个空间计算函数,支持投影转换、缓冲区、面积、拓扑校验等复杂 GIS 分析。
4.2.2 自建 TiDB(TiKV 行存表)空间能力现状(重点)
⚠️ 重要结论:自建 TiDB 集群(TiKV 行存表),可以创建 GEOMETRY 字段、支持基础 ST 系列空间函数,但是不支持 SPATIAL 空间索引。 即使执行
CREATE SPATIAL INDEX语法不会报错,但 TiDB 会直接忽略该索引定义,不会构建 R 树 / GiST 多维索引,所有空间过滤条件都会触发全表扫描,大数据量场景完全不可用。
能力清单:
- ✅ 支持存储
GEOMETRY、POINT类型,可导入 WKT/WKB 格式坐标; - ✅ 支持基础 ST 函数:ST_DWithin、ST_Contains、ST_Intersects 等;
- ❌ 无原生 R 树 / GiST 空间索引;
- ❌ 没有 PostGIS 扩展,缺少投影转换、复杂拓扑分析函数;
- ❌ 没有
geography球面椭球类型,GEOMETRY 只能做平面近似距离计算,全球范围经纬度距离存在误差。
补充:TiDB Cloud Lake 的 Fuse 列存表才支持 SPATIAL 索引,属于 OLAP 分析场景,不适合 OLTP 实时空间查询业务。
4.2.3 带空间坐标表的 3 套迁移方案选型
方案 A:业务侧预处理(本次迁移选用,OLTP 实时点位查询,推荐)
业务场景:GPS 点位表,高频查询 “指定多边形范围内的点”、“距离目标坐标 N 公里范围内点位”。 实现思路:
- TiDB 表保留原始
GEOMETRY字段,存储原始 WKT 坐标; - 新增独立 double 字段
longitude经度、latitude纬度; - 对 lon/lat 建立 Btree 复合索引;
- 业务层使用 GeoHash 或 Hilbert 曲线,将二维坐标降维做范围粗筛;
- 使用 Btree 索引在 TiDB 中筛选候选数据集,在应用内存完成精确距离、多边形相交校验。
优点:完全适配自建 TiDB,复用 TiDB 原生 Region 调度、分布式事务能力;适合百万级点位 OLTP 场景。 缺点:业务代码需要改造,无法直接复用原有 PostGIS 的 SQL。
方案 B:外置 GIS 引擎(适合大数据量、复杂拓扑分析)
TiDB 只存储业务主数据、主键、原始坐标;通过 CDC 同步坐标数据到独立 GIS 引擎(PostGIS/Elasticsearch Geo 等)。业务查询时,先在 GIS 引擎检索,拿到主键 ID 列表,再回表 TiDB 查询业务属性。 适合场景:大量多边形、复杂空间相交、拓扑分析。
方案 C:保留 Citus 分片(不推荐)
无法解决原有 Citus 跨分片查询、扩容麻烦的痛点,违背本次迁移目标。
4.3 Citus 全量数据导出
在 Citus 协调节点执行全量备份,保证所有分片数据完整导出:
# 备份业务数据库
pg_dump -h localhost -p 5432 -d business_db -U postgres -F c -f citus_full_backup.dump
导出完成后,清洗备份文件,过滤 Citus 分布式元数据、分片配置、PG 系统表,将 geometry 数据转为 WKT 格式,生成适配 TiDB 的 SQL 文件。
4.4 数据导入 TiDB
数据量较小时,直接 mysql 客户端 source 导入;大批量数据推荐使用TiDB Lightning极速导入,规避普通导入的性能瓶颈,支持 TB 级数据迁移。
# 常规SQL导入
mysql -h tidb-host -P 4000 -u root < tidb_import.sql
4.5 增量数据同步保障一致性
全量导入存在时间窗口,必须开启增量同步补齐这段时间的数据变更。通过解析 PostgreSQL WAL 日志,实时同步新增、更新、删除数据至 TiDB;迁移期间双读兜底,保证数据最终一致。
五、迁移核心踩坑记录(高分核心段落)
坑点 1:直接复用 Citus 分片键导致 TiDB 读写热点
初期照搬 Citus 的 user_id 分片逻辑,业务按用户 ID 单调写入,造成 TiDB 单 Region 热点,写入 QPS 无法打散。 解决方案:去掉人工分片逻辑,合理设计主键,利用 TiDB Region 自动分裂、调度能力打散数据,解决热点问题。
坑点 2:PG 无主键表导入 TiDB,增量同步异常
Citus 业务存在部分无主键表,导入 TiDB 之后,增量同步无法精准定位行,出现重复更新、数据不一致问题。 解决方案:迁移前统一为所有业务表添加显式主键,主键是 TiDB 数据同步、行更新的基础保障。
坑点 3:Citus 跨分片查询逻辑迁移报错
Citus 跨分片查询语法在 TiDB 中执行异常,部分复杂 Join 无法直接运行。 解决方案:基于 TiDB 分布式查询能力重构 SQL,利用 TiDB 全局索引、分布式 Join 能力,简化原有分片查询逻辑。
坑点 4:PostGIS geometry 字段迁移 TiDB,误以为 SPATIAL 索引可以直接生效
原有 Citus 表使用 PostGIS geometry 存储 GPS 点位,Worker 分片上建有 GiST 空间索引,业务大量 SQL 依赖ST_DWithin、ST_Intersects做空间范围检索。迁移初期,直接把 geometry 字段导入 TiDB,执行CREATE SPATIAL INDEX,语法没有报错,直接上线测试。 上线后发现大范围空间查询性能极差,查询耗时数十秒。 根因:自建 TiDB(TiKV 行存)仅兼容 SPATIAL 索引语法,并不会真正构建空间 R 树 / GiST 索引,所有空间谓词无法走索引过滤。TiDB Cloud Lake 的 Fuse 列存表才支持空间索引,不适用于 OLTP 在线业务。 解决方案:
- 拆分 geometry,单独存储经度、纬度 double 字段;
- 对 lon/lat 建立 Btree 复合索引;
- 业务层使用 GeoHash 做空间粗筛,拿到候选主键集合;
- 在应用内存完成精确距离、多边形相交计算,减少数据库计算压力。改造后空间查询性能恢复到业务可用标准。
六、迁移后架构升级与性能优化
6.1 架构简化
迁移后彻底抛弃 Citus 协调节点 + 多 Worker 分片架构,替换为标准 TiDB PD+TiDB+TiKV 三层架构,架构更简洁、稳定、可扩展。
6.2 性能提升亮点
- 写入吞吐提升:TiDB 自动 Region 打散,无人工分片瓶颈,并发写入能力大幅提升。
- 查询稳定性提升:原生分布式查询,无需中间件转发,跨节点查询性能更稳定。
- 运维效率提升:在线扩容、自动负载均衡,无需手动分片重平衡。
- 空间查询改造:放弃数据库层多维空间索引,业务层预过滤,在不改动 TiDB 内核前提下,实现地理点位检索能力。
七、结合 PCTA 考点的学习总结
本次 Citus + PostGIS 迁移 TiDB 的实操过程,覆盖多个 PCTA 核心考点:
- TiDB 分布式架构原理:PD 调度、Region 分裂与迁移、负载均衡机制,对比传统中间件分片方案的优势。
- 热点问题优化:业务分片逻辑适配、主键设计规避读写热点。
- 数据迁移工具使用:全量导入、增量同步、数据校验完整流程。
- 分布式事务优势:对比 Citus 弱跨分片事务能力,理解 TiDB 分布式事务对业务的价值。
- TiDB 索引能力边界:TiKV 仅原生支持 Btree 索引,不支持 GiST/Rtree 多维空间索引,对比 PostgreSQL 可扩展索引访问方法的差异,理解不同分布式数据库的设计取舍。
八、最终总结
Citus 作为 PostgreSQL 分片扩展,搭配 PostGIS 可以实现地理空间数据存储与检索,适合轻量分片场景。但在高并发、强事务、大规模扩容场景存在天然短板。 TiDB 原生分布式数据库,能够替代 Citus 分片架构,大幅简化运维架构,降低业务改造复杂度,提升系统稳定性与吞吐。但是要注意:自建 TiDB 不支持空间索引,不能直接平移 PostGIS 的空间查询方案,需要做业务层改造。 本次完整迁移实操,不仅解决线上业务痛点,也深度理解 TiDB 分布式核心机制与索引能力边界,对 PCTA 认证学习和实际运维工作都有很大帮助。后续会持续深耕 TiDB 性能调优、故障排查等实操场景,持续输出实战内容。
参考文档
- TiDB 官方文档:https://docs.pingcap.com
- Citus 分布式分片官方文档:https://docs.citusdata.com
- PostGIS 官方文档:https://postgis.net/docs/