在分布式系统中,咱们晓得CAP定理和BASE实践,数据的平安和性能是负相关的,数据的安全性进步了,那他的性能就会降落,相干,他的性能进步了,数据的安全性就会降落。咱们从几个中间件来探讨这个问题。

mysql

当mysql性能呈现瓶颈时,咱们会做分库分表、读写拆散等,读写拆散须要咱们做主从复制,对读的操作,咱们指向从库,对写的操作,咱们指向主库,上面看看主从复制的原理。
当业务零碎提交数据的时候,master会将这变更的数据保留到binlog文件,写入成后,事务提交胜利。
slave中会有一个线程,他会从master中读取binlog信息,而后写入relay文件。而后他还会有其余线程,读取relay文件的信息,把这个信息从新在slave库执行一遍。也就是说,如果在master中执行了一个update的语句,那在slave中同样也执行截然不同的update语句,这样两边的数据就会保持一致,因为master先执行,而后slave再执行,所以会有略微的提早。如果执行的语句比拟久,那这个延时也会比拟长,当然零碎的压力也会影响提早的工夫。

除了延时以外,他还有一个比拟大的问题,当写入binlog后,代表事务提交胜利,此时master挂了,导致slave没方法读取这部分的binlog,所以就会呈现数据的失落,两边的数据就没方法放弃一致性,所以咱们通常会把下面的异步复制模式设置半同步复制,也就是semi-sync。
半同步复制与异步复制不同的是,当master写入到binlog后,他会被动的把数据同步到从库,从库把信息写入到relay文件,会返回ACK给master,此时master才会认为他的事务是提交的。

如果有多个slave的状况,则至多返回1个ACK才认为事务的提交的。半同步复制尽管解决了数据安全的问题,然而他须要从库写入relay文件并返回ACK才算提交事务,与异步复制比照,他的性能是降落的。

单体

在单体中,也同样存在着数据安全和性能的负相关问题。
这里简述一下mysql对update语句的一个流程。

  • 当执行update的时候,会先从磁盘里把数据读取到缓存。
  • 写入undo文件,这里是在咱们事务回滚的时候用的。
  • 批改缓存数据,比方把id为1的name由张三改为李四。
  • 写入redo缓存,redo次要是为了宕机重启时,复原数据用的。
  • 写入redo缓存后,写入到磁盘。
  • 写入binlog文件。
  • 写入binlog后,提交commit给redo,跟redo说曾经写入到binlog了。
  • 定期把缓存的数据写入到磁盘。


咱们对数据的更新,都是在缓存中进行的,这样能够保障性能的进步。同时为了数据的安全性,还引入了undo、redo、binlog等货色。咱们能够看redo和binlog两种写入磁盘的策略。
在redo中,咱们能够抉择0,即不把缓存数据写入磁盘,这样能够疾速执行完redo操作,如果此时宕机了,还没写入磁盘的数据就失落了,尽管进步了性能,然而数据安全性没有了。如果抉择1,因为要写入磁盘才能够实现redo操作,尽管保障了数据的安全性,然而性能却降落了。
同样的,binlog写入oscache是进步了性能,然而服务器宕机会导致oscache的数据不能及时的写入磁盘,导致数据的安全性没有。如果间接写入磁盘,性能又降落。

redis

与mysql已有,redis的复制也是异步复制的,当业务零碎往master写入数据的时候,他就会通过异步复制的形式把数据同步给slave,所以和mysql相似,当业务零碎认为他曾经把数据写入到redis的时候,此时master挂了,然而数据还没同步到slave,他的数据就失落了。

另外一个场景,就是产生了脑裂,也就是sentinel认为master挂了,而后从新选举了master,此时业务零碎和master是失常通信的,他把数据提交到原master节点,然而原master节点的数据此时是没方法同步到其余节点,导致数据不统一。

在这状况下,咱们会做以下配置:

min-replicas-to-write 1min-replicas-max-lag 10

这个意思是至多有1个slave曾经有10秒没有同步,则master暂停接管申请。所以不会说master始终写入数据,而slave没有同步。如果产生以上两个场景,最多失落10秒的数据。尽管没有严格的做到数据安全性,然而也保障了数据的不一致性不会超过10秒,超过10秒后,因为不能写数据,写性能降落为0。

RocketMQ

咱们看看基于Dledger是怎么做broker同步的。

首先,master broker收到音讯后,会把这个音讯置为unconmmited状态,而后把这个音讯发送给slave broker,slave broker收到音讯后,会发送ack进行确认,如果有多个slave broker,当超过一半的slave broker发送ack时,master broker才会把这个音讯置为committed状态。在这种机制下,保障了数据的安全性,当master broker挂了,咱们还有至多超过一半的slave broker的数据是残缺的,因为须要多个slave broker进行ack确认,也升高了性能。
从单体上来说,异步刷盘和同步刷盘跟mysql的redo写入磁盘的一样的。
另外,kafka的同步机制跟这个相似,而且他也有写入oacache的操作。

Zookeeper

Zookeeper是CP模型的,那他的数据安全性是能够保障的,那他的性能呢?咱们假如此时master节点挂了,此时须要从新选主,这个时候Zookeeper集群是不可用状态的。那Zookeeper是如何保证数据的一致性呢?

咱们假如master同步给5个slave。
当master不必确认是否曾经同步给slave就间接返回,这个时候性能是最高的,然而安全性是最低的,因为master数据没同步到slave的时候挂了,那这个数据是失落的。
当master确认曾经同步给所有slave(这里是5个)才返回,这个时候,性能是最低的,然而安全性是最高的,因为不论哪个slave,他的数据都是残缺的。
不论是RocketMQ还是Zookeeper,都是折中抉择超过一半的slave同步,才算胜利。当咱们拜访Zookeeper的时候,他会依据曾经同步好的slave服务器让咱们来读取对应的信息,这样咱们读取的数据必定都是最新的。