时间:2021-07-01 10:21:17 帮助过:30人阅读
,整个数据层有Group1,Group2,Group3三个集群组成,这三个集群就是数据水平切分的结果,当然这三个集群也就组成了一个包含完整
如下图所示,整个数据层有Group1,Group2,Group3三个集群组成,这三个集群就是数据水平切分的结果,当然这三个集群也就组成了一个包含完整数据的DB。每一个Group包括1个Master(当然Master也可以是多个)和 N个Slave,这些Master和Slave的数据是一致的。比如Group1中的一个slave发生了宕机现象,那么还有两个slave是可以用的,这样的模型总是不会造成某部分数据不能访问的问题,除非整个 Group里的机器全部宕掉,但是考虑到这样的事情发生的概率非常小(除非是断电了,否则不易发生吧)。
在没有引入集群以前,我们的一次查询的过程大致如下:请求数据层,并传递必要的分库区分字段(通常情况下是user_id)?数据层根据区分字段Route到具体的DB?在这个确定的DB内进行数据操作。 这是没有引入集群的情况,当时引入集群会是什么样子的呢?看图一即可得知,我们的路由器上规则和策略其实只能路由到具体的Group,也就是只能路由到一个虚拟的Group,这个Group并不是某个特定的物理服务器。接下来需要做的工作就是找到具体的物理的DB服务器,以进行具体的数据操作。基于这个环节的需求,我们引入了负载均衡器的概念(LB)。负载均衡器的职责就是定位到一台具体的DB服务器。具体的规则如下:负载均衡器会分析当前sql的读写特性,如果是写操作或者是要求实时性很强的操作的话,直接将查询负载分到Master,如果是读操作则通过负载均衡策略分配一个Slave。我们的负载均衡器的主要研究放向也就是负载分发策略,通常情况下负载均衡包括随机负载均衡和加权负载均衡 。 随机负载均衡很好理解,就是从N个Slave中随机选取一个Slave。这样的随机负载均衡是不考虑机器性能的,它默认为每台机器的性能是一样的。假如真实的情况是这样的,这样做也是无可厚非的。假如实际情况并非如此呢?每个Slave的机器物理性能和配置不一样的情况,再使用随机的不考虑性能的负载均衡,是非常不科学的,这样一来会给机器性能差的机器带来不必要的高负载,甚至带来宕机的危险, 同时高性能的数据库服务器也不能充分发挥其物理性能。基于此考虑从,我们引入了加权负载均衡,也就是在我们的系统内部通过一定的接口,可以给每台DB服务器分配一个权值,然后再运行时LB根据权值在集群中的比重,分配一定比例的负载给该DB服务器。当然这样的概念的引入,无疑增大了系统的复杂性和可维护性。有得必有失,我们也没有办法逃过的。
有了分库,有了集群,有了负载均衡器,是不是就万事大吉了呢? 事情远没有我们想象的那么简单。虽然有了这些东西,基本上能保证我们的数据层可以承受很大的压力 ,但是这样的设计并不能完全规避数据库宕机的危害。假如Group1中的slave2 宕机了,那么系统的LB并不能得知,这样的话其实是很危险的,因为LB不知道,它还会以为slave2为可用状态,所以还是会给slave2分配负载。这样一来,问题就出来了,客户端很自然的就会发生数据操作失败的错误或者异常。这样是非常不友好的!怎样解决这样的问题呢? 我们引入集群节点的可用性探测机制 ,或者是可用性的数据推送机制 。这两种机制有什么不同呢?首先说探测机制吧,顾名思义,探测即使,就是我的数据层客户端,不定时对集群中各个数据库进行可用性的尝试,实现原理就是尝试性链接,或者数据库端口的尝试性访问,都可以做到,当然也可以用JDBC尝试性链接,利用Java的Exception机制进行可用性的判断,具体的会在后面的文字中提到。那数据推送机制又是什么呢?其实这个就要放在现实的应用场景中来讨论这个问题了,一般情况下应用的DB 数据库宕机的话我相信DBA肯定是知道的,这个时候DBA手动的将数据库的当前状态通过程序的方式推送到客户端,也就是分布式数据层的应用端,,这个时候在更新一个本地的DB状态的列表。并告知LB,这个数据库节点不能使用,请不要给它分配负载。一个是主动的监听机制,一个是被动的被告知的机制。两者各有所长。但是都可以达到同样的效果。这样一来刚才假设的问题就不会发生了,即使就是发生了,那么发生的概率也会降到最低。
上面的文字中提到的Master和Slave ,我们并没有做太多深入的讲解。如图一所示,一个Group由1个Master和N个Slave组成。为什么这么做呢?其中Master负责写操作的负载,也就是说一切写的操作都在Master上进行,而读的操作则分摊到Slave上进行。这样一来的可以大大提高读取的效率。在一般的互联网应用中,经过一些数据调查得出结论,读/写的比例大概在 10:1左右 ,也就是说大量的数据操作是集中在读的操作,这也就是为什么我们会有多个Slave的原因。但是为什么要分离读和写呢?熟悉DB的研发人员都知道,写操作涉及到锁的问题,不管是行锁还是表锁还是块锁,都是比较降低系统执行效率的事情。我们这样的分离是把写操作集中在一个节点上,而读操作其其他的N个节点上进行,从另一个方面有效的提高了读的效率,保证了系统的高可用性。读写分离也会引入新的问题,比如我的Master上的数据怎样和集群中其他的Slave机器保持数据的同步和一致呢?这个是我们不需要过多的关注的问题,MySql的Proxy机制可以帮助我们做到这点。