如何优雅的解决分布式数据库的复杂故障,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
故障简介
ACID是事务的四个特性,其中D(Duration)就是讲的持久性,数据库的一大价值就在于可以有效处理的故障,保证数据不会丢失。随着分布式数据库的发展,部署的复杂度上升,数据库面临的故障场景也越来越多。
常见硬件故障
接下来,我们看下常见数据中心的故障概率
《Designs, Lessons and Advice from Building Large Distributed Systems》,jeff dean
网络故障
除了上述故障,对于分布式系统设计,还有一些额外的网络故障需要考虑
脑裂,顾名思义,脑裂指的是系统因为网络故障被分割为多个独立的区域;
多网面条件下,部分网面故障,这个错误一般是很难出现的,因为每个网面往往是逻辑的,并不和网卡绑定,如果用户调整配置出错,可能导致这种故障,如果系统横跨多个网面,需要考虑这个故障;
脆弱的数据中心
实际上,数据中心也没有想象中的那么稳定,下图是笔者2017年11月22日截取的cloudharmony监控数据,监控300多家数据中心的可靠性情况。
可以看到,即使大名鼎鼎的azure,也未能达到宣称99.95%,有兴趣的详细了解的可以看这里
除此之外,再给大家举几个详细的例子
2017年9月29日 azure北欧数据中心故障
北欧数据中心部分在定期的常规灭火系统维护中发生了意外,释放出了灭火剂。然后导致了专门用于遏制和安全的空气处理单元(AHU)自动关闭。而受到影响区域的某些系统为防止系统过热对部分机器进行关机和重启,AHU在35分钟后手动恢复,因为系统突然关机导致部分数据需要恢复,系统在7小时后才恢复正常。该事故导致了部分用户的存储服务不可用。
2017年2月28日amazon s3故障
运维工程师定位账务系统变慢这个问题时,想要删除一小部分服务器,结果命令输入错误删除了大批服务器,包括 index subsystem和placement subsystem的服务器,导致S3服务从9:37AM开始不可用,直到1:54PM,其中最有意思的是,AWS Service Health Dashboard系统依赖S3,因此从故障发生直到11:37AM,监控页面没有显示故障。这个故障据说弄倒了半个墙外的互联网世界。
2016年4月13日Google Compute Engine停止服务
全球所有区域的Google Compute Engine停止服务,18分钟后恢复。该故障由一个运维工程师删除一个无用的ip blocks引起的,而删除ip这个操作并没有合理做配置同步,这个操作触发了网络配置系统的一致性检测,当网络配置系统检测到不一致后,进行了重启,导致服务中断。
2015年5月27日 杭州电信挖断阿里网线
光纤挖断后,部分用户无法使用,两小时后恢复。
2014年7月1日 宁夏银行核心数据库系统故障
银行二部(2014)187号正式发全国文件,对宁夏银行事故的描述大致如下,2014年7月1日,宁夏银行核心系统数据库出现故障,导致该行(含异地分支机构)存取款、转账支付、借记卡、网上银行、ATM和POS业务全部中断。
经初步分析,在季末结算业务量较大的情况下,因备份系统异常导致备份存储磁盘读写处理严重延时,备份与主存储数据不一致,在采取中断数据备份录像操作后,造成生产数据库损坏并宕机。因宁夏银行应急恢复处置机制严重缺失,导致系统恢复工作进展缓慢,直至7月3日5点40分核心系统才恢复服务,业务系统中断长达37小时40分钟,其间完全依靠手工办理业务。
故障分类
看一下数据中心网络的互联图
图上任何的硬件设备都可能发生故障,从各个主机,交换机到网线。
我们尝试以故障域对故障做一个简单的分类。所谓故障域,就是会因为一个故障而同时不可用的一组组件,常见的故障域包括:
物理机器,包括本地磁盘,网卡故障,内存故障等
数据中心共用一组电源的一个机柜
数据中心共用一个网络设备的数个机柜
受单个光纤影响的一个数据中心
处于同一个地域的多组数据中心,被同一个城市供电或受同一自然灾害影响
故障的变化
不同组件发生故障的概率是不同,google一项研究表明,在36 °C和47 °C范围内运转的磁盘,故障率***,随着时间的发展,磁盘故障率也逐渐提升,***年只有1.7%,第三年达到8.6%。
现在也有很多研究,将大数据和人工智能引入了磁盘故障预测领域,取得了不错的成果。
数据库故障处理
日志系统
数据库会为数据修改记录日志,日志记录了数据的变化,根据不同的日志用途,可以分为redo日志、. undo日志、redo/undo日志,现在流行的是redo日志。
看一下postgresql日志的结构:
根据不同日志记录方式,可以分为如下两种类型:
物理日志,上图即物理日志,replay速度快,但是日志量大,实现逻辑相对简单不易出错;
逻辑日志,replay速度相对慢,日志量小,而且对于MVCC机制的数据库有一个额外的好处,备机可以单独gc,和主机无关;
数据库日志系统有两个重要的原理:
WAL原则,也就是日志刷盘要在页面刷盘之前,这里的刷盘,并非调用write就可以,还需要调用sync操作。在合适时机,往往是事务提交时,将日志刷盘,并调用sync同步到磁盘,以保证断电时可以恢复数据。除了在事务提交时将日志刷盘,在涉及元数据操作时,往往也会调用sync将数据刷盘,以保证元数据的一致。
通过日志系统恢复,不仅仅需要一份完好的日志,还需要一份完整的(可以是落后的)数据作为起点。
日志系统是系统软件内广泛使用的技术,不仅仅是数据库,日志代表了系统的改变,他可以用来恢复/备份,也可以用做通知系统,掌握了系统的日志流,就相当于掌握了系统的整个状态,日志可以更抽象的理解为日志+状态机,通过不断的重访日志,改变状态机的状态,可以通过传递日志将状态改变传递到整个系统的各个角落,关于日志系统,笔者见过的***的一篇文章是The Log: What every software engineer should know about real-time data’s unifying abstraction,非常推荐一读,日志即一切。
日志回收
日志代表了系统所有的变化,如果数据大小是从0开始扩展到100G,那么日志至少也要有100G,甚至更多,而日志的增长和用户做出的改动是正相关,任何系统也无法存储***增长的日志。
回收日志所占存储空间是必然的选择,日志收回有两个好处:
减少日志占据磁盘空间
降低系统恢复需要的时间
实际上,对于MVCC机制实现的数据库,因为日志回收和事务提交没有关系,所以可以严格的将日志控制在指定大小,为系统运维提供方便。
上文所说数据恢复需要一份完好的数据作为起点,其实原因就是最开始的日志被回收了,如果能保留从初始状态到***状态的所有日志,那么光靠日志也可以恢复系统,但是很明显,任何系统也不能保留所有日志。
checkpoint
checkpoint用于回收日志,checkpoint的流程如下:
打点:记录当前日志位置;
将当前系统内所有内存中的数据刷盘,并调用sync同步到磁盘,此时仍要遵循WAL原则;
写checkpoint日志,或将checkpoint信息作为元数据刷盘;
回收checkpoint起始点之前的日志;
上面是常见的做checkpoint方式,这种方式也叫做全量检查点(full checkpoint),这种方式实现简单,但是明显checkpoint是一次IO峰值,会造成性能抖动。
还有一种做checkpoint的方式,叫做增量检查点(incremental checkpoint),过程如下:
后台写进程按照页面***次修改的顺序刷盘;
打点:记录当前刷盘的页面对应的日志点,写checkpoint日志或者作为元数据刷盘;
这种方式化checkpoint为后台写操作,做checkpoint时只需要打点即可,消除了IO峰值,有助于平稳数据库性能。
torn page
数据库页面大小和磁盘扇区大小往往不同,因此当页面刷盘时,如果系统断电,可能只有部分页面刷盘,这种现象,我们称之为torn page,这个页面相当于被彻底损坏,而日志replay需要一份完整的数据做起点,此时是无法恢复的。
处理半写有几种方式:
innodb的double write,pg的full page write,这两种方式原理是类似的,都是在页面刷盘前,将页面首先写在其他地方,sync后,再覆盖写页面。
从备份恢复,从备份单独恢复某一个页面。
这里有几个例外:
追加写的系统没有这个问题;
如果页面大小和扇区大小相同,也没有这个问题,很多元数据设计都会考虑这点;
很多文件系统或分布式文件系统,raid卡,或者磁盘本身也可以处理这个故障,如果使用能自处理半写故障的硬件,数据库就可以不开启这个功能;
磁盘写满
磁盘写满这种问题只能通过运维手段解决,因为数据库事务提交必须写日志,如果无法写日志,那么任何事务都不能提交,相当于停库,因此应对磁盘故障一般是通过监控,在磁盘空间即将不足时提前预警。
磁盘损坏
如上文所说,数据库恢复需要一份完整的数据和日志,因此,如果数据或者日志遇到了磁盘损坏,日志系统是无法恢复,只能依赖其他的方式了。
备份
按照级别划分,常见的备份方式有:
全量备份:传统数据库全量备份通常的做法是首先做一次checkpoint,然后将所有的数据和checkpoint点之后的日志拷贝走,如果数据量很多,这是一次很重的操作;
增量备份:增量备份同样需要做一次checkpoint,然后将上一次备份后变化的页面和checkpoint点之后的日志拷贝走,如何找到上一次备份之后变化的页面,做全量页面比对是一种方法,通过bitmap文件记录页面变化也是一种方法,percona就实现了第二种方法;增量备份往往是可以叠加的,也是可以合并的,全量备份和增量备份也可以按时间顺序合并。
日志归档:日志归档指的是将制定的日志定时归档;
很显然,从上述操作的开销从大到小排列依次是,全量备份>增量备份>日志归档。数据库运维时往往会结合这三种方式,以达到缩小故障RPO的目标。
amazon aurora实现了近实时备份的功能,备份时间不超过5分钟。
多机热备
如果某台机器因此各种原因发生了故障,比如cpu烧毁,内存故障,或者操作系统bug,甚至被炸掉了,都可以使用备份的方式恢复。
但是通过备份恢复往往耗时较长,不能满足业务连续性(Business Continuity)的需求,除了备份以外,数据库都支持单机热备,以及支持只读查询的备机。
很明显,备机要根据故障域和客户的要求,进行反亲和部署。
master-slave(-cascade)
每个主机可以挂多个备机,每个备机可以挂多个级连备,这是当前传统数据库的常见部署方式。postgres甚至支持多级的级连备(次级连)等等,但是不是很常用。这种部署方式可以有效的处理单机故障。作为支持只读操作的备机,可以有效的分摊读负载,这是一种有延迟的读操作,本身也是满足相应隔离级别的,但是和主机放在一起考虑的话,并没有一致性可言。
事务提交时机
根据主机事务的提交时机,有几种事务提交级别:
主机日志落盘,此时RTO<1min,RPO>0
主机日志落盘,同时主机日志发送到备机,此时RTO<1min,RPO=0
主机日志落盘,同时主机日志发送到备机,并且落盘,此时RTO<1min,RPO=0
这三种提交级别,主机性能越来越差,一般而言,同城备机采用第二种方式,异地备机使用***种方式。
共享磁盘
共享磁盘方案依赖共享存储,备机只读不写,虽然备机不写盘,但仍然需要不断的在内存replay日志,以便主机故障后能快速升主。
很明显,share disk方案性能类似数据单机,而且RTO<1min,RPO=0。但是受硬件限制,sharding disk方案只适用于同城。
技术是螺旋式前进的,在分布式计算中,share disk的思想也很流行,很多系统依赖分布式文件系统/存储系统,在其上构建基于share disk的计算系统,比如大数据领域久负盛名的hadoop,还有OLTP领域的新力量aurora,还有newsql领域的tidb+tikv。
master-master
master-master架构服务也很多,日渐成为主流,目前低一致性的大数据系统几乎都是多主架构,笔者对大数据不够熟悉,这里只列一致性较强的一些数据库系统
传统领域,oracle RAC,IBM purescale;
sharding中间件,很多互联网公司都开发属于自己的中间件,比如腾讯tdsql,阿里DRDS,中兴GoldenDB,开源的方案也有很多,像pg-xc,pg-xl,mycat等等,中间件方案符合互联网场景,技术门槛低,对用户限制大;
fdw(foreign-data wrapper),这也是一种类似sharding的方案,目前oracle和pg采用这种方案,将外部数据源直接映射为本地表,限制也很多,比如外部表的统计信息很难抽取等等;
mysql group replication,使用paxos作为复制协议,结合传统数据库做出了新的探索;
类spanner架构,商业数据库有spanner和oceanbase,开源数据库有tidb和cockroach;
master-master架构的系统,总有数据是可以提供服务的,因此可靠性更高,这是当前分布式系统的主流方案。
paxos/raft
paxos/raft是当前主流的分布式复制协议。
paxos协议精确定义了在分布式系统下达成共识的最小条件。关于paxos的原理可以参考这篇文章《一步一步理解Paxos算法》。
paxos是分布式系统的核心之一,关于这个算法给予再多的赞誉也不为过。paxos协议有很多变种,他的应用也是有一些主要注意的地方,《SRE: google运维解密》内23章讨论了paxos应用的一些场景和情况,有兴趣的可以了解一下。
一些工程可靠性手段
系统调用
什么样的系统调用是可靠的?几乎没有,c语言中最容易出问题的系统调用就是malloc,因为使用的太广泛了,在有些较深的代码逻辑内,一旦申请内存出错,处理相当棘手。在某个重要的内闭的模块中,首先申请足够的内存是一个比较好的做法,相当于半自管理的内存。
其次容易出错的系统调用是和IO相关的调用,比如IO调用出错更难处理的是IO变慢,读写操作的速度在故障时是完全不不能有任何期待的,几十秒,几分钟甚至更久都很正常,所以,如果自旋锁内包含一个IO操作,这个系统离崩溃就不远了。
凡是跨网络的操作,对网络不要有任何期待,在操作前,释放所有不必要持有的资源,并做好调用出错的准备,并为其设定一个超时时间,改为异步模式是一个好选择。
checksum
如果由于外部破坏或bug等原因导致数据损坏,可以通过checksum的方式探查,checksum一般在如下两个时机应用:
数据刷盘时计算,并同时记录到磁盘上;
数据读取时校验;
磁盘心跳/连接心跳
进程卡死是不可避免的,当系统cpu被占满时,或者由于某些bug,可能导致某些关键进程得不到调度,导致其无法传递某些信息,某些故障可能对整个系统都是致命。
如何侦测进程/线程卡斯,有两种常用的做法:
维护磁盘心跳,比如定期touch某个文件,如果长时间文件的时间戳没有变化,表示该程序卡死;
提供接口供外部程序访问,外部程序定期访问该进程,如果长时间得不到回应,可以认为程序卡死;
对于bug导致的程序卡死,往往杀掉进程重新拉起可以解决。
调度/队列/优先级/流控
系统性能是很难做到线性提升的,对于数据库来说,更是不可能,对于大部分数据库系统来说,性能首先随连接数增加而提升到某个点,继续增加连接数,往往性能会下降,
上图是mysql8.0.3 CATS特性的性能测试结果,明显可以看到超过64连接后,性能随着连接数增加而降低。
这也是数据库系统一般都会做连接池的原因。
过量的压力可能导致系统崩溃,比如上图,FIFO的调度方式下,512连接,性能降低接近5倍。因此在大型系统中,连接池和优先级队列是一个好设计,可以方便对系统的压力进行有效控制,同时通过监控队列长度,可以直观看到这部分系统的压力和处理能力。
异地备份
主流的高可用方案有两种,一种是两地三中心,一种是异地多活。
两地三中心
对于传统数据库,两地三中心的方案比较常见,常见的部署是同城两中心,异地一中心
两地三中心是一个初级和简单的部署架构,一旦主库发生故障,异地中心很难顶上,只能起到冷备的作用:
一般而言,应用距离主库较近,异地网络延时大,性能往往不如主库;
异地中心往往较本地中心硬件条件差,无论是带宽还是时延,未必满足应用的需求;
异地中心不能提供服务,浪费资源;
如果不经常做主备切换,一旦发生故障,往往异地中心会出现各种问题,上文中宁夏银行在故障前一年就做过故障演练,但是一年不练,真的发生故障时,会出现各种问题。
还有一种共有云上可靠性更高的方案,如下图
有钱任性。当然,公有云海量部署可以摊低成本,在私有云上,这种方案更贵。
paxos并不适合两地三中心的部署,paxos协议要求有3个对等的故障域,并且能处理一个故障域的故障,两地三中心故障域并不对等
同城复制快,异地复制慢,性能受很大影响;
同城两中心在地质灾害时会同时故障,paxos不能处理;
异地多活
异地多活方案主要要考虑如下几个问题:
系统资源分配在异地条件下是否存在问题;
故障自闭,任意数据中心间断网造成的区域隔断是否会导致系统不可用,尤其注意当某个数据中心故障时,流控系统往往会立刻就将压力导入到其他可用区域,可能会立刻导致系统过载;
数据中心间数据同步性能是否可以满足需要;
数据中心是及其昂贵的,一旦整个数据中心发生故障,作为服务整体服务质量不降级是不可能的,如何优雅降级并保证尽可能多的数据可用,这是分布式系统需要重点考虑的问题。
以google spanner为例,时间戳分配是分布式的,不需要中心节点,数据可以由用户选择部署方式,横跨数据中心越多,性能越差,可靠性越强,区域故障完全自闭,不影响其他部分。
异地多活是一项系统工程,在这个庞大工程里,数据库只需要做好自己的事就可以了。
看完上述内容,你们掌握如何优雅的解决分布式数据库的复杂故障的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。