Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql主从同步与分库分表

MySQL主从同步与分库分表原理及实现方法

作者:短剑重铸之日

MySQL主从同步和分库分表技术是解决高并发和大数据量问题的关键,本文详细解析了这两项技术的原理、实现方法及最佳实践,帮助读者构建高性能的MySQL架构,感兴趣的朋友跟随小编一起看看吧

MySQL主从同步和分库分表是应对高并发、大数据量场景的两大核心技术,通过数据复制和水平/垂直拆分,有效解决了单点性能瓶颈和存储容量限制。主从同步实现了数据库的高可用性和读写分离,而分库分表则进一步提升了系统的扩展性和负载能力。本文将深入解析这两项技术的原理、实现方法及最佳实践,帮助您在实际项目中构建高性能的MySQL架构。

一、主从同步原理与架构

1.1 核心组件与工作流程

MySQL主从同步通过二进制日志(BINLOG)实现数据的异步/半同步复制。主库负责处理写操作并将变更记录到binlog中,从库通过IO线程获取binlog并写入本地的relay log,然后由SQL线程执行这些日志中的事件,最终实现与主库的数据一致。

工作流程详解

  1. 主库操作:当客户端在主库执行写操作时,InnoDB引擎首先将数据变更记录到Redo Log以确保事务持久性,随后将变更写入Binlog。
  2. IO线程传输:从库的IO线程通过长连接监听主库的Binlog变更,获取新事件后写入本地中继日志(Relay Log)。
  3. SQL线程执行:从库的SQL线程读取中继日志中的事件并重放,将变更应用到本地数据库,最终实现与主库的数据一致。

1.2 同步模式对比

MySQL主从同步支持三种模式,各有优缺点:

模式特点适用场景RTO/RPO
异步复制主库处理完SQL直接返回结果高写入性能要求,对数据一致性要求较低最高
半同步复制主库处理完SQL等待至少1个从完成平衡性能与一致性,多数生产环境使用中等
全同步复制主库处理完SQL等待所有从完成数据一致性要求极高,但性能最差最低

半同步复制实现机制:MySQL半同步复制依赖rpl_semi_sync_master插件,通过AFTER_SYNC或AFTER_COMMIT两种模式实现 。AFTER同步模式要求主库等待从库将Binlog写入中继日志,而AFTER提交模式则要求从库执行到SQL线程阶段。

半同步复制能显著降低数据丢失风险,但会增加约20%的写入延迟。配置时需设置rpl_semi_sync_master_timeout(超时时间,默认10000ms)和rpl_semi_sync_master enabled(启用状态) 。

1.3 主从同步配置步骤

1. 主库配置

配置参数详解

2. 从库配置

配置参数详解

3. 验证同步

-- 在主库插入测试数据
INSERT INTO test_table (id, name) VALUES (1, 'test');
-- 在从库查询数据
SELECT * FROM test_table WHERE id=1;

若数据成功同步,则配置成功。

二、分库分表策略与实现

2.1 分库分表类型

分库分表主要分为垂直分库/分表和水平分库/分表,通常建议先进行垂直拆分,再考虑水平拆分

垂直分库适用场景:

水平分表适用场景

2.2 垂直分库实现

1. 创建独立业务库

-- 创建用户库和订单库
CREATE DATABASE user_db;
CREATE DATABASE order_db;
-- 将用户表迁移到用户库
RENAME TABLE original_db.user TO user_db.user;
-- 将订单表迁移到订单库
RENAME TABLE original_db.order TO order_db.order;

2. 应用层路由配置

在应用层代码中设置不同业务的数据库连接:

// 用户操作使用user_db连接
DataSource userDataSource = setupDataSource("user_db");
// 订单操作使用order_db连接
DataSource orderDataSource = setupDataSource("order_db");

3. 分片键选择原则

分片键的选择直接影响分库分表的效果,需遵循以下原则:

2.3 垂直分表示例

1. 原始宽表结构

