场景痛点:某电商平台的亿订MySQL订单表达到7亿行时,出现致命问题:
复制-- 简单查询竟需12秒!单何 SELECT * FROM orders WHERE user_id=10086 LIMIT 10; -- 统计全表耗时278秒 SELECT COUNT(*) FROM orders;1.2.3.4.5.核心矛盾:
B+树索引深度达到5层,做分磁盘IO暴增。库分单表超200GB导致备份时间窗突破6小时。亿订写并发量达8000QPS,单何主从延迟高达15分钟。做分关键认知:当单表数据量突破5000万行时,库分就该启动分库分表设计预案。亿订
那么问题来了,单何假如现在有10亿的做分订单数据,免费源码下载我们该如何做分库分表呢?库分
今天这篇文章就跟大家一起聊聊这个问题,希望对你会有所帮助。亿订
图片
优化效果:
核心表体积减少60%高频查询字段集中提升缓存命中率分片键选择三原则:
离散性:避免数据热点(如user_id优于status)业务相关性:80%查询需携带该字段稳定性:值不随业务变更(避免使用手机号)分片策略对比:
策略类型
适用场景
扩容复杂度
示例
范围分片
带时间范围的单何查询
简单
create_time按月分表
哈希取模
均匀分布
困难
user_id % 128
一致性哈希
动态扩容
中等
使用Ketama算法
基因分片
避免跨分片查询
复杂
从user_id提取分库基因
针对订单系统的三大高频查询:
用户查历史订单(user_id)商家查订单(merchant_id)客服按订单号查询(order_no)解决方案:
图片
Snowflake订单ID改造:
复制// 基因分片ID生成器 publicclass OrderIdGenerator { // 64位ID结构:符号位(1)+时间戳(41)+分片基因(12)+序列号(10) privatestaticfinalint GENE_BITS = 12; public static long generateId(long userId) { long timestamp = System.currentTimeMillis() - 1288834974657L; // 提取用户ID后12位作为基因 long gene = userId & ((1 << GENE_BITS) - 1); long sequence = ... // 获取序列号 return (timestamp << 22) | (gene << 10) | sequence; } // 从订单ID反推分片位置 public static int getShardKey(long orderId) { return (int) ((orderId >> 10) & 0xFFF); // 提取中间12位 } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.路由逻辑:
复制// 分库分表路由引擎 publicclass OrderShardingRouter { // 分8个库 每个库16张表 privatestaticfinalint DB_COUNT = 8; privatestaticfinalint TABLE_COUNT_PER_DB = 16; public static String route(long orderId) { int gene = OrderIdGenerator.getShardKey(orderId); int dbIndex = gene % DB_COUNT; int tableIndex = gene % TABLE_COUNT_PER_DB; return"order_db_" + dbIndex + ".orders_" + tableIndex; } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.关键突破:通过基因嵌入,使相同用户的做分订单始终落在同一分片,同时支持通过订单ID直接定位分片

Elasticsearch索引表结构:
复制{ "order_index": { "mappings": { "properties": { "order_no": { "type": "keyword" }, "shard_key": { "type": "integer" }, "create_time": { "type": "date" } } } } }1.2.3.4.5.6.7.8.9.10.11. 4.2 全局二级索引(GSI) 复制-- 在ShardingSphere中创建全局索引 CREATE SHARDING GLOBAL INDEX idx_merchant ON orders(merchant_id) BY SHARDING_ALGORITHM(merchant_hash) WITH STORAGE_UNIT(ds_0,ds_1);1.2.3.4.双写迁移方案:

灰度切换步骤:
开启双写(新库写失败需回滚旧库)全量迁移历史数据(采用分页批处理)增量数据实时校验(校验不一致自动修复)按用户ID灰度流量切换(从1%到100%)双十一期间发现某网红店铺订单全部分到同一分片。
解决方案:引入复合分片键 (merchant_id + user_id) % 1024
这里的分布式事务使用的RocketMQ的数据最终一致性方案:
复制// 最终一致性方案 @Transactional public void createOrder(Order order) { orderDao.insert(order); // 写主库 rocketMQTemplate.sendAsync("order_create_event", order); // 发消息 } // 消费者处理 @RocketMQMessageListener(topic = "order_create_event") public void handleEvent(OrderEvent event) { bonusService.addPoints(event.getUserId()); // 异步加积分 inventoryService.deduct(event.getSkuId()); // 异步扣库存 }1.2.3.4.5.6.7.8.9.10.11.12.13.跨分片查询页码错乱。站群服务器
解决方案:改用ES聚合查询或业务折衷方案(只查最近3个月订单)。

性能指标:
场景
拆分前
拆分后
用户订单查询
3200ms
68ms
商家订单导出
超时失败
8s完成
全表统计
不可用
1.2s(近似)
真正的架构艺术,是在分与合之间找到平衡点。免费信息发布网
相关文章:
相关推荐: