数据库高可用、性能扩展的实现

可用性场景

如果只从应用层和缓存层看高可用问题,是比较容易解决的。

对于应用层来说,根据业务特点可以很方便地设计成无状态的服务,在大多数互联网公司中,在业务层的最上层使用动态DNS、LVS、HAProxy等负载均衡组件,配合Docker和Kubernetes实现弹性伸缩,能够很容易实现应用服务的高可用。

对于缓存层来说,也有很多可选的开源方案来帮助解决,比如Codis、Twemproxy、Redis Cluster等等,如果对缓存数据的一致性和实时性要求不高,这些方案就可以很好解决缓存层面的问题。但对存储层来说,支持高可用非常困难。

数据库问题

在互联网架构中,最底层的核心数据存储一般都会选择关系型数据库,最流行的当属MySQL。大数据时代,大家渐渐发现传统的关系型数据库开始出现一些瓶颈:单机容量不能支撑快速增长的业务需求;高并发的频繁访问经常造成服务的响应超时;主从数据同步带来的数据不一致问题;大数据场景下查询性能大幅波动等等。

当前,数据库方案有了很多不一样的变化。首先,不同于早期的单机型数据库,在当下数据呈现爆发式增长,数据总量也从GB级别跨越到了TB甚至PB级别,远超单机数据库的存储上限,所以只能选择分布式的数据存储方案。其次,随着存储节点的增加,存储节点出问题的可能性也大大提高,光靠人工完全不现实,所以需要数据库层面保证自己高效快速地实现故障迁移。另外,随着存储节点的增加,运维成本也大大增加,对自动化工具也提出了更高要求。最后,新分布式数据库的出现,用户在OLTP数据库基本需求的基础上,对大数据分析查询的业务要求更高,在某种程度上OLTP和OLAP融合的新型数据库会是未来极具潜力的发展方向之一。

如何打造高可用的数据库

数据库是一个非常大的概念,从传统单机SQL,到NoSQL,再到现在流行的NewSQL,这里面不同的实现方案实在太多,本文聚焦在关系型数据库,主要探讨最流行的MySQL数据库及其生态。最近几年,随着大家在分布式数据库领域的探索,出现了很多不同类型的解决方案,比如中间件/Proxy的方案,典型的比如TDDL、Cobar、Altlas、DRDS、TDSQL、MyCAT、KingShard、Vitess、PhxSQL等,还有一种新型的NewSQL数据库,比如Google Spanner/F1、Oceanbase、TiDB等。下面看下业界在打造高可用数据库方面新的技术进展,以及和传统方案选型的对比。

消除单点问题

 

为了实现数据库层面的高可用,必须要消除单点问题(SPOF)。存在单点服务的情况下,一旦单点服务挂掉,整个服务就不可用。消除单点问题最常用的方案就是复制(Replication),通过数据冗余的方式来实现高可用。主要能保证在检测到服务发生问题之后及时做服务切换。

对于MySQL来说,默认复制方式是异步的主从复制方式,虽然这种方案被很多的互联网公司所采用,但实际上这种方案存在一个致命问题——存在丢失数据的风险。数据传输经过网络,这也就意味着存在传输时延,那么对于异步复制来说,主从数据库的数据本身是最终一致性的,所以主库一旦出现了问题,切换从库极有可能会带来数据不一致的风险。

因为异步复制方式存在更大的问题,很多时候大家都会考虑用半同步复制方式Semi-Sync,这种数据复制方式在默认情况下会使用同步的数据复制方式,不过在数据复制压力较大的情况下,就会退化成异步的数据复制方式,所以依然会存在高可用问题。当然,也有人会选用完全同步的方式,但是这种复制方式在并发压力下会有明显的性能问题,所以也不常用。

那有没有一种数据复制方式,能同时保证数据的可靠性和性能?答案是有的,那就是最近业界讨论较多的分布式一致性算法,典型的是Paxos和Raft。简单来说,它们是高度自动化、强一致的复制算法。以Raft为例,Raft中基数个节点组成一个Raft Group,在一个Raft Group内,只要满足大多数节点写成功,就认为可以写成功了,比如一个3节点的Raft Group,只要保证Raft Leader和任意一个Raft Follower写成功就可以了,所以同步写Leader,异步写两个Follower,只要其中一个返回就可以,相比完全的同步方式,性能要好很多。所以从复制层面来看,Raft更像是一个自适应的同步+异步复制方案,同步和异步的最优选择通过Raft算法来保证。

庆幸的是,业界早已意识到这个问题,从最开始的Galera Cluster探索到前段时间微信开源的PhxSQL,再到最新MySQL官方发布的MRG(MySQL Group Replication),还有我们从0到1打造的开源分布式数据库TiDB,都在这方面进行了探索。大家的出发点基本相同,采用新的分布式一致性来替换传统的Master-Slave复制方式,不同的仅仅是大家选择的协议:TiDB选择了Raft,而PhxSQL和MRG选择了Paxos。

 

自动故障恢复

 

有了数据复制,理论上来说,在一个数据库节点出现问题时就不用那么慌张,毕竟还有额外的数据副本存在。所以下面要做的就是尽早发现服务故障并快速恢复,也就是常说的Auto-failover。

