本文由作者授权网易云发布,未经许可,请勿转载

作者:温正湖,网易数据库技术专家

MGR(MySQL Group Replication)是MySQL官方推出的领先的服务高可用和数据高可靠方案,网易从2017年下半年开始对MGR进行了全面的性能和稳定性测试,发现并解决了数个问题,同时对MGR核心功能进行了优化增强,目前基于MGR的网易RDS金融版服务已经上线,为网易电商业务提供了跨机房的高可用方案。本次分享将为大家介绍网易对MGR的优化和增强,以及MGR在网易电商业务的使用和调优实践。

本文是在由IMG(Inside MySQL Group)社区主办的第三届MySQL技术嘉年华上所做的“网易MGR使用和优化实践”PPT讲稿。

大家好上午好,今天主要跟大家聊聊MySQL Group Replication,也就是MGR在网易杭研这边的使用和优化情况。杭研这边使用MGR的场景主要有这么几个,一种是用在业务系统上,一般是电商业务。另一种就是作为其他高可用或跨机房系统的元数据库,这个很多业务场景都需要。

这次分享的内容包括本页所述的3个部分。先是分析下MGR技术实现,简单介绍MGR下的事务执行流程,重点分析事务排序和事务认证,这也是为第二部分做铺垫;第二部分是本次分享的重点,将会分享我们在MGR使用时发现的一些不足以及我们的优化思路和效果;最后简单说下网易杭州研究院MGR的使用方案。

这个图相信了解过MGR的同学都比较熟悉。应该说比较直观得显示了MGR与普通的MySQL复制模式的区别。主要是MGR模式下,事务完成引擎层prepare,写Binlog之前会被MySQL的预埋钩子HOOK before_commit()拦截,进入到MGR层,将事务封装成消息通过Paxos一致性协议(consensus)进行全局排序后发送给MGR各个节点,在各节点上独自进行认证(certifiy)。

认证通过后本地节点写Binlog完成提交。其他节点写relay-log,由注册的复制通道group_replication_applier channel完成事务并行回放。

上页中提到MGR模式下有个certify阶段,就是事务认证或者叫冲突检测的过程。这是因为MGR支持多主模式(multi-master),意味着允许各个节点都执行事务并在本地提交,这就存在潜在的问题,第一个问题是不同节点可能在同一时刻更新相同的记录,第二个问题是交叉更新,比如节点A先更新了记录1再更新记录2,节点B先更新记录2在更新记录1,由于非本地事务是先写到relay-log里面,所以可能出现两个事务分别更新第二条记录时,第一条记录的更新所对应的relay-log还没有回放,也就是说读取了第二条记录的旧版本进行了更新操作。

这显然是跟事务的ACID有冲突的。必须解决掉。那么MGR就是通过certify阶段解决。其实不仅仅是多主模式,在单主模式的新主切换过程中,也需要进行事务认证。

进行事务认证的核心是右图所示的冲突检测数据库。其保存了记录的当前版本信息(gtid_set字段),即最后一次修改该记录的事务提交后的gtid_executed。通过pk hash字段来标识某条记录。冲突检测数据库的sequence_number字段并不用于事务认证,而是规定认证通过后的并行回放行为。

每个事务进行认证的时候都会携带执行该事务时节点的gtid_executed,也就是事务的版本snapshot_version,以及事务所修改的记录信息writeset。通过这两部分信息跟冲突检测数据库里的信息进行比对,就能确定该事务应该提交还是回滚。

如果一个事务在各个节点都已经执行或回放了,那么该事务的writeset信息就不需要继续缓存在冲突检测数据库中。MGR会对其进行周期性清理。先广播各自节点的gtid_executed信息,然后收集其他节点的gtid_executed信息取交集stable_gtid_set,再基于这个交集来清理无用的信息。

