没有合适的资源?快使用搜索试试~ 我知道了~
1. 客户端直连方案,因为少了一层proxy转发,所以查询性能稍微好一点儿,并且整体架构简 2. 带proxy的架构,对客户端比较友好 1. 对于必须要拿到最新
资源详情
资源评论
资源推荐
28 | 读写分离有哪些坑?
2019-01-16 林晓斌
在上一篇文章中,我和你介绍了一主多从的结构以及切换流程。今天我们就继续聊聊一主多从架
构的应用场景:读写分离,以及怎么处理主备延迟导致的读写分离问题。
我们在上一篇文章中提到的一主多从的结构,其实就是读写分离的基本结构了。这里,我再把这
张图贴过来,方便你理解。
图1 读写分离基本结构
读写分离的主要目标就是分摊主库的压力。图1中的结构是客户端(client)主动做负载均衡,这
种模式下一般会把数据库的连接信息放在客户端的连接层。也就是说,由客户端来选择后端数据
库进行查询。
还有一种架构是,在MySQL和客户端之间有一个中间代理层proxy,客户端只连接proxy, 由
proxy根据请求类型和上下文决定请求的分发路由。
图2 带proxy的读写分离架构
接下来,我们就看一下客户端直连和带proxy的读写分离架构,各有哪些特点。
1. 客户端直连方案,因为少了一层proxy转发,所以查询性能稍微好一点儿,并且整体架构简
单,排查问题更方便。但是这种方案,由于要了解后端部署细节,所以在出现主备切换、库
迁移等操作的时候,客户端都会感知到,并且需要调整数据库连接信息。
你可能会觉得这样客户端也太麻烦了,信息大量冗余,架构很丑。其实也未必,一般采用这
样的架构,一定会伴随一个负责管理后端的组件,比如Zookeeper,尽量让业务端只专注于
业务逻辑开发。
2. 带proxy的架构,对客户端比较友好。客户端不需要关注后端细节,连接维护、后端信息维
护等工作,都是由proxy完成的。但这样的话,对后端维护团队的要求会更高。而且,proxy
也需要有高可用架构。因此,带proxy架构的整体就相对比较复杂。
理解了这两种方案的优劣,具体选择哪个方案就取决于数据库团队提供的能力了。但目前看,趋
势是往带proxy的架构方向发展的。
但是,不论使用哪种架构,你都会碰到我们今天要讨论的问题:由于主从可能存在延迟,客户端
执行完一个更新事务后马上发起查询,如果查询选择的是从库的话,就有可能读到刚刚的事务更
新之前的状态。
这种这种““在从库上会读到系统的一个过期状态在从库上会读到系统的一个过期状态””的现象,在这篇文章里,我们暂且称之为的现象,在这篇文章里,我们暂且称之为““过期过期
读读””。。
前面我们说过了几种可能导致主备延迟的原因,以及对应的优化策略,但是主从延迟还是不能
100%避免的。
不论哪种结构,客户端都希望查询从库的数据结果,跟查主库的数据结果是一样的。
接下来,我们就来讨论怎么处理过期读问题。
这里,我先把文章中涉及到的处理过期读的方案汇总在这里,以帮助你更好地理解和掌握全文的
知识脉络。这些方案包括:
强制走主库方案;
sleep方案;
判断主备无延迟方案;
配合semi-sync方案;
等主库位点方案;
等GTID方案。
强制走主库方案强制走主库方案
强制走主库方案其实就是,将查询请求做分类。通常情况下,我们可以将查询请求分为这么两
类:
1. 对于必须要拿到最新结果的请求,强制将其发到主库上。比如,在一个交易平台上,卖家发
布商品以后,马上要返回主页面,看商品是否发布成功。那么,这个请求需要拿到最新的结
果,就必须走主库。
2. 对于可以读到旧数据的请求,才将其发到从库上。在这个交易平台上,买家来逛商铺页面,
就算晚几秒看到最新发布的商品,也是可以接受的。那么,这类请求就可以走从库。
你可能会说,这个方案是不是有点畏难和取巧的意思,但其实这个方案是用得最多的。
当然,这个方案最大的问题在于,有时候你会碰到“所有查询都不能是过期读”的需求,比如一些
金融类的业务。这样的话,你就要放弃读写分离,所有读写压力都在主库,等同于放弃了扩展
性。
因此接下来,我们来讨论的话题是:可以支持读写分离的场景下,有哪些解决过期读的方案,并
分析各个方案的优缺点。
Sleep Sleep 方案方案
主库更新后,读从库之前先sleep一下。具体的方案就是,类似于执行一条select sleep(1)命令。
这个方案的假设是,大多数情况下主备延迟在1秒之内,做一个sleep可以有很大概率拿到最新的
数据。
这个方案给你的第一感觉,很可能是不靠谱儿,应该不会有人用吧?并且,你还可能会说,直接
在发起查询时先执行一条sleep语句,用户体验很不友好啊。
但,这个思路确实可以在一定程度上解决问题。为了看起来更靠谱儿,我们可以换一种方式。
以卖家发布商品为例,商品发布后,用Ajax(Asynchronous JavaScript + XML,异步JavaScript
和XML)直接把客户端输入的内容作为“新的商品”显示在页面上,而不是真正地去数据库做查
询。
这样,卖家就可以通过这个显示,来确认产品已经发布成功了。等到卖家再刷新页面,去查看商
品的时候,其实已经过了一段时间,也就达到了sleep的目的,进而也就解决了过期读的问题。
也就是说,这个sleep方案确实解决了类似场景下的过期读问题。但,从严格意义上来说,这个
方案存在的问题就是不精确。这个不精确包含了两层意思:
1. 如果这个查询请求本来0.5秒就可以在从库上拿到正确结果,也会等1秒;
2. 如果延迟超过1秒,还是会出现过期读。
看到这里,你是不是有一种“你是不是在逗我”的感觉,这个改进方案虽然可以解决类似Ajax场景
下的过期读问题,但还是怎么看都不靠谱儿。别着急,接下来我就和你介绍一些更准确的方案。
判断主备无延迟方案判断主备无延迟方案
要确保备库无延迟,通常有三种做法。
通过前面的第25篇文章,我们知道show slave status结果里的seconds_behind_master参数的
值,可以用来衡量主备延迟时间的长短。
所以第一种确保主备无延迟的方法是,第一种确保主备无延迟的方法是,每次从库执行查询请求前,先判断
seconds_behind_master是否已经等于0。如果还不等于0 ,那就必须等到这个参数变为0才能执
行查询请求。
seconds_behind_master的单位是秒,如果你觉得精度不够的话,还可以采用对比位点和GTID
的方法来确保主备无延迟,也就是我们接下来要说的第二和第三种方法。
如图3所示,是一个show slave status结果的部分截图。
图3 show slave status结果
现在,我们就通过这个结果,来看看具体如何通过对比位点和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集合。
如果这两个集合相同,也表示备库接收到的日志都已经同步完成。
可见,对比位点和对比GTID这两种方法,都要比判断seconds_behind_master是否为0更准确。
在执行查询请求之前,先判断从库是否同步完成的方法,相比于sleep方案,准确度确实提升了
不少,但还是没有达到“精确”的程度。为什么这么说呢?
我们现在一起来回顾下,一个事务的binlog在主备库之间的状态:
1. 主库执行完成,写入binlog,并反馈给客户端;
2. binlog被从主库发送给备库,备库收到;
3. 在备库执行binlog完成。
剩余20页未读,继续阅读
城北伯庸
- 粉丝: 27
- 资源: 315
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0