前言

1、在考虑MySQL数据库的高可用的架构时,主要要考虑如下几方面:

  • 如果数据库发生了宕机或者意外中断等故障,能尽快恢复数据库的可用性,尽可能的减少停机时间,保证业务不会因为数据库的故障而中断。
  • 用作备份、只读副本等功能的非主节点的数据应该和主节点的数据实时或者最终保持一致。
  • 当业务发生数据库切换时,切换前后的数据库内容应当一致,不会因为数据缺失或者数据不一致而影响业务。

一、主从架构

1.1 一主一从:

mysql 配置为主从模式,从库是主库的 backup,同一时间设置其中一台为主服务器,提供读写,另一台服务器作为热备,不提供读写,通过复制与主服务器数据保持一致,二者均开启 binlog。

(1)主从复制实现

在主库把数据更改记录到 binlog 中;备库将主库的日志复制到自己的 relaylog 中;备库读取 relaylog 中的事件,将其重放到备库上。

(2)主从复制关键技术: 异步复制、半同步复制

1.2 双主架构

两台mysql都可读写,互为主备,默认只使用一台(masterA)负责数据的写入,另一台(masterB)备用;masterA是masterB的主库,masterB又是masterA的主库,主主同步就是两台机器互为主的关系,在任何一台机器上写入都会同步(双向同步)。而主从只有对主写入时才会同步(单向同步)。

(1)双主实现: 两台主库之间做高可用,可以采用keepalived等方案(使用VIP对外提供服务);所有提供服务的从服务器与masterB进行主从同步(双主多从); 建议采用高可用策略的时候,masterA或masterB均不因宕机恢复后而抢占VIP(非抢占模式);

(2)双主关键技术: keepalived,通过共用的虚拟IP地址对外提供服务;每个热备组内同一时刻只有一台主服务器提供服务,其他服务器处于冗余状态,若当前在线的服务器宕机(故障检测心跳,采用定时发送一个数据包,如果机器多长时间没响应,就认为是发生故障,自动切换到热备的master机器上去。)其虚拟IP地址将会被其他服务器接替(优先级决定接替顺序),实现高可用为后端主机提供服务。

1.3 一主多从:

(1)一主多从原理

在主库的请求压力非常大时,可通过配置一主多从复制架构实现读写分离,把大量对实时性要求不是很高的请求通过负载均衡分发到多个从库上去读取数据,降低主库的读取压力。而且在主库出现宕机时,可将一个从库切换为主库继续提供服务。

在实际的生产环境中,如果对mysql数据库的读和写都在一台数据库服务器中操作,无论是在安全性、高可用性,还是高并发等各个方面都是不能满足实际需求的。因此,一般通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。

(2)一主多从实现: 实现方式主要基于mysql的主从复制,通过路由的方式使应用对数据库的写请求只在master上进行,读请求在slave上进行。

1、 基于程序代码的内部实现,在代码中根据select、insert进行路由分类,这类方法也是目前生产环境中较为常用的,优点是性能较好,因为在程序代码中实现,不需要增加额外的设备作为硬件开支;缺点是需要研发人员来实现,运维人员无从下手。

2、基于中间代理层实现代理一般位于客户端和服务器之间,代理服务器接收到客户端请求后通过判断后转发到后端数据库。如下有两个常用代理:

Mysql-proxy:其为mysql的开源项目,通过其自带的lua脚本进行sql判断,虽然是mysql官方产品,但是mysql官方并不建议其使用到生产环境中。

Amoeba: 由陈思儒开发,该程序由Java语言进行开发。这个软件致力于mysql的分布式数据库前端代理层,它主要为应用层访问mysql的时候充当sql路由功能。Amoeba能够完成多数据源的高可用、负载均衡、数据切片等功能。

mysql高可用架构有哪些 mysql数据库高可用架构_mysql高可用架构有哪些

1.4 多主多从:

MySQL多主模式是互为对方的从服务器,每台服务器即是对方的主服务器,又是对方的从服务器。无论对哪一台进行操作,另一台都会同步数据。一般用作高容灾方案

1.5 主从复制出现的问题

在传统的master-slave主从复制模式下,如果master发生了crash,MySQL DBA需要手动将slave升级为新master(比如关闭只读开关等),旧的master重启后需执行change master to进行复制关系重建,并执行start slave开启复制。如果是semi-sync半同步复制,还需要进行半同步参数配置。但在MGR模式下MySQL能自动发现primary crash,通过选主产生新的primary节点对外提供读写服务。旧的primary节点重启后,DBA只需要执行start group_replication即可将crash节点重新加入到Group中,在运维便利性和系统健壮性上有极大的提升。