CREATE TABLE user (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    email VARCHAR(100),
    phone VARCHAR(20),
    address TEXT,
    profile JSON,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

2. 分表后结构

-- 核心信息表
CREATE TABLE user Core (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    email VARCHAR(100),
    phone VARCHAR(20),
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 扩展信息表
CREATE TABLE user Extend (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    address TEXT,
    profile JSON,
    FOREIGN KEY (user_id) REFERENCES user Core(id)
);

2.4 水平分表示例

1. 按用户ID取模分

-- 创建4张分表
CREATE TABLE user_001 (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    name VARCHAR(50),
    email VARCHAR(100),
    phone VARCHAR(20),
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 类似创建user_002、user_003、user_004表
-- 设置主键步长避免冲突
ALTER TABLE user_001 AUTO_INCREMENT=1;
ALTER TABLE user_002 AUTO_INCREMENT=2;
ALTER TABLE user_003 AUTO_INCREMENT=3;
ALTER TABLE user_004 AUTO_INCREMENT=4;
-- 设置全局步长
SET GLOBAL auto_increment_increment=4;

2. 数据操作示例

public class UserDAO {
    private static final int SHARD_COUNT = 4;
    // 获取分片编号
    private int getShardNumber(long userId) {
        return (int) (userId % SHARD_COUNT);
    }
    // 插入用户
    public void insertUser(User user) {
        int shardNumber = getShardNumber(user.getId());
        String table = "user_" + String.format("%03d", shardNumber);
        try (Connection conn = getDataSource(shardNumber). connections()) {
            conn预备语句(
                "INSERT INTO " + table + " (user_id, name, email, phone) " +
                "VALUES (?, ?, ?, ?)"
            ). executeUpdate();
        }
    }
    // 查询用户
    public User getUser(long userId) {
        int shardNumber = getShardNumber(userId);
        String table = "user_" + String.format("%03d", shardNumber);
        try (Connection conn = getDataSource(shardNumber). connections()) {
           预备语句ps = conn预备语句(
                "SELECT * FROM " + table + " WHERE user_id = ?"
            );
            ps.setLong(1, userId);
            结果集rs = ps执行查询();
            if (rs.next()) {
                return mapToUser(rs);
            }
        }
        return null;
    }
    // 获取对应分片的数据库连接
    private DataSource getDataSource(int shardNumber) {
        switch (shardNumber) {
            case 0:
                return userDataSource0;
            case 1:
                return userDataSource1;
            case 2:
                return userDataSource2;
            case 3:
                return userDataSource3;
            default:
                throw new IllegalArgumentException("无效的分片编号");
        }
    }
}

三、分库分表后的挑战与解决方案

3.1 分布式事务处理(若感兴趣,评论区告诉我,会单独出一期详细讲述)

1. XA事务实现

XA事务实现步骤

  1. 准备阶段:事务协调器向所有参与的数据库发送"准备"请求,每个数据库执行本地事务操作但不提交,记录事务日志并持有相关资源锁。
  2. 提交阶段:若所有数据库都反馈"同意提交",协调器向所有数据库发送"提交"指令,释放资源锁并提交事务。若有任何数据库失败,则发送"回滚"指令 。

XA事务优缺点

2. TCC事务模式

TCC事务实现步骤

  1. Try阶段:检查资源是否满足要求(如检查账户余额是否足够),若符合条件则对资源进行锁定(如冻结可扣减金额)。
  2. Confirm阶段:若Try阶段所有操作都成功,则执行正式提交(如实际扣减金额)。
  3. Cancel阶段:若Try阶段有失败,则执行回滚(如释放冻结金额) 。

TCC事务优缺点

3.2 跨库JOIN查询优化(若感兴趣,评论区告诉我,会单独出一期详细讲述ShardingSphere)

1. 业务层聚合

        分别查询用户表、订单表,再应用层对结果合并

2. 宽表同步

实现方法

3.3 主键冲突解决方案

1. 步长分配法(使用较少)

实现步骤

在主库设置全局步长

SET GLOBAL auto_increment_increment = 4;

在每个分片表设置初始值:

ALTER TABLE user_001 AUTO_INCREMENT = 1;
ALTER TABLE user_002 AUTO_INCREMENT = 5;
ALTER TABLE user_003 AUTO_INCREMENT = 9;
ALTER TABLE user_004 AUTO_INCREMENT = 13;

每个分片表的步长与全局步长一致,确保主键全局唯一 

步长分配法优缺点

2. 雪花算法实现(高频

实现步骤

  1. 设计分布式ID生成器,包含时间戳、机器ID和序列号。
  2. 为每个分片分配唯一的机器ID。
  3. 在分片间共享序列号,避免重复。

雪花算法优缺点

四、实际操作指南

4.1 主从同步完整配置流程

1. 环境准备

# 主库IP: 192.168.1.100
# 从库IP: 192.168.1.101
# 安装MySQL 8.0
sudo yum install mysql-server
sudo systemctl start mysqld
sudo systemctl enable mysqld

2. 主库配置

# 获取初始化密码
sudo grep 'temporary password' /var/log/mysqld.log
# 登录MySQL
mysql -u root -p
# 修改root密码
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'NewPassword123';
# 创建复制用户
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED WITH mysql_native_password BY 'ReplPassword123';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES;
# 修改my.cnf配置文件
sudo vi /etc/my.cnf
# 添加以下配置
[mysqld]
server-id=1
log-bin=mysql-bin
binlog-do-db=your_db
binlog-checksum=NONE
binlog-format=ROW
sync_binlog=1
innodb_flush_log_at_trx_commit=1
gtid_mode=ON
enforce_gtid_consistency=ON

3. 从库配置

# 修改my.cnf配置文件
sudo vi /etc/my.cnf
# 添加以下配置
[mysqld]
server-id=2
log-bin=mysql-bin
replicate-do-db=your_db
replicate_binlog checksum=0
replicate_binlog format=MIXED
gtid_mode=ON
enforce_gtid_consistency=ON
read-only=ON

4. 初始化数据同步

# 主库导出数据
mysqldump -u root -p --single-transaction your_db > dump.sql
# 从库导入数据
mysql -u root -p your_db < dump.sql
# 从库设置主库信息
CHANGE MASTER TO
    MASTER_HOST='192.168.1.100',
    MASTER_USER='repl',
    MASTER_PASSWORD='ReplPassword123',
    MASTER AUTO_POSITION=1;
# 启动从库复制
START SLAVE;
# 检查复制状态
SHOW SLAVE STATUS\G

关键指标检查

4.2 分库分表示例

1. 手动分库分表示例

-- 创建分片表
CREATE TABLE user_001 (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    name VARCHAR(50),
    email VARCHAR(100),
    phone VARCHAR(20),
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 创建其他分片表
CREATE TABLE user_002 LIKE user_001;
CREATE TABLE user_003 LIKE user_001;
CREATE TABLE user_004 LIKE user_001;
-- 设置主键步长
ALTER TABLE user_001 AUTO_INCREMENT=1;
ALTER TABLE user_002 AUTO_INCREMENT=5;
ALTER TABLE user_003 AUTO_INCREMENT=9;
ALTER TABLE user_004 AUTO_INCREMENT=13;
-- 设置全局步长
SET GLOBAL auto_increment_increment=4;

2. 数据操作路由逻辑

// 使用ShardingSphere的ShardingSphereDataSource
ShardingSphereDataSource dataSource = ShardingSphereDataSourceFactory.createDataSource(
    createDataSourceMap(),
    createRuleConfiguration(),
    new Properties()
);
// 插入用户
public void insertUser(User user) {
    try (Connection conn = dataSource.getConnection()) {
        conn预备语句(
            "INSERT INTO t_user (user_id, name, email, phone) " +
            "VALUES (?, ?, ?, ?)"
        ). executeUpdate();
    }
}
// 查询用户
public User getUser(long userId) {
    try (Connection conn = dataSource.getConnection()) {
       预备语句ps = conn预备语句(
            "SELECT * FROM t_user WHERE user_id = ?"
        );
        ps.setLong(1, userId);
        结果集rs = ps执行查询();
        if (rs.next()) {
            return mapToUser(rs);
        }
    }
    return null;
}

3. ShardingSphere中间件配置

# ShardingSphere配置文件
rules:
- !SHARDING
  tables:
    t_user:
      actualDataNodes: ds_${0..3}.t_user_${0..3}
      tableStrategy:
        standard:
          shardingColumn: user_id
          shardingAlgorithmName: inline
  shardingAlgorithms:
    inline:
      type: INLINE
      props:
        algorithm-expression: t_user_${user_id % 4}

配置说明

五、最佳实践与性能优化

5.1 主从同步优化建议

1. 配置优化

优化建议

2. 性能监控指标

监控方法

5.2 分库分表优化策略

1. 跨库查询优化

优化方法

六、总结与实施建议

MySQL主从同步和分库分表是应对高并发、大数据量场景的核心技术,通过合理设计和配置,可以显著提升系统的性能和可靠性。在实际实施中,建议遵循以下原则:

  1. 先垂直后水平:优先按业务模块进行垂直分库分表,再考虑水平分片 
  2. 逐步扩展:从简单的读写分离开始,随着数据量增长逐步引入分库分表 
  3. 工具辅助:使用Mycat、ShardingSphere等中间件简化分库分表实现 
  4. 监控先行:建立完善的监控体系,跟踪复制延迟、分片负载等关键指标 
  5. 数据一致性:在性能与一致性之间找到平衡点,根据业务需求选择合适的事务处理方案

结语: 至此,《7天读懂MySQL》已完结,你都懂了吗?

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

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