从这个层面来看,目前基于主从的数据库复制方案基本上无法脱离运维,使用中间件/Proxy方案更会增加难度,毕竟人力运维是有上限的,所以选择这种方案,人力成本也是一个需要考虑的问题。Google之前在广告业务中也是使用的MySQL中间件方案,大约100个节点的规模,在这个量级下维护的复杂度和成本非常高。所以Google要做一个真正替换MySQL中间件的理想方案,这就有了后来的Google Spanner/F1,包括后来的TiDB,都采用了这种新的NewSQL架构,唯一不同的是,Google选择了Paxos,而TiDB选择了Raft。这种分布式一致性算法,除了提供优雅的复制方案,还可以提供高效的Auto-failover支持。

 

在线扩容

 

随着数据库的数据量越来越大,Scale是不可避免的问题。对于数据库来说,技术层面最大的追求就在于如何不停服务地对数据库节点进行Scale操作,这是非常有挑战性的事情。以中间件/Proxy方案来说,很多时候不得不提前对数据量进行规划,把扩容作为重要的计划来做,从DBA到运维到测试到开发人员,很早之前就要做相关的准备工作,真正扩容时为了保证数据安全,经常会选择停服务来保证没有新的数据写入,新的实例数据同步后还要做数据的一致性校验。当然业界大公司有足够雄厚的技术实力,可以采用更自动化的方案,将扩容停机时间尽量缩短(但很难缩减到0),但大部分中小互联网公司和传统企业依然无法避免较长时间的停服务问题。TiDB完全实现了在线的弹性扩容,主要基于Placement Driver的调度和Raft算法。

在线表结构变更

 

数据量较大时,数据库的DDL操作也是一个需要注意的高可用问题。以常见的Add Column操作为例,在表规模很大的情况下通常会造成数据库锁表,导致数据库服务不可用。对于中间件/Proxy方案来说,因为依托于底层的单机MySQL数据库提供DDL支持,所以很难从根本上解决,只能依赖于第三方工具,比如Facebook和Percona的方案,当然这些方案也有本身的局限性。最近业界有了更好的进展,比如Github数据团队的方案gh-ost,处理表级别的Binlog,将原表的数据同步到新的临时表中,当数据追平时再进行一个数据库操作,将临时表命名为原表,这样一个AddColumn操作就完成了。这种方案依然要引入额外的组件,除了学习成本之外,也要考虑额外组件的高可用问题。但实际上Google的F1给我们提供了更好的实现参考,TiDB即是根据F1启发进行的研发,简单来说,就是通过把TiDB中DDL操作的状态设定为前向兼容的几个不同状态,中间严格保证不能跨越两个状态。为什么这样?因为整个TiDB集群是分布式的,没有办法把DDL操作实时通知给所有的TiDB节点,就会出现部分TiDB节点感知到了DDL变化,另一部分TiDB节点还没有感知到的情况,这样就可能导致数据不一致。比如对于一个Add Index的DDL,有一个节点先感知到了,然后对于插入数据就增加了一个Index,但是另外一个节点没有感知到,正好这个节点还有一个删除操作,所以就只把行数据删除了,但Index还留在里面,这样当使用Index查询这行时就会找不到数据。TiDB参考的算法是Google F1中一个非常经典的算法,感兴趣的可以看看这篇文章Online, Asynchronous Schema Change inF1。

数据量大的解决方案

1.      最常用的手段是在数据库前面加一层分布式缓存,例如redis/memcache。加db缓存的方法主要的思路是把热点内容缓存到内存中,最好是读多写少。这种做法需要结合具体的业务场景, 应用深度切入,哪些数据能缓存,哪些不能,数据丢失后如何补偿,如何进行持久化等等都需要业务通盘考虑。

2.      放弃传统的rdbms,采用一个支持持久化存储的分布式的key-value分布式存储,例如采用dynamo/cassandra/riak;dynamo就是amozon用来存放用户购物车的数据,对一致性的要求没那么高。这种做法也是结合应用的特点。

3.      考虑rdbms自身做scale-out,例如读写分离,分库分表;即可在业务上做,例如通过java的持久化框架来做,也需要业务自己做,也可以通过数据库中间件做;总之,需要在数据库设计阶段决策,后期变更比较困难。

4.      OLAP数据库的sharding技术已经比较成熟了,例如greenplum这种mpp的架构,可以由分布式数据对单表自动做sharding。对于OLTP来说,为了避免2pc的额外消耗,表级别的事物还是放到一个物理node,避免分布式事务(例如阿里的ocean base);基于mysql的数据库中间件暂时还不支持跨分片的事物,例如开源的mycat,阿里云的DRDS。使用这种方案需要在业务上做一定规避,或者业务本身没这种需求。

5.      单机的硬件加速,db主机加内存,存储换成pcie-ssd/全闪存阵列,单卡的pcie-ssd,4k-随机读写iops提升到100K+。据说业界有人用redis+pcie-ssd的方案来应对秒杀业务。

6.      对于写性能要求比较高的场景,可以考虑更换存储引擎,采用tokudb的fractal-tree/cola-b-tree(写入性能介于B+tree和LSM tree之间)

7.      fusionIO的atomic write针对mysql特定版本可以省去log - buffer的double write,当然这是个小的tricky。

        从实践来看,目前大家用的比较多的还是做通过中间件方式进行简单的分库分表,采用分布式事务型存储引擎的并不多,ocenbase这种不算。

 

参考:

原文链接:http://www.tuicool.com/articles/2ueQFvj

数据量大的解决: http://blog.csdn.net/load2006/article/details/51073356

 

 

 

你可能感兴趣的:(数据库)