内容简介:由于
Proxy
对比
- 客户端直连
- 少了一层
Proxy转发,查询性能稍微好一点 - 整体架构简单,排查问题方便
- 需要了解 后端部署细节 ,在出现主从切换、库迁移时,客户端有感知,需要调整数据库连接信息
- 一般伴随着一个负责管理后端的组件,例如
ZooKeeper
- 一般伴随着一个负责管理后端的组件,例如
- 少了一层
- Proxy – 发展趋势
- 对客户端友好,客户端不需要关注后端细节,但后端维护成本较高
-
Proxy也需要 高可用 架构,带Proxy的整体架构相对复杂
过期读
由于 主从延迟 ,主库上执行完一个更新事务后,立马在从库上执行查询,有可能读到刚刚的事务更新之前的状态
解决方案
强制走主库
- 将查询请求做 分类
- 必须要拿到最新结果的请求,强制将其发送到主库上
- 可以读到旧数据的请求,将其发到从库上
- 如果完全不能接受过期读,例如金融类业务,相当于放弃读写分离,所有的读写压力都在主库上
SLEEP
- 主库更新后,读从库之前先
SLEEP一下,类似于SELECT SLEEP(1)- 基于的假设:大多数主从延时在1秒内
- 卖家发布商品后,用
Ajax直接把客户端输入的内容作为“新的商品”显示在页面上,而非真正的做数据库查询- 等卖家再次刷新页面,其实主从已经同步完成了,也达到了
SLEEP的效果
- 等卖家再次刷新页面,其实主从已经同步完成了,也达到了
-
SLEEP方案解决了类似场景下的过期读问题,但存在 不精确 的问题- 如果主从延时只有0.5秒,也会等到1秒
- 如果主从延迟超过了1秒,依然会出现 过期读 的问题
判断主从无延迟
-
SLOW SLAVE STATUS.Seconds_Behind_Master- 每次在从库执行查询请求前,先判断
Seconds_Behind_Master是否等于0 -
Seconds_Behind_Master=0才能执行查询请求 -
Seconds_Behind_Master的精度为 秒 ,如果需要更高精度,可以考虑对比 位点 和GTID
- 每次在从库执行查询请求前,先判断
- 位点
-
Master_Log_File和Read_Master_Log_Pos,表示 读到的主库的最新位点 -
Relay_Master_Log_File和Exec_Master_Log_Pos,表示 从库执行的最新位点 -
Master_Log_File=Relay_Master_Log_File和Read_Master_Log_Pos=Exec_Master_Log_Pos- 表示接收到的日志已经 同步完成
-
-
GTID-
Auto_Position=1,表示 主从关系 使用了GTID协议 -
Retrieved_Gtid_Set,表示从库 收到 的所有日志的GTID集合 -
Executed_Gtid_Set,表示从库所有 已经执行完成 的GTID集合
-
mysql> SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Log_File: master-bin.000003
Read_Master_Log_Pos: 484
Relay_Log_File: relay-bin.000003
Relay_Log_Pos: 699
Relay_Master_Log_File: master-bin.000003
Exec_Master_Log_Pos: 484
Seconds_Behind_Master: 0
Master_UUID: b0bda503-3cf1-11e9-8c3a-0242ac110002
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Retrieved_Gtid_Set: b0bda503-3cf1-11e9-8c3a-0242ac110002:1-6
Executed_Gtid_Set: b0bda503-3cf1-11e9-8c3a-0242ac110002:1-6,ba0b2f12-3cf1-11e9-9c40-0242ac110003:1-5
Auto_Position: 1
不精确
-
binlog在主从之间的状态- 主库执行完成,写入
binlog,反馈给客户端 -
binlog被主库发送到从库,从库收到 - 从库执行
binlog(应用relaylog)
- 主库执行完成,写入
- 上面判断的 主从无延迟 : 从库收到的日志都执行完成了
- 并没有考虑这部分日志:客户端已经收到提交确认,但从库还未收到
- 主库上执行完成了3个事务:
trx1、trx2和trx3 -
trx1和trx2已经传到从库,并且已经执行完成了 -
trx3在主库执行完成后,并且已经回复给客户端,但还未传到从库中 - 如果此时在从库上执行查询请求,按上面的逻辑,从库已经 没有同步延迟 了,但还是查不到
trx3的变更,出现了 过期读
SEMI-SYNC
SEMI-SYNC设计
- 事务提交到时候,主库把
binlog发给从库 - 从库收到
binlog后,发回给主库一个ACK,表示收到了 - 主库收到这个
ACK以后,才能给客户端返回事务完成的确认
小结
- 启用了
SEMI-SYNC- 所有给客户端发送过确认的事务,都确保了 某一个从库 已经收到了这个日志
-
SEMI-SYNC+位点的方案,只针对 一主一从 的场景是成立的- 在 一主多从 的场景里,主库只要等到一个从库的
ACK,就开始给客户端返回确认 - 如果对刚刚响应了
ACK的从库执行查询请求(+判断 主从无延迟 ),能够确保读到最新的数据,否则可能是 过期读
- 在 一主多从 的场景里,主库只要等到一个从库的
- 如果在业务高峰期,主库的位点或者GTID集合更新很快,从库可能一直跟不上主库,导致从库迟迟无法响应查询请求
- 在出现 持续延迟 的情况下,可能会出现 过度等待 (判断 主从无延迟 )的情况
等主库位点
SELECT MASTER_POS_WAIT(file, pos[, timeout]);
- 在 从库 上执行
- 参数
file和pos指的是 主库 上的文件名和位置 -
timeout单位为秒 - 返回正整数,表示从 命令开始执行 ,到应用完
file和pos,总共 执行了多少事务- 如果在执行期间,从库的同步线程发生异常,返回NULL
- 如果等待超过
timeout秒,返回-1 - 如果刚开始执行的时候,发现 已经执行 过这个位置,返回
0
样例
先在 MySQL A 执行 trx1 ,然后在 MySQL B 执行查询请求
- 事务
trx1更新完成后,马上执行SHOW MASTER STATUS,得到当前主库执行到的File和Position - 选择一个从库执行查询语句
- 在该从库上先执行
SELECT MASTER_POS_WAIT(File, Position, 1) - 如果返回值
>=0,则直接在这个从库上执行查询语句 - 否则,在主库上执行查询语句
- 一种退化机制,针对 主从延时不可控 的场景
-- MySQL A
mysql> SHOW MASTER STATUS;
+-------------------+----------+--------------+------------------+------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+------------------------------------------+
| master-bin.000003 | 643 | | | b0bda503-3cf1-11e9-8c3a-0242ac110002:1-7 |
+-------------------+----------+--------------+------------------+------------------------------------------+
-- MySQL B
mysql> SELECT MASTER_POS_WAIT('master-bin.000003',643,1);
+--------------------------------------------+
| MASTER_POS_WAIT('master-bin.000003',643,1) |
+--------------------------------------------+
| 0 |
+--------------------------------------------+
等GTID
-- 等待,直到这个库执行的事务中包含传入的gtid_set,返回0 -- 超时返回1 SELECT WAIT_FOR_EXECUTED_GTID_SET(gtid_set, 1);
样例
- 从 MySQL 5.7.6开始,允许在执行完 更新类事务 后,把这个事务的
GTID返回给客户端 - 事务
trx1更新完成后,从返回结果中直接获取trx1的GTID,记为gtid1 - 选择一个从库执行查询语句
- 在该从库上先执行
SELECT WAIT_FOR_EXECUTED_GTID_SET(gtid1, 1) - 如果返回
0,则直接在这个从库上执行查询语句 - 否则,在主库上执行查询语句
-- MySQL A
mysql> SHOW MASTER STATUS;
+-------------------+----------+--------------+------------------+------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+------------------------------------------+
| master-bin.000003 | 643 | | | b0bda503-3cf1-11e9-8c3a-0242ac110002:1-7 |
+-------------------+----------+--------------+------------------+------------------------------------------+
-- MySQL B
mysql> SELECT WAIT_FOR_EXECUTED_GTID_SET('b0bda503-3cf1-11e9-8c3a-0242ac110002:1-7',1);
+--------------------------------------------------------------------------+
| WAIT_FOR_EXECUTED_GTID_SET('b0bda503-3cf1-11e9-8c3a-0242ac110002:1-7',1) |
+--------------------------------------------------------------------------+
| 0 |
+--------------------------------------------------------------------------+
参考资料
《MySQL实战45讲》
转载请注明出处:http://zhongmingmao.me/2019/03/02/mysql-read-write-separation/
访问原文「MySQL -- 读写分离」获取最佳阅读体验并参与讨论
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 想用数据库“读写分离” 请先明白“读写分离”解决什么问题
- Java 读写锁浅析
- Golang文件读写
- ReentrantReadWriteLock 读写锁解析
- 用Python读写文件
- golang读写文件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
A Common-Sense Guide to Data Structures and Algorithms
Jay Wengrow / Pragmatic Bookshelf / 2017-8-13 / USD 45.95
If you last saw algorithms in a university course or at a job interview, you’re missing out on what they can do for your code. Learn different sorting and searching techniques, and when to use each. F......一起来看看 《A Common-Sense Guide to Data Structures and Algorithms》 这本书的介绍吧!