MYSQL中的分库分表
分库分表是分布式数据库架构中常用的优化手段,用于解决单库单表数据量过大、性能瓶颈等问题。其核心思想是将数据分散到多个数据库(分库)或多个表(分表)中,以提升系统的吞吐量、查询性能和可扩展性。
一:为什么要分库分表
MySQL单库数据量超过5000万或单表数据量超过1000万时,性能会显著下降。随着数据增长,单库单表的查询和写入效率逐渐成为系统瓶颈。
二:拆分类型
拆分类型可分为两类:水平拆分和垂直拆分。
1.水平拆分
水平拆分又分为水平分库和水平分表。
1)水平分库:将相同表结构的表复制到另一个库中,减少单个数据库的访问压力。
优点
- 提升系统吞吐量:大幅降低单库数据量,提升查询和写入性能;
- 便于扩展:支持数据库层面的水平扩展(增加数据库节点)。
- 提高可用性:单个数据库故障不会影响整体服务,其他库仍可正常访问。
缺点
- 跨库查询复杂:需要聚合多个库的数据时,需借助中间件或手动处理,性能较低。
- 事务一致性难保证:分布式事务实现复杂,通常需引入额外机制。
2)水平分表:是将同一张表的数据分到多个表中,提高查询效率。
优点
- 单表数据量可控:将大表拆分为小表,避免单表数据量过大导致的索引效率下降、查询变慢等问题。
- 提高并发性能:不同分表可分散到不同磁盘或物理机,降低I/O争用。
缺点
- 跨表查询麻烦:需合并多个分表结果,查询复杂。
- 扩容复杂度高:后期调整分表数量时,需要进行数据迁移。
2.垂直拆分
垂直拆分又分为垂直分库和垂直分表。
1)垂直分库:将不同表分到不同库中,降低单机的访问瓶颈。
优点
- 业务解耦:按业务维度拆分库,不同业务的数据独立存储,降低耦合度。
- 资源隔离:不同库可部署在不同服务器,避免资源竞争(如CPU、内存、磁盘IO)。
- 扩展性强:业务增长时可单独扩展特定库的硬件资源。
缺点
- 跨库查询复杂:需通过接口或中间件(如ShardingSphere)实现跨库关联查询,开发复杂度高。
- 事务一致性难保证:分布式事务(如Seata)引入的性能开销。
2)垂直分表:将一张表的不同列拆分到多个表中,比如将热点字段或者长度大的字段单独分表。
优点
- 冷热数据分离:将高频字段与低频字段拆分,减少单表数据量,提升查询效率。
- 减少锁冲突:不同业务字段拆分后,更新操作锁定更少数据。
缺点
- 查询复杂:跨表查询需关联操作,增加开发复杂度;
- 数据量大:仍需解决单表数据量过大问题。
三:分库分表策略
在选取分库分表的策略前,首先要了解什么是分片键。
分片键(Sharding Key):决定数据分布的字段,需选择高频查询条件(如用户 ID、订单 ID),避免跨分片查询(尽量选取连续的分片放到同一个库或表中)。
1.常见的分片策略
1)哈希取模:对分片字段进行哈希取模(shard_id = hash(key) % node_count)
。
优点:
- 数据分布均匀,不会容易出现冷热数据分离导致性能瓶颈。
缺点:
- 扩容时需迁移大量数据(如从 3 库扩至 4 库,需重新计算所有数据的分片)。
- 会出现跨分片查询的情况(如要查询订单金额前10,需要查询每段分片的前10然后比较)。
2)范围划分:按时间(如年 / 月)或数值范围(如用户 ID>1000 万)分片,适合递增数据(如订单)。
优点:
- 便于水平扩展,如果要进行扩容,只需要添加节点即可,无需像哈希取模一样要进行数据迁移。
- 连续分片能够尽可能的避免跨分片查询,提高查询性能。
缺点:
- 可能导致热点分片(如最新月份的数据量过大),会被频繁的读和写,从而导致单个分片的数据量访问过大,出现性能瓶颈。
四.分布式问题
1.分布式主键:分库分表后需保证不同库 / 表的主键唯一。
- UUID:简单易用,但作为主键性能较差(字符串类型,索引效率低)。
- 雪花算法(Snowflake):生成 64 位唯一整数,包含时间戳、机器 ID 等,性能高且有序,适用于高并发场景。
- 数据库自增序列:每个分片设置不同的起始值和步长(如库 1 起始 1、步长 3;库 2 起始 2、步长 3),避免主键冲突。
2.分布式事务:分布式事务需要解决跨节点(如跨数据库、跨服务)操作的原子性问题,即确保多个节点的操作要么全部成功,要么全部回滚,下面主要讲解seata中的两种模式。
在讲解两种模式前需要了解三个概念。
- TM(Transaction Manager):事务管理器
- TC(Transaction Coordinator):事务协调者
- RM(Resource Manager):资源管理器
特性 | AT 模式(Auto Transaction) | XA 模式 |
---|---|---|
一致性级别 | 最终一致(柔性事务) | 强一致(刚性事务) |
业务侵入性 | 无侵入(基于生成回滚日志) | 低侵入(需使用 Seata 提供的 XA 数据源) |
性能损耗 | 较低(仅在提交 / 回滚阶段有少量额外开销) | 较高(两阶段提交需等待所有分支事务响应) |
隔离性 | 基于全局锁实现读已提交(RC) | 支持可重复读(RR)等强隔离级别 |
适用场景 | 高并发、允许短暂不一致的业务(如电商订单、库存) | 金融级强一致场景(如资金转账、账户余额) |
数据库支持 | 关系型数据库(MySQL、Oracle 等) | 支持 XA 协议的数据库(MySQL 5.7+、PostgreSQL) |
1)AT模式
(1)一阶段(Prepare)
- 注册分支事务
- 生成回滚日志:将相关信息存入 UNDO_LOG 表。
- SQL 执行并提交
- 释放本地锁:本地事务提交,释放数据库行锁。
(2)二阶段(Commit/Rollback)分为提交和回滚两种情况
- 提交(Commit):TM(事务管理器)通知 RM(资源管理器)直接删除 UNDO_LOG,无需操作数据库。
- 回滚(Rollback):RM 通过 UNDO_LOG 中的记录恢复数据。
2)XA模式(两阶段提交模式)
XA 模式基于数据库原生 XA 协议实现强一致事务,遵循“两阶段提交(2PC)” 协议:
(2)一阶段(Prepare)
- TM 向所有 RM 发送
prepare
请求。 - RM 执行本地事务,但不提交,将事务资源锁定。
- RM 向 TM 返回
成功
或失败
。
(2)二阶段(Commit/Rollback)
- 若所有 RM 均返回成功,TM 发送
commit
,RM 提交本地事务; - 若任一 RM 失败,TM 发送
rollback
,RM 回滚本地事务。
五.Seata 中如何配置?
(一)AT 模式配置示例(基于 Spring Boot)
seata:enabled: trueapplication-id: ${spring.application.name}tx-service-group: my_test_tx_groupconfig:type: nacos # 配置中心类型nacos:server-addr: 127.0.0.1:8848registry:type: nacos # 注册中心类型nacos:server-addr: 127.0.0.1:8848data-source-proxy-mode: AT # 指定AT模式
关键步骤:
- 引入 Seata 客户端依赖;
- 配置数据源代理(
DataSourceProxy
); - 在需要分布式事务的方法上添加
@GlobalTransactional
注解。
(二)XA 模式配置示例
seata:data-source-proxy-mode: XA # 指定XA模式xa-override-boundary: true # 允许XA事务跨越多个本地事务
关键步骤:
- 使用 Seata 提供的
SeataDataSourceProxyXA
包装数据源; - 确保数据库开启 XA 支持(如 MySQL 需设置
innodb_support_xa=ON
); - 其余配置与 AT 模式类似。
六.总结
维度 | AT 模式 | XA 模式 |
---|---|---|
事务协调机制 | 基于 UNDO_LOG 和全局锁 | 基于数据库 XA 协议 |
资源锁定时间 | 仅在一阶段执行期间锁定(时间短) | 一阶段到二阶段提交期间一直锁定(时间长) |
性能 | 高(接近本地事务) | 低(两阶段提交开销大) |
回滚机制 | 自动根据 UNDO_LOG 回滚 | 依赖数据库回滚能力 |
隔离级别 | 读已提交(RC) | 可重复读(RR)等数据库原生级别 |
异常处理 | 需处理全局锁冲突和补偿失败 | 需处理事务悬挂和协调者故障 |
- AT 模式是 Seata 的默认模式,适合大多数高并发、最终一致的业务场景,通过无侵入的方式实现柔性事务。
- XA 模式适合对一致性要求极高的金融级业务,依赖数据库原生支持实现强一致,但性能较低。
- 实际应用中,建议根据业务特性混合使用不同模式(如核心链路用 XA,例如转账等功能,非核心用 AT),并结合监控系统及时发现和处理异常。