
1. 项目背景与挑战国际计费系统作为企业核心业务支撑平台随着全球业务扩张面临着数据量激增的典型挑战。我们遇到的场景是单库数据量已突破3TB日均增量超过200万条记录传统垂直扩展方式如升级服务器配置已无法满足性能需求。特别是在月末计费高峰期复杂报表查询响应时间从最初的2秒延长至47秒严重影响了业务运营效率。这个系统的特殊性在于涉及跨国交易数据需要满足不同地区的合规要求计费逻辑复杂包含费率计算、税务处理、货币转换等多维度数据关联业务连续性要求高迁移过程必须保证7×24小时服务可用2. 技术选型与方案设计2.1 分库分表方案对比我们评估了三种主流方案应用层分片在业务代码中实现路由逻辑优点灵活可控缺点侵入性强需要改造所有DAO层代码MySQL Fabric官方提供的分片方案优点原生支持缺点功能简单社区支持弱ShardingSphere生态Sharding-JDBC轻量级Java驱动Sharding-Proxy独立代理服务最终选择Sharding-Proxy的核心考量对现有系统零侵入无需修改应用代码完整支持MySQL协议兼容现有运维工具链提供完善的数据分片、读写分离、分布式事务能力2.2 分片策略设计针对计费业务特征设计了复合分片策略shardingRule: tables: t_order: actualDataNodes: ds_${0..31}.t_order_${0..7} tableStrategy: inline: shardingColumn: user_id algorithmExpression: t_order_${user_id % 8} databaseStrategy: standard: shardingColumn: region_code preciseAlgorithmClassName: com.xxx.RegionHashAlgorithm关键设计点双重分片维度按地区分库32个物理库按用户ID分表每个库8张表自定义地区哈希算法考虑数据分布均衡性满足GDPR等合规要求特定地区数据物理隔离热点数据处理对大客户采用单独分片策略设置非对称分片区间如北美地区分配更多分片3. 迁移实施细节3.1 环境准备Sharding-Proxy部署架构[Application] - [HAProxy] - [Sharding-Proxy Cluster] - [MySQL Cluster] ↑ Keepalived具体配置Proxy节点8核16G × 3部署在K8s集群连接池配置props: max.connections.size.per.query: 5 acceptor.size: 16 executor.size: 163.2 数据同步方案采用双写增量同步的混合模式全量迁移阶段/* 通过Proxy执行 */ INSERT INTO new_table SELECT * FROM old_table WHERE create_time 2023-01-01;增量同步阶段基于Canal监听binlog自定义转换器处理分片逻辑public class ShardingTransformer implements EntryTransformer { Override public String transform(String originSQL) { // 解析原SQL并添加分片条件 return ShardingSQLRewriter.rewrite(originSQL); } }数据校验机制行级CRC校验抽样比对每日凌晨执行def verify_data(shard, primary): diff spark.sql(f SELECT count(*) FROM {shard} s FULL OUTER JOIN {primary} p ON s.idp.id WHERE s.checksum!p.checksum OR s.id IS NULL OR p.id IS NULL ) return diff.collect()[0][0]4. 关键问题与解决方案4.1 分布式事务处理计费业务涉及多表事务操作我们采用Seata的AT模式与Sharding-Proxy集成配置调整props: proxy.transaction.type: XA proxy.opentracing.enabled: true异常处理流程超时事务自动回滚死锁检测每5分钟扫描补偿任务队列4.2 跨分片查询优化针对报表类复杂查询实现方案并行查询内存归并// 使用HintManager强制全库路由 try (HintManager hintManager HintManager.getInstance()) { hintManager.setMasterRouteOnly(); ListOrder orders orderRepository.findAll(); }建立全局索引表Elasticsearch同步关键字段预计算常用统计指标4.3 在线扩容方案当需要新增分片时滚动扩容流程新节点加入 - 数据rebalance - 流量切换 - 旧节点下线数据迁移工具./bin/start.sh -m -c config-sharding.yaml \ -Dsharding.scaling.job.offsetlatest \ -Dsharding.scaling.worker.thread205. 性能优化实践5.1 连接池调优测试发现默认配置在高并发下存在问题连接等待超时线程竞争激烈优化后配置props: max.connections.size.per.query: 10 acceptor.size: 32 # CPU核心数×2 executor.size: 64 # IO密集型任务 query.with.cipher.column: false5.2 SQL改写规则针对典型慢查询进行优化禁止全表扫描/* 原SQL */ SELECT * FROM orders WHERE status1; /* 改写后 */ SELECT * FROM orders WHERE status1 AND user_id IN (...,...) /* 自动注入分片条件 */分页查询优化// 使用流式查询替代内存分页 try (StreamOrder stream orderRepository.streamAll()) { stream.limit(1000).forEach(...); }5.3 监控体系建设基于Prometheus的监控指标关键指标查询延迟P99连接池利用率分布式事务成功率告警规则示例- alert: HighQueryLatency expr: rate(shardingsphere_proxy_requests_latency_sum[1m]) 0.5 for: 5m6. 实施效果与经验总结迁移后性能指标对比指标迁移前迁移后写入TPS1,2008,500查询延迟(P99)2.3s320ms存储成本3TB(SSD)1.2TB×3踩坑经验拆分键选择初期使用订单ID导致热点问题最终采用用户ID地区码复合键批量插入优化// 错误方式产生大量小事务 orders.forEach(repository::save); // 正确方式 repository.saveAll(orders);数据类型陷阱BIGINT自增ID在分片后可能冲突改用Snowflake分布式ID对于计划实施类似迁移的团队建议先在小规模数据上验证分片策略建立完善的数据校验机制准备详细的回滚方案进行充分的性能压测