分库分表问题
分库分表后会带来什么问题
1.全局主键问题
2.分页问题
3.统计问题
4.事务问题
5.扩容问题
分开分布的自增主键如何设计?
1.步长设置
根据分表的数量确定步长,每张表设置不同的其实值 假设有3个分片表,可以这样配置: 第一个表:起始值1,步长3,生成序列1、4、7、10... 第二个表:起始值2,步长3,生成序列2、5、8、11... 第三个表:起始值3,步长3,生成序列3、6、9、12..
设置自增步长和起始值
SET @@auto_increment_increment = 3; -- 步长为分片数量 SET @@auto_increment_offset = 1; -- 起始值按分片顺序设
缺点:
1.分片扩容时由于数据分布已经固定,如果重新分片可能会有数据分布不均匀或主键重复问题,需要进行复杂的数据迁移
2.UUID
优点:
UUID是36位字符串,优点是本地生产,性能高不占用网络资源
缺点:
缺点是占用空间大,无序会造成数据库写入性能问题,且会造成索引分裂。
3.Redis
优点:
性能高,实现简单Redission有专门的API(RIdGenerator)
缺点:
需要依赖Redis中间,会有网络波动或宕机问题,需要单独维护Redis。
4.雪花算法
优点:
1.生产时不依赖数据库、完成在内存中生产,呈自增状态,每秒钟可生产百万的自增ID,有时间回拨问题
2.雪花算法的结构:符号位(1位) + 时间戳(41位) +机器标识(10位) +序列号(12位)

缺点:
存在时间回拨问题,由于依赖服务器时间,服务器时间回拨可能会造成重复Id。
1.人为修改系统时间。 2.不同机器之间可能存在时差。
如何解决时钟回拨问题
1.和上次时钟比较:每次生产新的时钟时和上次记录的最大时钟比较,如何发现有时钟回拨问题,则拒绝生产ID并等待时间追上并告警 2.单独维护时间钟:逻辑时钟保证总是递增,不依赖系统时钟。但需要额外的机制来同步和持久化逻辑时钟。
5.美团Leaf
基于步长+雪花算法实现
分库分表后分页会有什么问题
问题
数据归并失效:简单在每个分表执行 LIMIT offset, pagesize 后内存合并,结果不准确。例如,8条记录(1-8)分两张表(1-4,5-8),查询 LIMIT 3,2(第4、5条),各分库返回(4)、(8),合并得(4,8)而非正确结果(4,5)。
深度分页性能差:LIMIT 100000,10 需扫描排序大量数据,IO和排序开销大
数据分布不确定性:无法预知目标页数据来自哪个分库,需查询所有分库再合并,效率低
解决方案
全局视野法
每个分库查询 LIMIT 0, offset+pagesize,汇总后全局排序取目标页
例如,查第3页(每页2条)时,每个库返回前6条数据,内存合并后取第5-6条。优点:结果精准;缺点:数据量大时网络和内存压力大
禁止跳页
仅支持"上一页/下一页",基于游标(如最大ID)分页,这种适用于APP上的滑动分页
SELECT * FROM orders WHERE id > last_max_id ORDER BY id LIMIT pagesize;
二次查询法
将全局偏移量除以分库数作为新offset查询各分库找出所有返回数据的最小值time_min二次查询并确定time_min在全局的offset,最终获取目标页数据
优点:减少数据传输量;缺点:需多次查询
分库分表后数据统计问题
直接查询法
使用 UNION ALL 将所有分表数据合并后进行统计,适用于分表数量较少的情况,如示例中64张分表需要分4次查询再汇总,简单直观但性能较差,不适合大规模分表场景。
分层汇总法
先在各分库/分表执行统计,再对结果进行二次聚合。
public int countAllOrders() { int total = 0; for (String db : List.of("db1", "db2", "db3")) { String sql = "SELECT COUNT(*) FROM " + db + ".orders"; total += jdbcTemplate.queryForObject(sql, Integer.class); } return total; }
数据同步法
如订单数据一般都会推送到数据仓库或OLAP系统,我们可以借助数仓进行统计。
分布式事务解决方案
1.二阶段提交(2pc)
2.三阶段提交(3pc)
3.TCC(try confirm cancel)
4.MQ最终一致性
刚性事务严格遵循ACID原则,通过全局锁定资源保证强一致性:2PC,3PC
柔性事务则遵循BASE理论,通过业务逻辑改造实现最终一致性:TCC,MQ最终一致性
参考:https://blog.csdn.net/qq_31430665/article/details/118054170
分库分表后如何进行扩容
停机方式
停服准备:发布停服公告,停止写入操作
数据迁移:通过工具按新分片规则(如从uid%3改为uid%4)全量迁移历史数据
规则切换:更新分库分表中间件配置,启动新路由规则
缺点:需严格规划停服时间,迁移失败风险高,且大容量数据迁移耗时较长
不停机方式
开启双写:新库表结构就绪后,业务代码同时向旧库和新库写入数据,但查询请求仍走旧库
数据同步:通过工具(如Canal、数据迁移程序)将旧库历史数据全量同步至新库,并定期校验数据一致性
流量切换:数据校验完成后,将查询流量逐步切至新库,观察业务稳定性
旧库下线:确认新库运行正常后,停止向旧库写入,最终下线旧库
缺点
查询时旧库切换新库,新库数据无法重复预热可能出现网络抖动。
双写时事务层面是先更新旧库,旧库更新完成后再更新新库,新库一单失败就会存在不一致问题,需要用强一致性事务或MQ最终一致性

浙公网安备 33010602011771号