分库分表的概念
分库分表是一种数据库水平拆分技术,用于解决单库单表数据量过大导致的性能瓶颈问题。
- 分库:将数据分散到多个数据库实例,降低单库负载。
- 分表:将数据分散到同一数据库的多个表中,减少单表数据量。
分库分表的适用场景
- 单表数据量超过千万级,查询性能显著下降。
- 高并发写入场景,单库写入成为瓶颈。
- 业务需要隔离不同数据,如多租户系统。
分库分表的实现方式
水平分片(横向拆分)
按数据行拆分,不同行分配到不同库/表。
- 范围分片:按ID范围划分,如1-100万在表A,100-200万在表B。
- 哈希分片:对分片键(如用户ID)哈希取模,均匀分布数据。
垂直分片(纵向拆分)
按列拆分,不同字段分配到不同库/表。
- 将高频字段与低频字段分离,减少单表宽度。
分库分表的技术方案
客户端分片
在应用层实现分片逻辑,如通过MyBatis拦截器或Spring动态数据源。
- 优点:轻量级,无中间件依赖。
- 缺点:业务代码侵入性强,维护复杂。
中间件分片
使用ShardingSphere、MyCat等中间件透明化分片。
- 优点:对业务代码无侵入,支持动态扩容。
- 缺点:需维护中间件,存在性能损耗。
分库分表的挑战与解决方案
分布式事务
跨库事务需通过XA、TCC或Saga模式解决。
- XA:强一致性,性能低。
- TCC:通过Try-Confirm-Cancel实现最终一致性。
跨库查询
- 全局表:冗余基础数据到所有分库。
- 绑定表:关联表使用相同分片规则,避免跨库JOIN。
ID生成
避免自增ID冲突,采用雪花算法(Snowflake)或UUID。
-
雪花算法示例:
javapublic class SnowflakeIdGenerator { private final long twepoch = 1288834974657L; private final long workerIdBits = 5L; private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private long workerId; private long sequence = 0L; private long lastTimestamp = -1L; public synchronized long nextId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards."); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & 4095; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - twepoch) << 22) | (workerId << 12) | sequence; } }
分库分表的监控与运维
- 数据均衡:定期检查分片数据分布,避免倾斜。
- 扩容:通过双写或数据迁移工具平滑扩容。
- 监控:跟踪慢查询、连接数等指标,及时优化。
分库分表的替代方案
若数据量未达瓶颈,可优先考虑以下优化:
- 读写分离:主库写,从库读。
- 缓存:引入Redis减少数据库压力。
- 索引优化:覆盖索引、联合索引等。