在分布式系统中,咱们晓得 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 1
min-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 服务器让咱们来读取对应的信息,这样咱们读取的数据必定都是最新的。