事务在MGR中进行认证前,会先进行全局排序,就是前述的consensus。在这个阶段就需要把事务认证所需的所有信息都收集起来,封装好后通过Paxos协议进行广播。主要有3个部分的内容,第一部分是Transaction_context_log_event,它携带了事务认证所需信息,包括是否为本地事务,事务的快照版本,执行线程是否为worker线程,事务的gtid是否已确定(MGR作为其他MySQL实例的slave)、事务writeset等等。需要注意的是该log_event仅用于认证,不会写入binlog文件中。接下来2个部分是我们比较熟悉的部分,在认证通过后会写入Binlog文件中,首先是gtid_log_event,规定了事务的gtid,以及在回放时的组提交行为。接着就是真正的事务内容。

在后续的认证阶段,这三部分会被一一处理。

封装好后就会发给Paxos。为了支持各节点可写的多写模式,MGR底层采用了Paxos的变种mencius(也就是孟子,当然真正实现有点差别),每个Paxos节点或者说MySQL节点都是公平的,基于round robin的方式分配到对应的消息轮次。比如节点0分别占据0,3,6。。。槽位,节点1占据1,4,7。。。槽位,节点2占据2,5,8。。等槽位。

这样的话,各节点上执行的事务过了Paxos后就全局有序了。举个例子。比如MGR有3个节点,同一时间分别执行了T1,T2,T3这3个事务,在Mencius中,T2所在MySQL对应节点0,那么就占了槽位0,T1的MySQL对应节点1,占据槽位1。依次。最终排定了T1,T2和T3的全局顺序。

但如果3个节点的事务提交频率是不一样的,那么会出现什么情况呢? 那些没有事务提交或提交较少的节点对应的消息轮次/槽位会被noop/null所取代。表示该轮次没有需要达成majority的数据,填空即可。但怎么知道对应消息轮次为空呢,这个实现起来比较麻烦。其他节点会尝试通过READ操作来读取,轮次所属的节点发现本轮次为空,那么发送noop,但如果是因为事务消息过大,导致达成majority很慢,其他节点多次通过READ操作无法获取到,又或者是由于网络延迟较高/网络分区,导致多次READ无法成功,那么其他节点会尝试广播prepare noop来协商该轮次为空值。这是一个潜在的问题。

这页的图是事务认证的实现方案。基于pipeline机制,一共有3个pipeline,分别是event_cataloger,certification_handler,applier_handler。事务消息中的3部分log event依次进入pipeline。其中最核心的是certification_handler处理gtid_log_event这个环节,决定了事务是否认证通过,以及通过后如果分配gtid,如何确定非本地节点事务的并行回放行为等。下面展开来说下这一步如何进行认证。

假设T2事务的快照版本是1-100,更新的记录是1,查看冲突检测数据库中1对应的版本是1-50,也就是说T2在执行的时候,节点已经执行或回放了之前所有对记录1的修改操作。因此T2得已认证通过。认证通过后需要为T2分别gtid,这里我们先确定分配的是201,他会被更新到冲突检测数据库1的gtid_set上。那么201是怎么确定的呢。需要看下图。

MGR会为每个节点预留一段连续的gtid区间,Available gtid_set表示给当前各个节点预留的可用gtid。比如当前为Server2预留的是201到300,由于T2属于Server2,所以分配目前可用的最小gtid,也就是201。这样就保证了多写模式下,给不同节点事务分配的gtid是不一样的。

确定了gtid后,对于非本地事务,需要写到relay-log后回放。那么在写入relay-log文件前,需要确定并行回放行为,即last_committed和sequence_number。

再次以T2为例,首先说sequence_number,这是并行复制的逻辑时钟,根据事务提交先后次序递增。待分配的sequence_number由全局变量parallel_applier_sequence_number保存,那么T2的sequence_number即为387。parallel_applier_sequence_number递增为388。

接下来确定last_committed,我们在前面在介绍过,并行回放时的组提交行为依赖于冲突检测数据库,每条记录除了保存了对应的事务版本外,还保持了最后一个事务的sequence_number。这里是120,那么T2这个事务依赖于120这个事务,必须等待120这个事务回放完之后才能回放,也就是说T2的last_committed初始化为120。最后,将记录1对应的sequence_number更新为387。

