分库分表问题

分库分表后会带来什么问题

1.全局主键问题
2.分页问题
3.统计问题
4.事务问题
5.扩容问题

 

分开分布的自增主键如何设计?

1.步长设置

根据分表的数量确定步长,每张表设置不同的其实值
假设有3个分片表,可以这样配置:
第一个表:起始值1,步长3,生成序列1、4710...
第二个表:起始值2,步长3,生成序列2、5811...
第三个表:起始值3,步长3,生成序列3、6912..

设置自增步长和起始值

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最终一致性

posted @ 2025-11-08 10:19  爵士灬  阅读(1)  评论(0)    收藏  举报