二、mysql主备延时如何解决?

主备延迟的来源: 产生主备延迟的根本原因是备库上消费 binlog 的速度赶不上主库产生 binlog 的速度

1.备库所在机器的性能要比主库所在的机器性能差,主从库之间的网络延迟。

2.备库的压力大。主库既然提供了写能力,那么备库可以提供一些读能力。或者一些运营后台需要的分析语句,不能影响正常业务,所以只能在备库上跑。结果就是,备库上的查询耗费了大量的CPU资源,影响了同步速度,造成主备延迟。

处理方案
  • 提高从库的机器性能。
  • 一主多从。除了备库外,可以多接几个从库,mysql服务器可以平行扩展, 分散压力 ,让这些从库来分担读的压力。
  • 增加缓存层服务的基础架构。在业务层和持久化层间加入memcache或者redis构成的cache层. 降低mysql的读写压力
  • 是否有大事务, 大事务这种情况很好理解。因为主库上必须等事务执行完成才会写入binlog,再传给备库。所以,如果一个主库上的语句执行10分钟,那这个事务很可能就会导致从库延迟10分钟。
  • 锁冲突问题也可能导致从机的SQL线程执行慢,比如从机上有一些select … for update的SQL,或者使用了MyISAM引擎等。使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。
  • 多线程,在 MySQL 5.6 版本之前,MySQL采用单线程复制,而从 5.6 开始,正式支持多线程复制。如果是单线程同步,单个线程存在写入瓶颈,导致主备延迟,那就先调整为多线程试试效果。

三、 mysql mgr组复制

引入组复制,主要是为了解决传统异步复制和半同步复制可能产生数据不一致的问题。组复制依靠分布式一致性协议(Paxos协议的变体),实现了分布式下数据的最终一致性,提供了真正的数据高可用方案。Group Replication由至少3个或更多个节点共同组成一个数据库集群,事务的提交必须经过半数以上节点同意方可提交,在集群中每个节点上都维护一个数据库状态机,保证节点间事务的一致性。Group Replication基于分布式一致性算法Paxos实现,允许部分节点故障,只要保证半数以上节点存活,就不影响对外提供数据库服务,是一个真正可用的高可用数据库集群技术。 Group Replication支持两种模式,单主模式和多主模式。在同一个group内,不允许两种模式同时存在,并且若要切换到不同模式,必须修改配置后重新启动集群。

  • 在单主模式下,只有一个节点可以对外提供读写事务的服务,而其它所有节点只能提供只读事务的服务,这也是官方推荐的Group Replication复制模式。
  • 在多主模式下,每个节点都可以对外提供读写事务的服务。所有的组内成员对外提供读写服务,是真正意义上的并发,MGR对于高并发有很好的的处理能力。多主模式下,组内所有成员没有主从之分,对用户来说,就像在操作一个MySQL一样。所有server成员都可以同时接受更新操作,group内的所有机器都是primary节点,同时可以进行读写操作,并且数据最终一致性。但在多主模式下,多个节点间的事务可能有比较大的冲突,从而影响性能,并且对查询语句也有更多的限制。

mysql高可用架构有哪些 mysql数据库高可用架构_服务器_02

选举策略:

  • 单主模式下,如果主节点挂了,那么其他的成员会自动选举出新的主成员,成员之间可以通过配置权重来确定下一个主成员是谁,如果没有配置权重,则会对所有在线成员的UUID进行排序,然后选取UUID最小的成员作为主成员,用户在任意一个在线的成员上都能够查询到主成员的UUID。
  • 多主模式下,没有单主模式的概念,没有必要使用选主程序,因为不同server成员之间没有特殊的差异。

原子广播: MGR中Xcom就是用来对用户发起的写事务做全局排序的。当一个事务准备在事务所在的server提交的时候,此server会原子性的广播此事务所修改的值及所在行的标识到所有server;所有server会对提交的事务顺序达成一个共识(consensus)。

  1. 有效性:正常节点发出的消息,会被其他所有节点接收到
  2. 一致性:一个正常的节点收到消息,则其他正常节点最终也会收到该消息
  3. 完整性:一个消息对于每个节点来说,最多接收到一次
  4. 顺序性:各节点收到的消息顺序和消息发出的顺序是一致的