这样,MGR在事务处理过程中需做的事情基本上处理完了。Relay-log中的事务交由worker线程回放即可。

接下来分享下MGR在考拉业务场景上使用时遇到的问题,由于时间有限,只重点说明影响最大的2个案例,分别是事务认证模块、Paxos模块的不足和优化。还有很多问题的定位和优化就只能简单列举下。

我们前面提到,MGR会每隔60s广播本节点的gtid_executed信息,各节点取交集后清理冲突检测数据库信息。但在网易电商场景下,这样的机制会有明显的性能波动,如下图所示,非常规律的每分钟一次性能波动,延迟升高,tps降低。我们分析发现,这个跟冲突检测数据库清理有关系。因为上面累计了太多的writeset,而执行清理的时候需要加锁遍历整个冲突检测数据库,这就跟事务的认证流程的加锁起了冲突,导致事务性能下降,延迟提升。另外,在某些较极端的情况下,比如流控参数设置较大,或每个事务writeset较多,或gtid_set较大时。writeset清理不及时也容易导致冲突检测数据库占据过多内存空间,导致mysqld OOM。

基于此,我们做了些优化。比如将gtid_executed广播周期变为动态可调的参数,默认设置为20s;将事务产生的writeset个数减半;将writeset个数纳入MGR流控模块管理。这样可以有效降低冲突检测数据库中的writeset个数。

但在节点间复制延迟较大的情况下,冲突检测数据库仍可能比较大。针对这个,我们在单主模式下做了进一步优化,就是writeset清理不依赖于gtid_executed交集。而是采用writeset个数达到阈值直接清掉。因为单主模式不会有事务冲突,冲突检测数据库并不是发挥事务认证作用,而仅仅用于决定事务的并行回放行为。这个优化还报错将gtid_set字段置空,来进一步减少内存消耗。

右图是优化前后的性能对比。可以发现,性能波动大大改善。平均性能得到提升。而且内存也得到了有效控制。

除了事务认证模块,也对MGR底层的Paxos模块进行了优化改进。这主要是为了解决大量数据导入场景下的问题,包括Primary节点OOM,或者有个Secondary节点由于无法获取paxos消息导致mysqld自动退出问题。

如右图所示,使用NDC往MGR导入数据,设置为10个并发,每个事务为200条记录组成的batch,事务大小在30MB左右。导入过程中,内存急剧增涨。

我们分析发现原因一是paxos cache过大,占据太多内存;二是paxos消息发送和接受环节未考虑高网络延迟情况。

这个2个方面原因展开来说:

首先是paxos cache,目前社区版的cache大小阈值1G,且不可调(8.0最新版本已经可调),但其实无法将cache大小严格控制在1G左右。主要是因为达到大小阈值后,cache对象是否能被清理需要看其他节点是否还需要该cache对象,另外达到cache大小阈值后,新的cache对象还会从空闲队列分配而不是通过LRU替换当前cache对象。

第二个原因是paxos协议实现导致的内存增涨,主要是由于大事务在网络中低效率传输导致网络拥塞。由于网络拥塞,未参与majority的节点尝试获取消息时无法及时获取对应消息轮次节点返回的消息,就会不断进行重试,但其实此时对应轮次节点正在回复消息数据,只是因为事务较大,网络较差,导致传输速度太慢。如果不断重试,会不断恶化这个情况。节点在回复消息数据时会拷贝一份消息数据。也就是30MB,那么如果每个消息都重复多次,显然会迅速消耗大量内存。

这是Paxos层优化后的内存使用情况,可以看到,已经变得非常平稳。而且其实性能并没有什么损耗。

除了上面这些大点外,我们也从运维角度进行了优化,比如将8.0版本的一些特性迁移到5.7上。还有就是对故障恢复和选主等环节进行优化,比如选择gtid_executed最新的节点作为Primary节点。节点加入集群时优先从其他secondary节点获取数据。等等。时间关系,不展开介绍。

