You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

数据库读写分离功能说明

概述

本项目已集成数据库读写分离功能,通过修改 Db.java 类实现了自动的读写分离路由。该功能具有以下特点:

  • 自动检测:根据 application.yaml 配置自动检测是否存在从库配置
  • 智能路由:读操作自动路由到从库,写操作强制使用主库
  • 负载均衡:多个从库时随机选择一个进行读取
  • 故障转移:从库连接失败时自动回退到主库
  • 零侵入:现有代码无需修改,透明支持读写分离

配置方式

1. 主库配置(必须)

application_dev.yamlapplication_pro.yaml 中配置主库:

mysql:
  driverClassName: com.mysql.cj.jdbc.Driver
  user: root
  password: 123456
  jdbcUrl: jdbc:mysql://localhost:3306/dswork?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true

2. 从库配置(可选)

如果需要启用读写分离,添加从库配置:

mysql:
  driverClassName: com.mysql.cj.jdbc.Driver
  user: root
  password: 123456
  jdbcUrl: jdbc:mysql://localhost:3306/dswork?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
  
  # 从库1
  slave1:
    driverClassName: com.mysql.cj.jdbc.Driver
    user: root
    password: 123456
    jdbcUrl: jdbc:mysql://slave1:3306/dswork?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
  
  # 从库2
  slave2:
    driverClassName: com.mysql.cj.jdbc.Driver
    user: root
    password: 123456
    jdbcUrl: jdbc:mysql://slave2:3306/dswork?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true

使用方式

1. 自动读写分离(推荐)

现有代码无需修改,系统会自动根据 SQL 类型进行路由:

// 读操作 - 自动路由到从库
List<Record> users = Db.find("select * from user where status = ?", 1);
Record user = Db.findFirst("select * from user where id = ?", 123);
List<Record> result = Db.query("select count(*) from user");

// 写操作 - 强制使用主库
Db.update("insert into user(name, email) values(?, ?)", "张三", "zhangsan@example.com");
Db.update("update user set status = ? where id = ?", 1, 123);
Db.delete("delete from user where id = ?", 123);

// 事务操作 - 强制使用主库
Db.tx(() -> {
    Db.update("insert into user(name) values(?)", "李四");
    Db.update("update user set status = 1 where name = ?", "李四");
    return true;
});

2. 强制指定数据库

如果需要强制使用特定数据库,可以使用以下方法:

// 强制使用主库查询(用于需要强一致性的场景)
List<Record> users = Db.queryFromMaster("select * from user where id = ?", 123);
Record user = Db.findFirstFromMaster("select * from user where id = ?", 123);

// 强制使用指定从库查询
List<Record> users = Db.queryFromSlave(0, "select * from user"); // 使用第1个从库

3. 查看读写分离状态

// 获取读写分离状态信息
String status = Db.getReadWriteSeparationStatus();
System.out.println(status);

自动路由规则

读操作(路由到从库)

以下 SQL 语句会自动路由到从库:

  • SELECT 查询语句
  • SHOW 语句
  • DESC / DESCRIBE 语句
  • EXPLAIN 语句

对应的方法包括:

  • Db.find()Db.findFirst()Db.findById()
  • Db.query()Db.queryFirst()Db.queryXxx()
  • Db.paginate() 分页查询
  • Db.findByCache() 缓存查询

写操作(强制使用主库)

以下操作强制使用主库:

  • INSERTUPDATEDELETE 语句
  • 所有事务操作
  • Db.save()Db.update()Db.delete()
  • Db.tx() 事务方法

性能优化建议

  1. 合理配置从库数量:根据读写比例配置适当数量的从库
  2. 使用连接池:确保主从库都配置了合适的连接池参数
  3. 监控从库延迟:定期检查主从同步延迟,避免读取到过期数据
  4. 强一致性场景:对于需要强一致性的查询,使用 queryFromMaster() 方法

注意事项

  1. 主从同步延迟:从库可能存在数据同步延迟,对于实时性要求高的查询建议使用主库
  2. 事务一致性:所有事务操作都在主库执行,确保数据一致性
  3. 配置格式:从库配置必须以 slave 开头,后跟数字(如 slave1slave2
  4. 故障处理:从库连接失败时会自动回退到主库,不会影响业务正常运行

启动日志

系统启动时会输出读写分离状态信息:

[Db] 读写分离已启用,发现 2 个从库配置

[Db] 未发现从库配置,使用单库模式

故障排查

如果读写分离功能异常,请检查:

  1. 配置文件中从库配置格式是否正确
  2. 从库连接信息是否正确
  3. 从库服务是否正常运行
  4. 网络连接是否正常

系统会在控制台输出相关错误信息,便于排查问题。