Paxos Group: 每个节点都会主动和其他节点建立连接,组成一个Paxos组,在这个组中主动发起连接的节点永远是Leader(Proposer). 其他的节点只有Acceptor和Leaner的角色。所以一个三个节点的集群中,实际上有3个Paxos组。每个节点分别领导一个Paxos组。当一个节点收到客户端的数据排序请求时,就使用自己领导的Paxos组执行一个Paxos过程。三个Paxos组之间互不干涉。

数据同步策略: 当DB1上有事务T1要执行时,T1对DB1是来说本地事务,对于DB2、DB3来说是远端事务;DB1上在事务T1在被执行后,会把执行事务T1信息广播给集群各个节点,包括DB1本身,通过Paxos模块广播给MGR集群各个节点,半数以上的节点同意并且达成共识,之后共识信息进入各个节点的冲突检测certify模块,各个节点各自进行冲突检测验证,最终保证事务在集群中最终一致性。在冲突检测通过之后,本地事务T1在DB1直接提交即可,否则直接回滚。远端事务T1在DB2和DB3分别先更新到relay log,然后应用到binlog,完成数据的同步,否则直接放弃该事务。

与原有复制方式相比,主要增加了certify的概念,如下图所示,certify模块主要负责检查事务是否允许提交,是否与其它事务存在冲突,如两个事务可能修改同一行数据。在单机系统中,两个事务的冲突可以通过封锁来避免,但在多主模式下,不同节点间没有分布式锁,所以无法使用封锁来避免。为提高性能,Group Replication乐观地来对待不同事务间的冲突,乐观的认为多数事务在执行时是没有并发冲突的。事务分别在不同节点上执行,直到准备提交时才去判断事务之间是否存在冲突。

多主模式下,在组复制中通过Group Replication Protocol协议及Paxos协议,形成的整体高可用解决方案,同时增加了certify的概念,负责检查事务是否允许提交,是否与其它事务存在冲突,Group Replication是由多个节点共同组成一个数据库集群,每个节点都可以单独执行事务,但是read-write(RW)的操作只有在组内验证后才可以commit,Read-only (RO)事务是不需要验证可以立即执行,当一个事务在一个节点上提交之前,会在组内自动进行原子性的广播,告知其他节点变更了什么内容/执行了什么事务,然后为该事物建立一个全局的排序,最终,这意味着所有的服务器都以相同的顺序接收相同的事务集。因此,所有服务器都按照相同的顺序应用相同的变更集,因此它们在组中保持一致。 在多主模式下,该组的所有成员都设置为读写模式,在多主模式下,不支持SERIALIZABLE事务隔离级别,且不能完全支持级联外键约束。

certify方法:

1、 transaction message,XCom是基于Paxos协议实现的一套通信组件。它是XCom广播消息的内容结构;**transaction_context_log_event:有两个部分,

  • write set,是事务更新行相关信息的Hash值,write set=Hash(库名+表名+主键(唯一键)字段) ,用于冲突检测,是否对同一行同时进行了修改。
  • gtid_executed,GTID (Global Transaction ID)是全局事务ID,当在主库上提交事务或者被从库应用时,可以定位和追踪每一个事务已经执行完成的事务GTID的集合,GTID是递增的,即事务快照。

mysql高可用架构有哪些 mysql数据库高可用架构_服务器_03

2、certification info ,在每个节点上的certify模块进行检测内存的结构。certification info是一个内存结构,相当于一个map,key:string类型,保存着write set;value:set集合,保存着gtid_executed。

3、认证过程: 检测过程中根据节点收到的transaction message中的write set到节点内存中certification info找到对应的kv,若能找到,然后对比两者的gtid_executed的值,若前者大于等于后者,那么就更新,反之检测未通过。检测是否为数据库中的同一行数据冲突,若不同库不同表不同行则不冲突。

多节点写入冲突检测:

Group Replication乐观地来对待不同事务间的冲突,乐观的认为多数事务在执行时是没有并发冲突的。事务分别在不同节点上执行,直到准备提交时才去判断事务之间是否存在冲突 。乐观锁通过使用数据版本(Version)记录机制实现 数据每更新一次,对此version值加一。显然这种多点写入条件下,对于同一条记录的并发修改,由于大量的回滚,导致性能很低,因此MySQL官方建议,这种对于同一条记录的修改,应该放在同一个节点执行,这样可以利用节点本地锁来进行同步等待,减少事务回滚,提高性能。

