Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql分库分表

MySQL分库分表的实践示例

作者:没事学AI

MySQL分库分表适用于数据量大或并发压力高的场景,核心技术包括水平/垂直分片和分库,需应对分布式事务、跨库查询等挑战,通过中间件和解决方案实现,最佳实践为合理策略、备份恢复、监控调优及预留扩展,感兴趣的朋友跟随小编一起看看吧

一、分库分表的触发条件

在MySQL数据库的使用过程中,当数据量增长到一定规模时,单库单表的架构会面临性能瓶颈,此时就需要考虑分库分表。以下是常见的触发场景:

1.1 数据量阈值

1.2 并发压力

当数据库的并发连接数过高,超过单库的处理能力时,会出现连接超时、锁等待等问题。

二、分库分表的核心技术模块

2.1 水平分表

水平分表是将一个表中的数据按照某种规则(如范围、哈希)拆分成多个结构相同的子表,每个子表只包含一部分数据。

2.1.1 技术原理

2.1.2 案例与代码实现

案例:一个电商平台的订单表orders,包含字段order_id(订单ID)、user_id(用户ID)、order_time(下单时间)等,数据量达到2000万,需要进行水平分表。采用按order_id范围分片,每500万订单ID为一个区间,分为4个子表orders_1orders_2orders_3orders_4

代码实现

