您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# MySQL读写分离写完读不到问题如何解决
## 引言
在现代互联网应用中,数据库往往是性能瓶颈所在。为了提升数据库的并发处理能力,读写分离架构被广泛采用:主库(Master)负责写操作,从库(Slave)负责读操作。这种架构虽然能显著提升系统性能,但会引入一个典型问题——**写完读不到**,即主库写入后,从库无法立即读取到最新数据。
本文将深入分析MySQL读写分离架构下"写完读不到"问题的产生原因,并给出多种解决方案,帮助开发者根据业务场景选择最合适的处理方式。
## 一、问题现象与原因分析
### 1.1 典型场景示例
```sql
-- 客户端向主库执行写入
INSERT INTO orders (user_id, amount) VALUES (1001, 500);
-- 立即从从库查询(可能查不到刚插入的记录)
SELECT * FROM orders WHERE user_id = 1001;
出现该问题的核心原因是主从复制延迟(Replication Lag),具体包括:
根据业务场景不同,数据不一致的容忍度也不同:
业务类型 | 容忍度 | 示例 |
---|---|---|
金融交易 | 零容忍 | 支付后查余额 |
社交内容 | 可容忍 | 发帖后显示 |
日志统计 | 高容忍 | 操作日志记录 |
解决方案可分为三大类:
// Spring配置多数据源
@Bean
@Primary
public DataSource masterDataSource() {
// 主库配置
}
@Bean
public DataSource slaveDataSource() {
// 从库配置
}
// 使用注解切换数据源
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReadMaster {
}
// AOP切面处理
@Around("@annotation(readMaster)")
public Object around(ProceedingJoinPoint joinPoint, ReadMaster readMaster) {
DynamicDataSource.setDataSource("master");
try {
return joinPoint.proceed();
} finally {
DynamicDataSource.clear();
}
}
可制定路由规则: - 订单表相关查询强制走主库 - UPDATE/DELETE操作后的SELECT走主库
优点: - 实现简单 - 强一致性保证
缺点: - 主库压力增大 - 失去读写分离优势
-- 主库执行写入后获取GTID
SELECT @@GLOBAL.GTID_EXECUTED;
-- 从库等待直到应用该GTID
SELECT WT_FOR_EXECUTED_GTID_SET('aaa-bbb-ccc:10', 2); -- 超时2秒
-- 主库查询binlog位置
SHOW MASTER STATUS;
-- 从库等待
SELECT MASTER_POS_WT('mysql-bin.000002', 154, 10);
def write_then_read(query):
# 执行写操作
execute_master("INSERT...")
# 获取主库最后GTID
gtid = query_master("SELECT @@GLOBAL.GTID_EXECUTED")
# 从库等待
wait_slave(f"SELECT WT_FOR_EXECUTED_GTID_SET('{gtid}', 1)")
# 执行查询
return query_slave(query)
# 配置主从规则
spring:
shardingsphere:
masterslave:
name: ms_ds
master-data-source-name: master
slave-data-source-names: slave1,slave2
load-balance-algorithm-type: round_robin
props:
max.connections.size.per.query: 5
sql.show: true
# 开启读主库配置
master-slave.read.write-splitting.allow-read-write-splitting-when-replication-delay: false
master-slave.read.write-splitting.replication-delay-milliseconds-threshold: 1000
-- 定义查询规则
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply)
VALUES (1,1,'^SELECT.*FOR UPDATE',10,1); -- 主库
INSERT INTO mysql_query_rules (rule_id,active,match_pattern,destination_hostgroup,apply)
VALUES (2,1,'^SELECT',20,1); -- 从库
-- 特殊规则:写后读主
INSERT INTO mysql_query_rules (rule_id,active,flagIN,flagOUT,apply)
VALUES (3,1,0,100,0);
INSERT INTO mysql_query_rules (rule_id,active,flagIN,match_pattern,destination_hostgroup,apply)
VALUES (4,1,100,'^SELECT',10,1);
# 主库my.cnf配置
[mysqld]
plugin-load = "rpl_semi_sync_master=semisync_master.so"
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_timeout = 10000 # 10秒超时
# 从库配置
plugin-load = "rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled = 1
-- 初始化组复制
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
-- 查看节点状态
SELECT * FROM performance_schema.replication_group_members;
方案 | 一致性保证 | 性能影响 | 复杂度 |
---|---|---|---|
原生MySQL路由 | 弱 | 低 | 低 |
ProxySQL | 中等 | 中 | 中 |
MGR | 强 | 高 | 高 |
业务层控制 | 可定制 | 可变 | 高 |
public Object readAfterWrite(String key, Supplier<Object> loader) {
// 先查缓存
Object value = cache.get(key);
if (value != null) {
return value;
}
// 查数据库(根据标记决定查主/从)
if (writeFlag.get(key)) {
value = masterDb.query(loader);
writeFlag.remove(key);
} else {
value = slaveDb.query(loader);
}
// 回填缓存
cache.set(key, value);
return value;
}
-- 数据表增加版本字段
ALTER TABLE orders ADD COLUMN data_version INT DEFAULT 0;
-- 写入时更新版本
UPDATE orders SET amount=100, data_version=data_version+1 WHERE id=1;
-- 读取时校验版本
SELECT * FROM orders WHERE id=1 AND data_version >= ?;
-- 查看从库延迟
SHOW SLAVE STATUS\G
-- 关注:
-- Seconds_Behind_Master
-- Slave_SQL_Running_State
-- 性能监控
SELECT * FROM sys.schema_table_lock_waits;
SELECT * FROM performance_schema.events_statements_summary_by_digest;
从库配置优化:
[mysqld]
slave_parallel_workers = 8
slave_parallel_type = LOGICAL_CLOCK
网络优化:主从同机房部署
大事务拆分:将大事务拆分为小批次
是否需要强一致?
├── 是 → 业务能否接受延迟?
│ ├── 能 → 等待复制方案
│ └── 不能 → 强制读主
└── 否 → 采用最终一致+业务补偿
方案 | 适用场景 | 一致性级别 | 性能影响 |
---|---|---|---|
强制读主 | 金融核心业务 | 强一致 | 高 |
等待复制 | 可容忍短延迟 | 最终一致 | 中 |
中间件路由 | 常规业务 | 可配置 | 低 |
MGR | 高可用要求 | 强一致 | 较高 |
MySQL读写分离架构下的”写完读不到”问题需要根据业务特点选择合适的解决方案:
实际生产中,建议通过完善的监控掌握主从延迟情况,结合业务特点进行技术选型。随着MySQL 8.0的普及,诸如MGR等新特性为读写分离场景提供了更多可能性,值得持续关注。
作者注:本文讨论的方案需要根据实际MySQL版本和业务场景进行调整,生产环境实施前务必充分测试。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。