最终一致性保证: 各节点定期交换事务执行信息。


四、MHA高可用方案

MHA是自动的master故障转移和Slave提升的软件包,MHA Manager 会定时探测集群中的 master 节点,当 master 出现故障时,它可以自动将最新数据的 slave 提升为新的 master,然后将所有其它的 slave 重新指向新的 master。它是基于标准的MySQL复制(异步/半同步)。

在 MHA 自动故障切换过程中,MHA 试图从宕机的主服务器上保存的二进制日志,最大程度地保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过 ssh 访问,MHA 没法保存二进制日志,只进行故障转移而丢失了最新数据。MHA 可以与半同步复制结合起来降低数据丢失的风险。MHA可以与半同步复制结合起来,如果只有一个slave已经收到了最新的二进制日志,MHA可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。

MHA 服务有两种角色, MHA Manager(管理节点)和 MHA Node(数据节点):

  • MHA Manager:
      通常单独部署在一台独立机器上管理多个 master/slave 集群(组),每个 master/slave 集群称作一个 application,用来管理统筹整个集群。
  • MHA node:   运行在每台 MySQL 服务器上(master/slave/manager),它通过监控具备解析和清理 logs 功能的脚本来加快故障转移。

mysql高可用架构有哪些 mysql数据库高可用架构_服务器_04

MHA工作原理总结为以下几条:
(1) 从宕机崩溃的 master 保存二进制日志事件(binlog events);
(2) 识别c出含有最新更新的 slave ;当master出现故障时,通过对比slave之间I/O线程读取master binlog的位置,选取最接近的slave做为latest slave。其它slave通过与latest slave对比生成差异中继日志。
(3) 在latest slave上应用从master保存的binlog,同时将latest slave提升为master。
(4) 在其它slave上应用相应的差异中继日志并开始从新的master开始复制。


五、布隆过滤器

filter主要工作就是帮助过滤一下不存在的元素 所以说和cache一样挡了一层 如果不存在再去数据库查找 ,所以后面还要跟一层数据管理系统 比如mysql 后面的是数据权威机构 而过滤器和cache是预先处理模块。

防止缓存击穿,将已存在的缓存放到布隆中,当使用缓存的时候,可以先访问布隆过滤器,存在则访问缓存,不存在则访问数据库;检索系统查询当前的输入信息是否存在于数据库中,也可以使用布隆过滤器。

布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

布隆过滤器其中重要的实现就是位图的实现,也就是位数组,并且在这个数组中每一个位置只占有1个bit,而每个bit只有0和1两种状态。将一个字符串存入布隆过滤器的时候,这个字符串会先被多个hash函数生成不同的hash值,然后在对应的bit数组的位置,将0置为1(bit数组初始化的时候,全部位置都是0);然后第二次在有相同的字符串存入的时候,因为之前已经对应的位置都被置为1了,所以可以很轻松的知道这个值已经存在了。

mysql高可用架构有哪些 mysql数据库高可用架构_mysql高可用架构有哪些_05

BloomFilter的核心思想有两点:

  1. 多个hash,增大随机性,减少hash碰撞的概率
  2. 扩大数组范围,使hash值均匀分布,进一步减少hash碰撞的概率。

等判断时,将输入对象经过这k个哈希函数计算得到k个值,然后判断对应bitarray的k个位置是否都为1(是否标黑),如果有一个不为1,那么这个输入对象则不在这个集合中。如果都是1,那说明在集合中,但有可能会误,由于当输入对象过多,而集合也就是bitarray过小,则会出现本来数据不存在但是查询bitmap都为1的情况,那样就容易发生误判!因此使用布隆过滤器是需要容忍错误率的。

在数据量很大的时候使用布隆过滤器非常方便,占用的内存空间很小(因为使用的是bit数组,空间使用非常小,空间开销就是bit数组的大小),查询效率也很高(直接通过计算hash函数的出来的),唯一的问题就是可能会有误判,不过概率也是比较小的,也可以通过增加白名单和增加hash函数的数量来减少这个问题的产生,总的来说利大于弊,在仅判断元素是否存在而不涉及到删除的情况下非常好用(最基本的bloomfilter是无法删除元素的,置为0就没法判断存在情况了,有bloom过滤器的变体是支持删除的)。