-- 创建分表
CREATE TABLE orders_1 (
  order_id BIGINT NOT NULL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  order_time DATETIME NOT NULL,
  -- 其他字段
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
WHERE order_id BETWEEN 1 AND 5000000;
CREATE TABLE orders_2 (
  order_id BIGINT NOT NULL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  order_time DATETIME NOT NULL,
  -- 其他字段
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
WHERE order_id BETWEEN 5000001 AND 10000000;
CREATE TABLE orders_3 (
  order_id BIGINT NOT NULL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  order_time DATETIME NOT NULL,
  -- 其他字段
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
WHERE order_id BETWEEN 10000001 AND 15000000;
CREATE TABLE orders_4 (
  order_id BIGINT NOT NULL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  order_time DATETIME NOT NULL,
  -- 其他字段
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
WHERE order_id BETWEEN 15000001 AND 20000000;
-- 创建视图,方便查询
CREATE VIEW orders AS
SELECT * FROM orders_1
UNION ALL
SELECT * FROM orders_2
UNION ALL
SELECT * FROM orders_3
UNION ALL
SELECT * FROM orders_4;

2.2 垂直分表

垂直分表是将一个表中字段较多的表,按照字段的热点程度、访问频率等,拆分成多个包含部分字段的子表。

2.2.1 技术原理

2.2.2 案例与代码实现

案例:用户表user包含字段user_idusernamephoneaddressintroduction等,其中usernamephone经常被查询,addressintroduction不常被查询,进行垂直分表。

代码实现

-- 创建用户基本信息表(热点字段)
CREATE TABLE user_base (
  user_id BIGINT NOT NULL PRIMARY KEY,
  username VARCHAR(50) NOT NULL,
  phone VARCHAR(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 创建用户详细信息表(冷字段)
CREATE TABLE user_detail (
  user_id BIGINT NOT NULL PRIMARY KEY,
  address VARCHAR(200),
  introduction TEXT,
  FOREIGN KEY (user_id) REFERENCES user_base(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.3 分库

分库是将多个表按照一定的规则拆分到不同的数据库中,以降低单库的压力。

2.3.1 技术原理

2.3.2 案例与代码实现

案例:一个大型电商平台,包含用户模块、商品模块、订单模块,将这三个模块的表分别放在user_dbproduct_dborder_db三个数据库中。

代码实现

-- 在user_db数据库中创建用户相关表
USE user_db;
CREATE TABLE user_base (
  user_id BIGINT NOT NULL PRIMARY KEY,
  username VARCHAR(50) NOT NULL,
  phone VARCHAR(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 在order_db数据库中创建订单相关表
USE order_db;
CREATE TABLE orders_1 (
  order_id BIGINT NOT NULL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  order_time DATETIME NOT NULL
  -- 其他字段
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

三、分库分表带来的新问题

3.1 分布式事务

分库分表后,一个业务操作可能涉及多个数据库或多个表,此时保证事务的一致性变得复杂。

3.1.1 问题说明

在单库单表中,MySQL的ACID特性可以保证事务的一致性。但在分布式环境下,多个数据库之间无法直接使用本地事务,可能出现部分操作成功、部分操作失败的情况。

3.1.2 案例

用户下单操作,需要在订单库中创建订单记录,同时在库存库中减少商品库存。如果订单创建成功,但库存减少失败,就会出现数据不一致。

3.2 跨库查询

分库分表后,查询可能需要涉及多个数据库或多个表,增加了查询的复杂度。

3.2.1 问题说明

例如,查询某个用户在多个月份的订单,由于订单表按月份分表且可能分布在不同的库中,需要同时查询多个库和表,然后合并结果。

3.2.2 案例

查询用户user_id=100在2023年1月和2月的订单,需要分别查询order_db1中的orders_202301表和order_db2中的orders_202302表,然后将结果合并。

3.3 数据迁移与扩容

随着业务的发展,可能需要对分库分表的方案进行调整,如增加分表数量、调整分片规则等,这会涉及到数据的迁移和扩容。

3.3.1 问题说明

数据迁移过程中需要保证数据的一致性和完整性,同时要尽量减少对业务的影响。扩容时需要考虑新的分片规则如何与原有规则兼容。

3.3.2 案例

原来订单表按照order_id范围分表,每500万一个表,现在由于业务增长,需要将每个分表的范围调整为250万,需要将原有的orders_1表(1-500万)拆分成orders_1(1-250万)和orders_5(251-500万),并迁移数据。

四、分库分表的解决方案

4.1 中间件方案

使用专门的分库分表中间件,如Sharding-JDBC、MyCat等,这些中间件可以帮助开发者透明地实现分库分表,减少手动处理的复杂度。

4.1.1 Sharding-JDBC

代码实现(Spring Boot整合Sharding-JDBC)

spring:
  shardingsphere:
    datasource:
      names: db0,db1
      db0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/order_db0
        username: root
        password: root
      db1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/order_db1
        username: root
        password: root
    rules:
      sharding:
        tables:
          orders:
            actual-data-nodes: db${0..1}.orders_${0..1}
            database-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order_db_inline
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order_table_inline
        sharding-algorithms:
          order_db_inline:
            type: INLINE
            props:
              algorithm-expression: db${order_id % 2}
          order_table_inline:
            type: INLINE
            props:
              algorithm-expression: orders_${order_id % 2}
    props:
      sql-show: true

4.2 分布式事务解决方案

4.2.1 两阶段提交(2PC)

4.2.2 最终一致性方案(如TCC、SAGA)

TCC案例代码(伪代码)

// 订单服务
public interface OrderTCCService {
    // Try阶段:创建订单,预留库存
    boolean tryCreateOrder(OrderDTO orderDTO);
    // Confirm阶段:确认创建订单
    boolean confirmCreateOrder(OrderDTO orderDTO);
    // Cancel阶段:取消创建订单,释放库存
    boolean cancelCreateOrder(OrderDTO orderDTO);
}
// 库存服务
public interface InventoryTCCService {
    // Try阶段:扣减库存预留
    boolean tryDeductInventory(InventoryDTO inventoryDTO);
    // Confirm阶段:确认扣减库存
    boolean confirmDeductInventory(InventoryDTO inventoryDTO);
    // Cancel阶段:取消扣减库存,恢复库存
    boolean cancelDeductInventory(InventoryDTO inventoryDTO);
}

五、分库分表的最佳实践

5.1 合理选择分片策略

5.2 做好数据备份与恢复

分库分表后的数据分布在多个库和表中,需要制定完善的数据备份策略,定期备份数据,并确保备份数据可以正常恢复。

5.3 监控与调优

5.4 考虑未来扩展性

在设计分库分表方案时,要考虑未来业务的增长,预留一定的扩展空间,使方案能够方便地进行扩容和调整。例如,采用可扩展的分片规则,当数据量增长到一定程度时,可以方便地增加新的分库分表。

到此这篇关于MySQL分库分表的实践与挑战的文章就介绍到这了,更多相关mysql分库分表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文