如何设计和实现高可用的MySQL
下面开始我们今天的主要内容,今天主要是通过什么、为什么、怎么做,这条思路跟大家呈现MySQL的高可用。
首先介绍一下什么是高可用?在我看来就是业务在高质量的情况下,对用户提供服务的可运行的总时长。其实我们从事MySQL相关的工作,大家对9这个数字比较敏感,大家选择云厂商云产品的时候,首先会看它的数据库有几个9。目前腾讯云MySQL可以做到99.95,全年在25分钟的样子。
据我了解,高可用最高是能做到3个9,1个6,做到4个9很困难,做到5个9就是极限了。
为什么我们要做高可用?因为我们不可控的因素太多了,比如说,挖挖机,我记得基本上每隔一年都会出现这种类似的事件,让我记忆犹新的是2015年杭州萧山的某个主干网被刮断,导致阿里的部分服务不可用。另外,还有类似的一些停电,或者一些天灾等等。值得一提是,运维人员的一些操作失误案例,rm整个目录或者drop表,民间有说法叫从删库到跑路。不可控制的因素很多,你的数据、用户是你的,如果不可控的话,你的业务上不去。
一般来说,有两个指标会被当作衡量的标准,第一是RPO,第二是RTO。RPO从故障开始到业务恢复所丢失的数据量,RTO就是从故障开始到业务恢复所耗费的时长,两者都是越短越好。
我们怎么做呢?一般来说业界有三种方式,左边是基于单机存储方式,这种方式在游戏场景比较多,大家上层是用单独的计算机节点,下层用三副本保证数据的可靠性。在计算节点发生故障以后可以快速迁移到另一个计算节点,当然我们腾讯云的MySQL已经推出了这种模式,相对来说非常廉价,是基础版,大家在官网都可以购买到这种模式。第二种是基于共享存储方式,也叫share disk模式,这种比较典型的是oracle的RAC架构。底层基于共享存储的方式,上层采用多个计算节点,某个计算节点故障可立即从ip列表中提出,不影响用户访问。第三种就是基于数据复制模式,也就share nothing模式,通过数据传输、复制协议达到两台主机数据一致性,也是本次讲解的重点。另外,除了存储节点的高可用,其整个链路也需要高可用,比如,咱们的IDC机房,交换机,以及主机服务器等。
下面我们介绍下基础设施的高可用。大家经常听到几个术语,第一是同城双活,第二是两地三中心,两地三中心对于金融相关的场景是个强需求,其实说白了就是说我们在同城两个节点相差十公里之外有两个数据中心,在100公里异地以外有另个灾备中心,保证了机房的高可用。另外包括网络、主机,其实架构上是这样的,至少说你的交换机网络都有备份,一个倒了以后,另一个需要替换上去。
下面进入我们的重点,基于数据复制的高可用,首先介绍一下备份,备份确实是非常重要的,而且备份是一个实在没办法最后的一个保障,所以说建议大家不管是在云上用的业务,还是自己的IDC尽量做好备份。
MySQL备份基本上是这两种:逻辑备份、物理备份。逻辑备份通常使用官方的MySQLDump与第三方工具MyDumper,MyDumper优势在于多线程备份,速率快。物理备份使用Percona的xtrabackup,可以不落盘,通过基于流式并发与压缩,生产出成功率较高、速率较快并且暂用存储空间较低的备份。最后一种就是快照,我们腾讯云的基础版的备份就是通过快照生成的。
那基于数据复制方式,一般是主从两个节点,数据怎么保证一致性呢?其实是通过复制协议进行数据传输,通过Switch切换保证故障以后服务能够尽快恢复。右边的图基本和腾讯云MySQL差不多的架构,我们采用了一主一从的方式,从节点只负责故障的转移,当主节点挂了以后,通过自动故障探测与自动切换,从而做到业务尽快恢复。另外针对读写分离,腾讯云MySQL现可以支持一主挂5个只读节点。
下面介绍一下复制,在介绍复制之前有必要介绍一个重要的概念:binlog,binlog是二进制文件,主要记录用户对数据库更新的sql信息,binlog是什么样子呢?它是在磁盘上是这个样子,使用show binlog events后它是这样的,里面会记录一些元信息,比如位点、事件等等,我们通过MySQL官方解析工具mysqlbinlog解析后是这样的,里面sql语句是使用base64编码的,解码后是这样的,可以看到这里是条插入语句。那什么时候写binlog呢?大家来看这个图,我们知道事务提交有两个阶段:prepare与commit,请问是哪个阶段写binlog呢?binlog其实是在prepare后commit前写入的,同时写事务过程中,会产生redolog与undolog,那这两者有什么区别呢?我们知道MySQL是多引擎的关系型数据库,binlog是MySQL Server层的日志,而redolog是MySQL引擎InnoDB层的日志;另外一个不同是两者写入时机不同,redolog是prepare阶段每执行sql语句就写redo了,而binlog是在prepare完commit前写的。那MySQL在主从架构下怎么保证数据一致性呢?众所众知,MySQL为了保证性能,数据是先写内存后落盘的。当你数据库运行的时候,发生了宕机,机器再次恢复的时候可能是部分数据落盘了,部分未落盘。这时,mysql是找到binlog最新同步的位点或GTID,来确定redolog或者undolog中哪些实例需要回滚,哪些事务需要重做。另外,在写日志的时候,比如redolog或binlog,MySQL为保证高性能,也是先写内存后落盘的,所以日志的落盘策略也会影响数据的一致性。为保证数据的一致性,建议大家将涉及日志的参数配置为“双1”,也就是如图上所示。
下面我们来看看复制整个流程,其实很简单,Master通过dump线程将binlog落盘,在Slave上会有两个线程,分别是IO线程和SQL线程。IO线程接受来自Master的binlog并落地形成Relaylog,SQL线程并行读取relaylog中的sql信息,执行回放动作。一般来说, 复制分三种:异步复制、半同步、强同步。这三者的区别在于何时将sql执行的结果反馈给客户端。异步复制,Master不管Slave,Master执行sql完毕后立即返回给客户端,这种方式性能最好,但是存在数据不一致的可能;强同步呢,Master完全关心Slave,等待Slave将relaylog回放后才返回给客户端,这种方式能够保证数据强一致,但是其性能有一定损耗;半同步则是Master部分关心Slave,认为只要binlog传输到Slave侧,落为relaylog后,即可以返回给客户端了。半同步是一种兼顾的实现,一方面保证数据一致性,另一方面兼顾了数据库的性能。
在复制过程中,我们经常遇到延迟问题,大家看图中所示,复制经历三个阶段:Dump线程落盘binlog、IO线程落盘relaylog、以及SQL线程回放,请问三个步骤里面哪个步骤是一个瓶颈?是SQL线程,是因为SQL线程在回放日志过程中是串行执行sql的,而Master对外是并行提供服务的。所以这里瓶颈是SQL线程。大家可用通过开启并行复制来解决延迟问题,MySQL5.6基于库级别并行复制;MySQL 5.7基于逻辑时钟并行复制,也就是表级别的并行;而MySQL8.0则是行级别的并行复制,粒度更细,复制效率更高。
刚才是说在协议级别进行复制,其实还有一种方式是块级别的数据复制,其不关心上层是什么,只需要保证在磁盘层面数据复制即可。当然这种方式的话,应用的比较少。说完复制后,咱们来说一下切换,其实MySQL官方之前并没有提供故障自动发现与转移的能力,基本上靠第三方工具来实现。
第一种是Keepalived,Master和Slave相互探测对方,时刻询问对方存活状态。当出现网络抖动或者网络出现问题的时候,可能会出现脑裂问题,变成了两主,数据就写错乱了。第二种就是MMM的方式, M1M2互为主备,再加上一个Slave节点做冗余。从图上看,虽然是双主,但该模式下同一时间点下只能有一个节点可以写,当发现这个主写节点出现故障,会将vip切换到另一个主上比。整体看,这种方式比较老,问题比较多。第三种是MHA,其应用广泛,这种方式是由复制组与管理节点组成,每个复制组里是由至少三个数据节点组成,数据节点上部署监控agent,定时上报到管理节点,当主节点出现问题时,由管理节点裁决是否切换到从节点。腾讯云是自己实现了一套故障检测,结构如右边的图,由高可用保证的Monitor节点来进行故障检测与切换。另外,目前我们还在做MySQL高可用的重构,届时能够做到故障检测恢复30秒钟以内,大大提高了高可用。
下面我们来说下集群的高可用架构,比较有名的就是PXC、MGC、MGR,PXC和MGC是结构比较类似,MGR是官方提供的,具有故障转移的高可用架构。大体的层级是这样的,MGR以插件的形式存在的,MGR主要是把复制协议进行改造,因为MGR支持多活,所以这里另一个重点是冲突检测,若多个节点同时写同一主键时,依照哪个为准呢?MGR是采用基于Paxos协议实现的冲突检测。下面,我们大致看下结构,MGR是支持多个节点写,即多活,支持某个节点挂了后自动剔除,恢复后自动加入集群。这张图是介绍一下MGR数据流逻辑,图上有三个节点构成最小MGR集群。假设DB1有一次写提交,在Prepare阶段,MGR插件会生成一个叫WriteSet的集合,并将其广播给其他节点。这个WriteSet集合包含此次提交的binlog和更新的唯一键,此唯一键由db名、表名和主键组成。这里可以看出MGR有个限制,表中必须要有主键,要不无法进行冲突检测。我们再说回来当节点收到这一信息时,会进行比对,每个节点都有一个缓存,保存当前同步情况,即唯一键对应的GTID SET。通过比对后将结果返回给DB1,只要多于半数的节点返回说OK,可以提交,那DB1接下来就会执行binlog的落盘操作,然后返回OK到客户端。其他节点则执行写Relaylog的动作,接下来进行回放的动作。若多数节点返回冲突,DB1则执行回滚操作,其他节点会drop掉复制过来的binlog。
其实PXC和MGC思路是差不多,应该说是MGR借鉴的,因为PXC和MGC是比较早就出来的,这里大同小异,主节点将WriteSet写集合广播出去,广播完后进行验证与裁决。
最后我们说一下NewSQL高可用架构,首先对AWS表示致敬,孵化出非常优秀的NewSQL产品-Aurora。那Aurora是怎么产生出来的呢?这与AWS数据库架构有关。我们来看看这个图,AWS数据库是架构在虚拟机与云盘上的,我们都知道MySQL的log比较多,所以很多IO是通过耗时较高的网络来完成的,因此AWS这种架构网络IO是它的瓶颈,性能也跑不上去。在此基础上,我们来认识下Aurora。
Aurora是计算与存储分离的架构,典型的share disk 的结构。底层存储采用6副本,部署在三个不同的AZ上,可以保证一个AZ挂了,或者至多两个AZ的一个副本丢失的情况下数据不丢失,业务可以正常对外服务。Aurora的理念是“日志即数据库”,其把MySQL存储层进行了彻底的改造,摒弃了很多LOG,只留下了Redolog,具备将redolog转换到Innodb page的能力。通过这种方式,Aurora宣称其减少至少85%比例的IO。另外其把备份和回档下沉到存储节点,使得备份恢复更快并得到保障。Aurora整体感觉相对比较接地气,成本相对比较低。
另一个就是阿里云的Polar,理念和AWS不同,阿里云觉得未来网络不是问题,未来网络可以接近总线的质量,所以是架构在RDMA网络的机房里,日志方面大动作较少,保证后续MySQL社区新特性可快速迭代近来。Polardb也是share disk的架构,其存储节点是通ParallelRaft协议保证数据的完备性。可见这也是个伟大的架构,但是相对来说成本比较高一些。
我们腾讯云自己的NewSQL在研发中,只是目前还没有正式上线,我们的名字叫CynosDB,相比来说我们的理念是兼顾两者,未来在高网络新硬件的基础实施下,会发挥更大的性能,更稳健的服务和更高的可用性。请大家拭目以待。
本次我的分享就到此为止。
Q & A
Q:我想问一下在腾讯游戏的高并发行业里面,我们主要采用哪种架构?
A:腾讯内部有很多自研项目,但基本上我们是基于数据复制的方式。内部有phxsql等分布式集群架构。
Q:如何在高并发情况下,保证总库的定延时呢?
A:可以开启并行复制,业务做分库分表,分散到多个实例上。
Q:比如说像游戏类的,在游戏高峰期的话会有很多人同时在线,这种情况下怎么在后台看数据呢?
A:可以对比较热的数据进行分层,前一层可以通过KV方式缓存,比如Redis,来提高热数据的读取,后一层使用MySQL,定期将数据同步落盘。
Q:这种情况下怎么保证数据库是一致的呢?
A:写数据可以不经过KV缓存,直接写MySQL数据库,读取时,缓存内没有数据,需要从DB中捞取出来。另外,KV缓存也有落地能力,非关键数据也可以不使用MySQL落地。