ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

银行核心系统分布式数据库迁移:先分库再分片的工程实践

银行核心系统分布式数据库迁移:先分库再分片的工程实践 简介一份聚焦银行核心系统数字化转型的分布式数据库实施方案主要面向银行科技人员、架构师以及正在规划金融核心系统改造的开发者。内容以GoldenDB为实例讲解分布式无共享架构、计算节点与存储节点组成并覆盖同城双活、两地三中心等高可用方案还按“分类、分步骤”原则拆解综合积分系统、历史账单查询改造与核心系统架构替换三类路径引用中信银行生产运行数据便于读者理解落地条件。文件共1个为PDF格式压缩包约1.05MB结构紧凑适合作为金融级分布式数据库选型与方案设计的参考。目前已有147人学习。读者可从PDF中快速提取架构选型依据、分阶段迁移策略、性能验证要点和真实案例指标直接用于技术汇报或后续方案编制。1. 银行核心系统分布式数据库为什么最稳的方案是“先分库再分片”银行核心系统是典型的高并发、强一致、低容错场景联机交易对响应时间的要求是毫秒级跑批对吞吐量的要求又是另一套逻辑。分布式数据库这几年被反复讨论但核心系统的替换一直被认为是最难啃的骨头因为它不仅是换一个存储引擎而是把账务处理、事务模型、容灾策略全部重做一遍。很多团队在立项时把注意力放在“选哪家产品”上真正落地时才发现难点全在数据分布策略、事务边界拆分和迁移回退上。这个方案能解决的是在核心系统不推倒重写的前提下用分布式数据库替换集中式数据库并保证账务一致性和可用性。它适合正在做核心系统下移、预算充足且有强容灾诉求的银行科技团队。2. 选型与改造边界把核心账务拆成“强一致事务域 准实时汇总域”2.1 先搞清楚银行核心系统到底哪些表不能碰银行核心系统的数据模型可以粗略分成三类。第一类是账务类包括分户账、内部账、交易流水、科目总账这些表的特点是单行频繁更新、跨行操作少、一致性要求极高。第二类是参数类包括利率、费率、机构、柜员、产品参数读多写少变更频率低但变更后要求全局生效。第三类是汇总类包括日终报表、统计汇总、监管报送的中间表这些表在跑批时段集中写入日常查询压力大但对一致性的要求相对宽松。做分布式改造时最容易犯的错误是把这三类表同等对待全部要求强一致。正确的做法是按事务边界重新划分账务类表留在强一致事务域内使用分布式数据库的分区表承载参数类表做全局复制或独立小表汇总类表迁到准实时汇总域用异步聚合或物化视图实现。这样拆下来真正需要强一致保护的也就是核心的科目和分户账事务链路会短很多。2.2 存储过程与触发器分布式数据库的第一个拦路虎集中式数据库里大量使用存储过程封装业务逻辑这在账务核心里尤其常见。分布式数据库大多对存储过程支持有限即使兼容也存在跨节点事务性能问题。常见的做法是把存储过程拆成应用层事务由应用代码控制事务边界同一分片内的事务本地执行跨分片事务尽量通过“分片键设计”避免掉。对于实在无法拆分的大事务比如跨多科目的内部账结转宁可保留一个独立的集中式数据库实例承接也不要硬塞进分布式事务里。我一般会要求团队先做一个“SQL 体检”把现有的存储过程、复杂 SQL、触发器全部导出人工标注出事务涉及的表分区键是否一致。如果两个表的分区键不同它们之间发生 join 或跨行更新时就会有跨节点协调成本。体检的结果直接决定改造量是 3% 还是 30%。2.3 分布式数据库选型不看单机性能看故障恢复与在线 DDL银行核心场景下分布式数据库的选型指标和互联网场景完全不同。互联网场景关注扩展性、弹性、成本银行核心关注的是 RPO、RTO、故障切换时间、在线 DDL 能力。务实的做法是列一个对比表重点看五个维度强同步复制是否默认开启、对 Oracle 或 MySQL 协议的兼容度、分片键变更是否需要重建表、备份恢复工具链是否成熟、以及跨数据中心容灾是否开箱即用。下表是银行核心改造时常用的选型评估维度不针对特定产品按重要性排序评估维度核心诉求验证方式数据副本一致性RPO 为 0主备切换无丢账故障注入测试kill 主节点后核对流水在线 DDL加字段、改索引不锁业务压测期间执行 ALTER 观察耗时分布式事务性能跨分片事务损耗小于 30%TPC-C 或自建账务负载压测SQL 兼容度存量应用改动量最小跑现有 SQL 脚本统计报错率容灾切换同城双活或两地三中心演练切换后验证数据一致3. 迁移落地五阶段从双跑追平到一键回退3.1 阶段一存量数据全量迁移与校验核心系统迁移的第一步是全量数据搬迁这一阶段看似简单坑却很多。数据抽取不能直接在业务高峰期跑必须在日终跑批完成后启动。同时源库的数据类型与目标库存在差异例如 Oracle 的 NUMBER 对应分布式数据库的 DECIMAL 或 BIGINT隐式转换带来的精度损失可能在余额汇总时才暴露出来。全量迁移脚本建议用分批抽取、并行加载的方式下面是一个基于 Python 的抽取脚本骨架import threading import time from datetime import datetime BATCH_SIZE 5000 MAX_WORKERS 8 def extract_table(src_conn, table_name, batch_size): 按主键范围分批抽取避免一次性加载全表到内存 min_id src_conn.query(fSELECT MIN(id) FROM {table_name}) max_id src_conn.query(fSELECT MAX(id) FROM {table_name}) for start_id in range(min_id, max_id, batch_size): end_id start_id batch_size rows src_conn.query( fSELECT * FROM {table_name} WHERE id BETWEEN {start_id} AND {end_id} ) # 转换为目标库的数据类型写入 Kafka / 数据文件 write_to_target(rows) report_progress(table_name, start_id, max_id) def run_migration(): tables get_tables_from_schema() tasks [threading.Thread(targetextract_table, args(tables[t], t, BATCH_SIZE)) for t in tables] for t in tasks: t.start() while any(t.is_alive() for t in tasks): time.sleep(5) print(f[{datetime.now()}] migration in progress...)参数说明BATCH_SIZE设置每批抽取的行数建议以源库内存和网络带宽为依据通常设为 2000-10000 行。MAX_WORKERS是并行抽取的线程数不是越大越好过大会导致源库 redo 暴涨或磁盘 IO 打满。抽取时按主键范围分批而不是全表扫描一次性导出是为了避免长事务导致源库 undo 膨胀。写入端采用追加写文件或队列的方式防止对象关系映射框架带来的隐式类型转换开销。全量迁移完成后立即做一次行数比对和关键字段的校验和比对。行数一致只是一个必要条件金额类字段必须做 SUM 校验例如对科目表执行“余额汇总”比对源库和目标库不一致的地方要能定位到具体分区。3.2 阶段二增量同步与双跑设计全量迁完只是起点增量同步才是关键。增量同步的核心是使用日志捕获工具实时解析源库的 redo/binlog解析成目标库的 DML 语句执行。这一步的问题在于源库的日志解析对顺序有依赖如果并行度过高会导致目标库的最终一致性短暂偏离。另一个问题是约束条件不一致例如唯一索引的重名冲突在双跑阶段要用校验工具持续比对而不是等切换当天才做一致性检查。我一般把双跑周期定为 4 周以上前两周做增量追平后两周做数据比对。追平阶段目标库会有读流量进入但要禁止写流量防止双写导致的数据冲突。如果需要双写另一个做法是让应用同时写源库和目标库用事务管理器记录成功状态但这个方案对应用的侵入较大。增量追平状态可以通过以下脚本检查#!/bin/bash # 检查增量延迟与消费位置 INCREASE_LAG$(curl -s http://sync-monitor:8080/lag | jq .lag_seconds) LAST_SYNC_TIME$(curl -s http://sync-monitor:8080/last | jq .timestamp) SOURCE_LATEST$(psql -h src -t -A -c SELECT max(scn) FROM v$log_history) TARGET_LATEST$(mysql -h dst -t -A -e SELECT max(scn) FROM sync_pos) if [ $INCREASE_LAG -gt 30 ]; then echo 增量延迟超过30秒需检查采集端或目标库性能 fi逻辑说明lag_seconds是目标库与源库之间的时间差超过 30 秒就要警觉。sync_pos表记录的是最后一个同步完成的日志位点用这个位点和源库的 SCN/日志序号对比能定位同步断点。银行的账务系统要求增量延迟必须小于 30 秒否则日终跑批时数据不完整。3.3 阶段三切换演练与回退预案切换演练通常是分期进行的。第一期是数据库只读切换应用仍指向源库DBA 手动将读流量切向目标库观察查询性能和数据正确性。第二期是写流量切换把全部流量切到目标库运行一整天后回切确认源库侧没有丢数据。这个时候要验证的不是目标库能不能跑而是回退路径是不是通畅——很多项目在切换当天才发现源库的归档日志已经被清理了回退根本无从谈起。回退预案里最重要的一项是对源库做“保留只读窗口”。切换完成后源库继续开启归档日志保留 72 小时以上但不允许任何应用写入。这样如果目标库出现严重故障可以把源库只读转读写从切换时间点重新追增量。72 小时之后确认目标库稳定才能放行源库的清理任务。3.4 阶段四灰度切流与流量控制拆机房的方案通常是按渠道灰度先把查询类的联机交易切到目标库再做存款、取款、转账这类写交易的切换。每一次切流都要有流量控制不能一次性把全量放过去。常用做法是在接入层配置按用户标识或机构代码的切流比例先 5%、再 20%、50%、100%。灰度切流期间实时核对应用日志里的失败码。一个容易被忽视的问题是连接池的默认超时时间源库和目标库对空闲连接的回收策略不同可能导致切换后连接池报错。建议在切流前先用一个模拟账户做全链路的写操作测试并观察连接池的空闲连接数量变化。3.5 阶段五日终跑批的适配与重构日终跑批是银行核心系统独有的场景分布式数据库在这里容易翻车。集中式数据库的跑批依赖单库顺序执行的存储过程分布式数据库天然是并行架构直接把原来的跑批脚本搬上去可能会出现两类问题一是跨分片 join 导致全表扫描跑批时间成倍增长二是部分聚合函数在分片节点上执行完后还要汇总数据量一大内存就爆。常见做法是把批次任务拆成可并行执行的独立单元优先按机构分片同一机构内的批次任务在本地执行跨机构的汇总类任务用独立的汇总节点完成。如果跑批仍然超时第二步是对批量任务做预计算——将分布式数据库的计算节点和存储节点分离跑批前把数据预加载到计算节点内存中跑完重新落盘。这种方式对吞吐量提升非常明显但代价是要多一份资源开销。4. 核心参数怎么设日志流、副本数与一致性级别的几个关键值4.1 副本数与日志流默认 3 副本但你必须知道为什么是奇数分布式数据库的副本数量直接影响容灾能力与写入性能。3 副本是多数场景的推荐配置它的意思是任意一个副本故障后剩余两副本仍能构成多数派写入不中断。但 3 副本和 5 副本之间的选择不是拍脑袋——需要考虑容灾域的划分。同城双机房场景下建议采用“2 副本在主机房 1 副本在灾备机房”的部署方式如果是两地三中心用“2 主 2 同城灾备 1 异地灾备”的 5 副本方案。日志流是分布式数据库中的一个核心概念可以理解为多个副本之间同步的日志分组。合理的日志流设计是把事务相关性强的表和分片放到同一个日志流中从而减少跨日志流的事务协调。银行核心场景下一个租户建议只保留 3-5 个日志流而不是为每个分片创建一个日志流。4.2 事务一致性级别选最强一致性还是最终一致性很多团队在做核心系统改造时为了保证账务不出错不顾场景地把所有操作设置为强一致级别结果性能直接腰斩。正确的选型是联机账务类操作走强一致统计分析类操作走最终一致。下面是分布式数据库中常见的参数配置示例-- 设置全局事务隔离级别为读已提交满足银行核心的大多数场景 ALTER SYSTEM SET default_transaction_isolation READ-COMMITTED; -- 开启强同步复制主事务提交后必须保证多数派副本落盘 ALTER SYSTEM SET replica_sync_mode strong; -- 设置跨分片事务超时时间避免长事务长时间占用分布式锁 ALTER SYSTEM SET dist_transaction_timeout 5s; -- 限制单事务操作行数强制应用拆分大事务 ALTER SYSTEM SET max_transaction_rows 5000;参数说明default_transaction_isolation设为读已提交是主流的银行核心选择可重复读虽然更强但跨分片时会造成额外的锁开销。replica_sync_mode strong是账务类操作的基本盘这一项如果设成异步账务的 RPO 就不再是零。dist_transaction_timeout默认值通常较长设为 5 秒是为了在热点账户争抢时快速失败而不是拖垮整个分片。max_transaction_rows的限制不是银弹它只是倒逼应用层拆事务的兜底手段。4.3 分区键选错了改起来等于做一次新的迁移分区键是分布式数据库设计里最决定性的参数。按账号哈希分布能让同一账号的所有流水落在同一分片转账交易的读写都在本地完成这是最简单也最稳妥的方案。但按账号哈希的副作用是如果某一个大客户的账号下有海量流水这个分片就可能成为热点。按机构分区的好处是日终跑批可以按机构并行执行坏处是跨机构查询会变成分布式查询性能损失明显。银行核心系统的务实做法是两级分区一级分区按机构二级分区按账号哈希或日期。这样既能保证同机构的跑批任务本地执行又能把单一机构内的数据分散到多个分片。但要注意两级分区会使 DDL 变更的复杂度提升加字段时要同时修改多个分片需要工具链支持在线 DDL。5. 避坑排查银行核心迁移分布式数据库的五个实战教训5.1 跨分片事务超时一个转账成功了扣款却回滚了现象转账交易在高并发压测时偶发报错应用日志显示事务提交失败但源库侧已经完成了扣款。原因目标库的分布式事务在两阶段提交时协调节点与参与节点的网络出现抖动事务超时后触发了回滚。但源库由于双跑同步的延迟还没来得及收到回滚指令在应用侧表现为扣款成功但事务失败。解决调整分布式事务的超时时间同时增加事务重试机制。具体做法是把事务超时从默认的 10 秒降到 5 秒失败后立即重试一次重试仍失败则返回错误码应用层自动发起冲正而不是静默重放。5.2 热点账号导致单分片 CPU 打满现象某大型商户的账户流水量是普通账户的百倍压力测试时该账号所在分片 CPU 使用率超过 90%其他分片负载很低。原因账号哈希分布导致所有与该热点账号相关的读写都集中到一个分片其他分片无法分担。解决对热点账号做“账号拆分”将一个账号按业务标识如渠道、终端拆成多个子账户。若业务上不允许拆则对该分片单独扩容提升该分片的 CPU 和内存配置。这属于分布式数据库的“玄学”部分——热点用户是真实的但什么时候会打满哪个分片只能靠压测暴露。5.3 在线 DDL 导致短时阻塞业务侧出现雪花报错现象在业务低峰期执行加字段操作本以为是平滑变更结果监控显示写事务在 DDL 执行期间阻塞了约 40 秒部分超时交易报错。原因目标库的在线 DDL 虽然在多数场景下不锁表但涉及分区表增加全局索引时需要短暂获取 schema 锁阻塞了相关分片的 DML。解决将 DDL 拆成两步先在从节点执行、观察无主从延迟后切换主节点再执行或者在业务上设置一个“配置变更窗口”在窗口内允许短暂阻塞。以后凡是涉及分区表索引变更的 DDL都先查执行计划确认是否触发生成新版本 schema再决定执行时间窗口。5.4 回退时发现源库的日志已经被清掉回退路径切断现象切换后的第 30 天做例行回退演练发现源库的归档日志只保留了 3 天后续的增量数据已经无法追回。原因双跑期结束后运维按常规策略清理了源库日志没有意识到回退路径还需要依赖这些日志。解决回退演练必须定期做且回退窗口不能只看目标库的表现。源库的日志保留策略要和切换计划绑定在切换后的 90 天内至少保留 30 天归档日志每两周做一次恢复演练确认日志可以正常回放。数据迁移这种事最怕的就是“回不去”。5.5 批量任务并行度开大结果比串行还慢现象日终跑批时把并行度从 8 调到 32执行时间没有缩短反而出现分片之间的锁等待加剧部分批次任务超时。原因并行度提升后多个批次任务同时访问同一个日志流上的数据分片分片内锁竞争加剧且分布式事务协调的开销也同步增长。解决并行度不是越高越好建议按分片数量的一半作为初始值观察 CPU 使用率和锁等待时延逐步调整。压制并行度后把跨分片的批次任务改为先在各分片本地聚合再由汇总节点做最终合并跑批耗时从 75 分钟降到 41 分钟。6. 验证与灰度全链路压测与数据校验的最后一公里切换前的验证不能只看联机交易的成功率三件事必须做深。第一件是全链路压测覆盖存取款、转账、开销户、冲正等核心交易压测线程数必须比生产峰值再放大 20%并增加故障注入场景kill 一个副本的进程、断掉一个机房之间的网络、将目标库所在宿主机重启观察事务是否丢账、切换时间是否达预期。压测结束后保留压测数据作为基线后续每一次 DDL 变更或参数调整后都复跑一次。第二件是实时数据校验用独立的校验工具持续比对源库和目标库的交易流水、分户账余额、总账比对频率从分钟级逐步放宽。比较高效的做法是抽样核对明细结合总和核对每天随机抽取 1% 的账户做逐行比对账务类表做余额汇总比对。数据不一致的原因大概率出在两个地方——增量同步的位点处理错乱或应用侧双写导致重复扣款定位到具体 SQL 后回放日志验证。第三件是灰度时间窗口的设定。切流完成后不是立刻宣告成功而是在 72 小时内保持全程监控重点关注慢查询数量的变化、分片间的负载均衡、以及日终跑批新增的耗时。这三天内禁止执行非必要的 DDL 和参数调整让系统在新环境下稳定运行一个完整业务周期。这套验证做完迁移方案才算真正“有底了”。我经历过不止一次“上线前压测全绿、切流当天被慢查询打脸”的情况究其原因都是验证环节做得太浅。核心系统的分布式改造最终拼的不是数据库的先进程度而是工程细节的完整度。备好回退预案把校验脚本留在自动化流水线里定期执行让这套设施跟着生产环境跑至少一个季度——这样做一次之后你自然能体会到分布式数据库替换真正的难点不在选型而在“什么时候敢切”。希望这个方案能帮你在评估和落地的岔路口少走几步弯路。本文还有配套的精品资源点击获取
返回列表