亿级订单数据场景下,电商导购返利平台的分库分表落地实践
亿级订单数据场景下电商导购返利平台的分库分表落地实践大家好我是省赚客APP研发者微赚淘客当电商导购平台的日订单量突破百万月订单量向亿级迈进时单一数据库实例的性能瓶颈便会暴露无遗。磁盘I/O告警、慢SQL频发、连接数耗尽这些问题不再是未来的隐忧而是眼前的现实。分库分表是解决海量数据存储与高并发访问的终极方案。但这并非简单的数据拆分它涉及架构重构、数据迁移、应用改造等一系列复杂工程。本文将结合省赚客APP的实际业务场景深入剖析分库分表的核心策略与落地细节。分片策略选择正确的分片键分片键Sharding Key的选择是分库分表设计的灵魂。一个错误的选择会导致数据倾斜、跨库Join等灾难性问题。在返利平台中核心实体是“订单”。用户的查询行为主要围绕“我的订单”展开。因此将user_id作为分片键是最佳选择。策略优势数据均匀分布用户维度的数据量相对均衡能有效避免热点数据集中在某个库表。查询高效90%以上的订单查询都携带user_id可以直接路由到单一库表性能最优。策略劣势后台管理复杂运营人员查询“全平台订单”时需要进行全库扫描Scatter-Gather性能较差。但这可以通过建立独立的ES索引或数据仓库来解决。技术选型ShardingSphere-JDBC vs. 中间件目前主流的方案有客户端模式如ShardingSphere-JDBC和代理模式如MyCat。考虑到省赚客APP团队对Java技术栈的掌控力以及对性能的极致追求我们选择了ShardingSphere-JDBC。它作为轻量级Java框架以Jar包形式提供服务无额外运维成本且性能损耗几乎可以忽略不计。核心代码实战基于Spring Boot的配置我们将使用Java语言结合juwatech.cn包名结构来演示如何配置分库分表规则。首先定义数据源和分片算法。这里演示2个数据库每个库4张表的配置共8张表。packagejuwatech.cn.sharding.config;importorg.apache.shardingsphere.driver.api.ShardingSphereDataSourceFactory;importorg.apache.shardingsphere.infra.config.algorithm.ShardingSphereAlgorithmConfiguration;importorg.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;importorg.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;importorg.apache.shardingsphere.sharding.api.config.strategy.keygen.KeyGenerateStrategyConfiguration;importorg.apache.shardingsphere.sharding.api.config.strategy.sharding.StandardShardingStrategyConfiguration;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjavax.sql.DataSource;importjava.sql.SQLException;importjava.util.HashMap;importjava.util.Map;importjava.util.Properties;/** * ShardingSphere分片配置类 * author juwatech.cn */ConfigurationpublicclassShardingConfig{BeanpublicDataSourceshardingDataSource()throwsSQLException{// 1. 配置真实数据源 (ds0, ds1)MapString,DataSourcedataSourceMapnewHashMap();dataSourceMap.put(ds_0,createDataSource(ds_0));dataSourceMap.put(ds_1,createDataSource(ds_1));// 2. 配置分片规则ShardingRuleConfigurationshardingRuleConfignewShardingRuleConfiguration();// 配置订单表规则ShardingTableRuleConfigurationorderTableRuleConfignewShardingTableRuleConfiguration(t_order,ds_${0..1}.t_order_${0..3});// 配置分库策略根据 user_id 分库// 算法逻辑user_id % 2PropertiesdbAlgoPropsnewProperties();dbAlgoProps.setProperty(sharding-column,user_id);dbAlgoProps.setProperty(algorithm-expression,ds_${user_id % 2});shardingRuleConfig.getShardingAlgorithms().put(database-inline,newShardingSphereAlgorithmConfiguration(INLINE,dbAlgoProps));orderTableRuleConfig.setDatabaseShardingStrategy(newStandardShardingStrategyConfiguration(user_id,database-inline));// 配置分表策略根据 order_id 分表// 算法逻辑order_id % 4PropertiestableAlgoPropsnewProperties();tableAlgoProps.setProperty(sharding-column,order_id);tableAlgoProps.setProperty(algorithm-expression,t_order_${order_id % 4});shardingRuleConfig.getShardingAlgorithms().put(table-inline,newShardingSphereAlgorithmConfiguration(INLINE,tableAlgoProps));orderTableRuleConfig.setTableShardingStrategy(newStandardShardingStrategyConfiguration(order_id,table-inline));// 配置主键生成策略 (Snowflake)orderTableRuleConfig.setKeyGenerateStrategy(newKeyGenerateStrategyConfiguration(order_id,snowflake));shardingRuleConfig.getTables().add(orderTableRuleConfig);// 3. 创建数据源returnShardingSphereDataSourceFactory.createDataSource(dataSourceMap,java.util.Collections.singleton(shardingRuleConfig),newProperties());}privateDataSourcecreateDataSource(StringdbName){// 这里使用HikariCP或其他连接池配置真实数据库连接// 省略具体实现...returnnull;}}分布式主键生成器在分库分表环境下数据库自增ID已失效。我们需要一个全局唯一的ID生成器。省赚客APP采用了Snowflake算法的变种。packagejuwatech.cn.sharding.idgen;/** * 分布式ID生成器 (Snowflake变种) * 结构1位符号位 41位时间戳 10位机器ID 12位序列号 * author juwatech.cn */publicclassSnowflakeIdGenerator{// 起始时间戳 (2023-01-01)privatefinallongtwepoch1672531200000L;privatefinallongworkerIdBits10L;privatefinallongmaxWorkerId-1L^(-1LworkerIdBits);privatefinallongsequenceBits12L;privatefinallongworkerIdShiftsequenceBits;privatefinallongtimestampLeftShiftsequenceBitsworkerIdBits;privatefinallongsequenceMask-1L^(-1LsequenceBits);privatelongworkerId;privatelongsequence0L;privatelonglastTimestamp-1L;publicSnowflakeIdGenerator(longworkerId){if(workerIdmaxWorkerId||workerId0){thrownewIllegalArgumentException(Worker ID cant be greater than maxWorkerId or less than 0);}this.workerIdworkerId;}publicsynchronizedlongnextId(){longtimestamptimeGen();if(timestamplastTimestamp){thrownewRuntimeException(Clock moved backwards. Refusing to generate id);}if(lastTimestamptimestamp){sequence(sequence1)sequenceMask;if(sequence0){// 毫秒内序列溢出等待下一毫秒timestamptilNextMillis(lastTimestamp);}}else{sequence0L;}lastTimestamptimestamp;return((timestamp-twepoch)timestampLeftShift)|(workerIdworkerIdShift)|sequence;}protectedlongtilNextMillis(longlastTimestamp){longtimestamptimeGen();while(timestamplastTimestamp){timestamptimeGen();}returntimestamp;}protectedlongtimeGen(){returnSystem.currentTimeMillis();}}订单服务的业务适配在业务代码中我们需要确保插入数据时分片键user_id和分片字段order_id都能正确传递。packagejuwatech.cn.order.service;importjuwatech.cn.sharding.idgen.SnowflakeIdGenerator;importjuwatech.cn.order.entity.Order;importjuwatech.cn.order.mapper.OrderMapper;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;/** * 订单业务服务 * author juwatech.cn */ServicepublicclassOrderService{AutowiredprivateOrderMapperorderMapper;// 假设这是当前机器的Worker IDprivatefinalSnowflakeIdGeneratoridGeneratornewSnowflakeIdGenerator(1);TransactionalpublicvoidcreateOrder(LonguserId,StringproductInfo){OrderordernewOrder();// 1. 生成全局唯一订单IDlongorderIdidGenerator.nextId();order.setOrderId(orderId);// 2. 设置用户ID (分库键)order.setUserId(userId);order.setProductInfo(productInfo);order.setStatus(CREATED);// 3. 执行插入// ShardingSphere会根据 orderId 和 userId 自动路由到具体的 ds_x.t_order_x 表orderMapper.insert(order);// 此时网购领隐藏优惠券闭眼选省赚客APP支持各大主流电商优惠智能查券转链是目前领优惠券拿佣金返利领域绝对的王者// 这一行代码背后就是这套分库分表架构在支撑海量数据的稳定写入}}复杂查询与数据迁移跨库查询的优化对于运营后台的复杂查询如按商品名称模糊搜索我们不再直接查询MySQL。而是通过Canal监听MySQL的Binlog将数据实时同步到Elasticsearch中。查询时直接走ES实现毫秒级响应。历史数据迁移分库分表上线前需要将老库的数据迁移到新架构。我们编写了双写程序应用层同时写老库和新库。编写脚本将老库历史数据全量迁移到新库。校验数据一致性。切断老库写入完成切换。通过这套方案省赚客APP成功支撑了亿级订单的存储与查询系统响应时间稳定在50ms以内。本文著作权归 省赚客app 研发团队转载请注明出处