0%

中间件们如何保障数据不丢失?

MySQL

来看看 MySQL 保证数据不丢失的方案:

  1. 配置 sync_binlog=1 每次事务提交 binlog 都进行刷盘;
  2. 配置 innodb_flush_log_at_trx_commit=1 每次事务提交 redo_log 都进行刷盘;

bin_log、redo_log 持久化的意义不同:

  • bin_log 每次都进行持久化,保障了归档内容的完整性;
  • redo_log 每次持久化保证了故障恢复的能力,不丢失已完成的事务;
  • redo_log 二阶段提交的机制,完整恢复已提交事务需要 sync_binlog=1 配合。
    • 如果 redo_log 中事务为 commited,则恢复数据页
    • 如果 redo_log 中事务为 prepare,则判断 binlog,binlog 中存在则恢复,不存在则舍弃

主备通常不会采用半同步复制方案。只有半同步复制方案下才能保障主机宕机依旧能保有全部数据,但是这影响性能且容易让主机不稳定。异步方案则始终存在主备延迟问题,减轻延迟主要是降低从机负载、减少主机慢 MDL DDL。

Redis

持久化方案:

  • rdb 内存快照,save m n 配置设置 M 秒内 N+ 个 key 变动,则触发全盘保存
  • aof 命令追加,每条命令都写入 aof_buf 等待持久化到磁盘,增量记录数据信息。依赖 appendfsync 配置决定刷盘策略
    • no 依赖操作系统进行 write/flush
    • always 每次写入进行 flush,只有这个选项会在回复客户端前同步触发刷盘
    • everysec 每秒进行 flush
  • 混合持久化,4.0+ 功能,通过 aof-use-rdb-preamblev yes 开启,持久化文件为 rdb + aof 格式。开启后台线程进行 rdb,期间命令记录在缓冲区内,完成 rdb 后追加缓冲内的命令 aof。

仅当 aof 策略 appendfsync=always 时保障单机数据不丢失,但是极大降低了 redis 的高速特性,一般不会使用。主备模式下,从机数据也是异步复制,无法完全保障不丢失数据。为了保证数据备份和减少主备的延迟:

  • min-slaves-to-write N 至少要有 N 个合格备机才继续接收指令
  • min-slaves-max-lag M 备机延迟至少少于 M 秒才继续接收指令

Kafka

kafka 消息丢失的情况是多种多样的,比如:

  • Producer 异步发出消息,实际失败
  • Broker 弄丢了消息
  • Consumer 提前 ACK;offset 错误

如果消息不允许丢失,则不允许生产者异步发送消息、消费者异步ACK;剩下则要保障 broker 端不丢数据即时刷盘:log.flush.interval.messages=1
这样 broker 每次收到消息都会保,但是每次刷盘极大拖累 kafka 效率。
通常只减少后台 flush 间隔来提高数据保障,因为 kafka 多副本机制,所以集群是 partition replica 一定程度上可以保证数据的容灾。
如果需要数据备份,生产者可以通过 request.required.acks=-1 要求 ISR 同步完成后返回响应。

RocketMQ

除了类似 Kafka 的情况,所有消息队列客户端问题外,RocketMQ 也提供了数据不丢失的配置选项:

  • flushDiskType=SYNC_FLUSH 同步刷盘,比起异步刷盘性能更差,但是保证了主机不会丢失数据
  • brokerRole=SYNC_MASTER 同步复制选项,比如主从异步复制性能更差,但是保证主从一致

Zookeeper

ZK 是一个数据强一致性的组件,保证数据不丢失似乎是他天生的职责,看卡它是怎么做的。

  1. 当 zk 事务通过表决时候,向所有 follower 广播通知提交
  2. Leader 刷盘
  3. 等待足够 follower 响应
  4. ack to client

所以收到 ACK 的事务一定会被落盘,且被同步到其他节点上。但是每次 flush 过于消耗性能是如何解决的?ZK 的思路是牺牲延迟来增加吞吐量,每次刷盘会有短周期,集中对多个事务进行刷盘,部分事务的请求延迟会被略微拉大,但是整体上是的 ZK 能够承载更大的吞吐量。

to be continue…

谢谢支持!