这是我们上报的MGR模块,或者跟MGR相关的一些官方bug。有MGR选主和故障恢复方向的,也有跟复制或者XA事务相关的。其中有些非常严重的问题,比如数据不一致问题,复制中断问题,只读开关开启情况下还能提交事务等等。这些问题的发现和修复,为我们业务能够使用MGR打下了基础。目前上面的绝大多数问题MySQL官方都已经在新版本上修复了。

第三部分简单说下MGR使用方案。包括MGR如何与网易云RDS服务配合实现金融级高可用实例,如何实现考拉的同城跨机房,如何与DDB配合来减低硬件成本等。

首先是MGR在网易云RDS上的使用。网易云RDS支撑了网易杭研95%以上的业务,是网易内部使用MySQL的主要平台。相比自建MySQL,网易云RDS有非常多的优势。目前网易云RDS已经基于MGR推出3节点的金融版实例。相比主从复制架构的RDS高可用实例,基于MGR的RDS金融版实例,在数据可用性和可靠性上又有了进一步提升。引入MGR后,RDS Agent和管控的逻辑需要针对性得进行适配,这里主要介绍MGR在故障时的路由切换和故障恢复。

MGR会自己选主,但无法将MySQL访问路由从旧主切换到新主,这个是需要RDS做的。RDS可以通过监测主节点心跳来发现节点宕机等行为。然后通过各个节点上报的心跳信息来感知view视图变更和新选出来的主是哪个节点。等新主的认证队列和回放队列均为空,relay-log回放完之后,将网络路由切换到新主上。

对于故障的节点,会尝试重启,如果无法重启,那么RDS需要用xtrabackup基于MGR其他节点做个全量物理备份,重新加入MGR。如果重启成功,重新加入MGR后节点会首先回放本地applier通道上的relay-log,然后连上其他节点使用recovery通道获取binlog并回放,如果此时其他节点binlog已经被purge,那么也需要进行全量重建。

接下来介绍下MGR在考拉跨机房上的使用,目前的部署模式是同城跨机房基于MGR,跨城的多机房使用异步复制进行。

为了提高同城2机房模式下数据的可用性和可靠性。我们将Primary节点放在1个机房,2个Secondary节点放在另一个机房。在2机房架构下,只有将Primary和Secondary隔开才能确保任意一个机房挂掉,数据不会丢失。因为如果Primary跟任意一个Secondary在一个机房的话,就可能在机房内部对一个事务达成majority,另一个机房的secondary可能还未收到数据。这种情况下,Primary所在机房宕机时就可能导致数据丢失。

还有,在跨机房场景下,不可能通过切换ip来调整业务的路由。所以设计了基于ETCD的DNS切换方案,一个机房故障的情况下,另一个机房还能跟第三个机房实现majority,因此还能够修改DNS地址,实现业务访问的跨机房高可用。

目前某电商业务的测试环境均已经升级为基于MGR的RDS金融版实例。线上也有小规模使用,目前正在逐步将线上RDS高可用实例替换为基于MGR的RDS金融版实例。

由于时间关系,PPT中所述的内容并不是每块都讲得很详细,大家有兴趣的话,可以关注我们在知乎上的专栏-数据库内核,来获取进一步得资料。

延伸阅读:

mysql group 更新递增_MySQL Group Replication在网易使用和优化实践相关推荐

  1. mysql 记录更新 内部_MySQL 入门(1):查询和更新的内部实现

    摘要 在MySQL中,简单的CURD是很容易上手的. 但是,理解CURD的背后发生了什么,却是一件特别困难的事情. 在这一篇的内容中,我将简单介绍一下MySQL的架构是什么样的,分别有什么样的功能.然 ...

  2. mysql mgr 读写分离_MySQL Group Replication mgr 单主 proxysql 读写分离配置过程

    1.前期准备,mgr安装见上一篇文章 2.创建用户和导入脚本 GRANT ALL ON *.* TO 'rootuser'@'%' IDENTIFIED BY '123456'; /mgr/mysql ...

  3. mysql group by自定义_mysql – GROUP BY和自定义顺序

    我已经阅读了 MySQL order by before group by的答案,但是将它应用于我的查询最后会在子查询中找到一个子查询,这是一个相当简单的案例,所以我想知道这是否可以简化: 带有样本数 ...

  4. mysql group by 天_MySQL group by语句如何优化

    在MySQL中,新建立一张表,该表有三个字段,分别是id,a,b,插入1000条每个字段都相等的记录,如下: mysql> show create table t1G ************* ...

  5. mysql group by 慢_mysql“group by”查询非常慢

    我在一个有大约100k记录的表中有这个查询,它运行得很慢(3-4s),当我取出组时它更快(少于0.5s).我很想知道如何解决这个问题: SELECT msg.id, msg.thread_id, ms ...

  6. MySQL不走联合索引_mysql group by 多列优化思路?为什么不走联合索引?

    explain SELECT a, b, COUNT(*) FROM tbname GROUP BY a, b order by a DESC limit 1 a 和 b 列已经设置联合索引, 为什么 ...

  7. mysql c 更新 数据_Mysql:如果数据存在则更新,不存在则插入

    本篇文章转载自:https://www.cnblogs.com/Eric-zhao/p/6655994.html mysql语法支持如果数据存在则更新,不存在则插入,首先判断数据存在还是不存在的那个字 ...

  8. mysql的更新用法_mysql更新语句的详细用法

    首先,单个表的UPDATE语句: 更新[LOW_PRIORITY] [IGNORE] tbl_name SET col_name1 = expr1 [,col_name2 = expr2 ...] [ ...

  9. mysql的更新用法_mysql update语句的用法详解

    本节内容: mysql数据库中的update语句. 首先,单表的UPDATE语句: UPDATE [LOW_PRIORITY] [IGNORE] tbl_name SET col_name1=expr ...

最新文章

  1. 【camera】全景驾驶感知网络YOLOP部署与实现(交通目标检测、可驾驶区域分割、车道线检测)
  2. 手把手教你 MongoDB 的安装与详细使用(二)
  3. Android进阶笔记09:Android 万能适配器
  4. 图片和input不对齐_pdf到png再到mp4短视频:不需要工具,2个指令1键搞定
  5. 1001 A+B Format (20point(s))(Java和C++)
  6. 今天的考核题目: 你知道React和Vue的区别吗? skr,skr
  7. CString类(转)
  8. “5 年内,PC 或将逐渐消失!”| 人物志
  9. Linux 系统编程技巧与概念 第14章 字节次序
  10. 学生信息管理系统(php MySql),基于PHP学生信息管理系统设计系统设计(MySQL)(含录像)...
  11. Linux下Libtorch运行出现free(): invalid pointer报错
  12. 这几个私藏的在线工具网站!真是相见恨晚!让码农彻底解放双手!
  13. 几种常见的文献管理软件
  14. 暴风激活工具,劫持Chrome浏览器主页
  15. JetPack之DataStore源码笔记
  16. 简练网软考知识点整理-易混概念项目绩效评估与团队绩效评价
  17. 你的手帕再也不干净了_我再也不会将手机带回国际航班了。 你也不要
  18. Linux端类似种子猫的软件,值得一试的四款Linux Torrent客户软件
  19. CSDN 富文本编辑器和 Markdown 编辑器使用 Word 支持的 LaTx 语法公式
  20. 单细胞-拟时序分析-R包SCORPIUS

热门文章

  1. 三步搞定oracle 11G 导出的pmd文件,导入oracle 12C数据库中
  2. java list 初始化值_java List的初始化
  3. Linux的super super super easy教程 | shell常用文本命令
  4. 分治算法——汉诺塔(HanoiTower)
  5. 大一新生学计算机用买电脑吗,大学一年级就买电脑合适吗?
  6. 深入理解 python 虚拟机:令人拍案叫绝的字节码设计
  7. Tomcat和Jenkins
  8. “广东省五一劳动奖章”获得者卫晓欣:“她”力量让新兴技术更获认可
  9. python 引用局部变量_在Python中赋值之前引用的局部变量?
  10. redis安全学习笔记