数据密集型中间件Kafka、MySQL、ES、ZK、Redis的横向对比分析【持续更新ing】

450 阅读13分钟

各中间件的横向对比

内容太多,持续更新。建议收藏

可靠性问题

如何保证单机可靠性

中间件如何保证单机可靠性
通用1. 使用 O_Append 格式打开文件可以保证多进程, 多线程追加写入都是安全的. (NFS系统除外)
Kafkakafka默认情况下, 单机不保证可靠性, 只负责写入内存, 由OS负责磁盘刷新机制。kafka 可配置日志写入级别,支持fsync写入
mysql使用redolog , 默认刷盘机制为同步刷。redolog会使用两阶段提交落盘, 当节点故障后redolog和binlog可实现崩溃恢复
ElasticSearch使用translog 保证, 刷新磁盘可控制。节点故障后, 可以使用translog进行崩溃恢复.控制translog大小,避免崩溃恢复时间过长。和mysql类似. 但是由于es不存在binlog,所以不需要两阶段提交
zookeepermaster节点每次接受请求的时候 先写事务日志, 同时向从节点广播该决议, 从节点也是先写事务日志. 等半数都成功后, commit 数据到内存中。事务日志可保证 数据可靠性, 参数配置提供了同步和异步刷盘机制,  默认是同步刷盘
redisAOF 日志追加写入
hbase

部分写失败

写入磁盘过程中OS突然宕机, 造成部分写失败。只有 Mysql会更新原数据文件所以有这个问题, 其他中间件都是写新文件,所以不存在部分写问题

中间件是否考虑部分写
kafka每次都追加写入 log末尾。不用考虑部分写问题
mysqldouble write机制。由于redolog是增量日志, 崩溃时如果页已经不完整, 是不能使用增量日志的, mysql采用将脏页率先 顺序写入磁盘, 然后再次写数据页方式, 如果数据页部分写失败, 可以使用备份恢复
ElasticSearches每次都是新增一个segment, 不存在文件更新问题, 所以当segment新文件写失败, 只需要使用translog恢复就可以了. 而且es可以通过备份节点恢复数据!!!
zookeeperzookeeper 的快照发生在 事务日志堆积过多的时候, 此时 zk 会选择全量备份数据到磁盘, 所以不存在部分写问题.
redis
hbase

如何保证数据分布式可靠性

中间件如何保证数据分布式可靠性
kafkafollower副本同步数据, 由producer负责写入的可靠性, 可选择忽略写入成功, 只保证leader成功, 和ISR列表都成功. 失败producer则再次重试(非常明智的选择, 把一致性选择前置到生产者)
mysqlbinlog可以实现数据复制. 但是有一定的延迟. 所以mysql崩溃后, 从节点可能丢数据。binlog在事务commit时 追加写入
ElasticSearch每次写入请求都是一个同步写入, 即ES主分片会同步请求到备份上. 但是备份同步失败, 此次请求也是成功的. 客户端无法感知备份失败, 此时如果主节点挂了, 也没写磁盘,所以会丢数据.
zookeeper
redis
hbase

崩溃如何恢复数据

中间件崩溃如何恢复数据
kafka
mysql
ElasticSearch
zookeeper
redis
hbase

WAL 日志叫什么

中间件如何保证数据分布式可靠性
kafkaLog
mysqlredolog
ElasticSearchtranslog (transaction)
zookeepertransaction log
redisAOF read only file
hbaseWAL日志写入分为四个级别 1. 无 WAL 2. 异步 WAL 3 同步刷到 HDFS 内存 4, 同步刷新到 HDFS磁盘

WAL 日志何时刷新

WAL 日志一般 可以设置 同步flush 进磁盘 也可以异步。异步一般会定时刷新进磁盘

如果 WAL 文件如果有最大限制, 会在文件满了刷新进磁盘. 也可能定义请求量, 例如每 1w 次请求刷新一次LOG。

刷新日志也要考虑 磁盘写入毛刺问题, 避免瞬间大量刷新数据到磁盘, 要定时+IO限速(例如美团就对 kafka 的刷磁盘做了限速,解决毛刺).

hbase 也对日志写入做限速. 合并 hFile 也会限速.(合并写入速度上下限[10M, 20M 之间] 可根据磁盘性能做更改)

Es 同样也做了段合并时磁盘写入的限速. (同样做了合并线程数, 如果 hdd 使用 1 个线程,ssd 可以设置大一些)

为什么可靠性问题这么重要

如果出现数据丢失如何解决呢?

如果出现一个分片不可用, 导致客户端雪崩?

如果数据不一致, 导致查询结果不一致呢?

总结

mysql的数据可靠性问题包括redolog 是否同步写磁盘、binlog是否同步写磁盘、如果数据页还在buffer pool, mysql崩溃如何、如果数据页还在buffer pool或者os cache, 数据崩溃怎么办、数据页回写磁盘写到一半断电如何处理、mysql master 磁盘故障怎么办

一致性问题

如何保证一致性

中间件如何保证一致性
kafkakafka 的consumer只会消费 已经同步到副本的数据, 而且只有leader分区提供读写,所以consumer的读取一致性, 可以保证. 写入数据暂时不一致
mysqlmysql写入时无法做到 同步写入.  无法保证主从数据强一致性.
ElasticSearch两个地方无法保证一致性。search主从分片无法保证搜索一致性, 因为各个节点 refresh 创建新分段 是完全独立的。写入过程没有强一致性保证, 全部读写成功, leader不关心备份写入失败.
zookeeperzookeeper保证的是最终一致性, 无法保证同一时刻, 客户端可看见已知数据.但保证client看到已成功的数据一定不会丢失, (半数写成功), 此刻一致性, 代表客户端可保证最终得到一致的数据视图。原子性:更新操作要么成功要么失败,不存在中间状态。顺序一致性: 来自同一客户端的更新将按顺序进行。及时性:客户端会及时获取ZooKeeper最新状态。不一致之处,由于半数以上提交即可成功,主从节点 某一个时刻, 读取数据是可能不一致的.
redis
hbase

一致性问题

中间件一致性问题
kafka主副本同步数据不一致.但是不会影响 consumer端消费
mysql1. 主从数据不一致 因为 binlog 异步复制。2. 避免 mysql 存储大数据对象,导致带宽被占满
ElasticSearch主从数据由于写入不同步导致一致性 。refresh 不同步导致搜索结果不一致。refresh 后再内存中的数据没有及时刷新到磁盘, 导致主从磁盘数据不一致。可指定主分片查询
zookeeper
redis
hbase

如何主从数据同步

中间件如何主从数据同步
kafkaproducer可以同步等待复制全部完成。ISR列表定期+通知拉取消息。半异步复制
mysqlbinlog 全异步复制
ElasticSearch每次写入请求会同步数据、半异步复制
zookeeper半异步复制
redis主从同步 采用。全异步复制
hbase

实时性

中间件实时性
kafkaISR follower同步副本后, 才会被消费
mysql实时
ElasticSearch近实时
zookeeper
redis
hbase

隔离性

中间件隔离性
kafka完全独立
mysql事务锁竞争
ElasticSearch乐观版本控制
zookeeper
redis
hbase

读写优化

数据写入优化

中间件数据写入优化
kafka追加写入 +异步刷新磁盘+ 分区
mysql写入内存+ redolog 异步追加磁盘写
ElasticSearch使用分片。写入内存+异步新建分段(耗费cpu+批量聚合)+ 定时刷新分段到磁盘。translog追加磁盘写
zookeeper
redis
hbase

数据写入速度

中间件数据写入速度
kafka写内存+并发写
mysql受限于 写锁, 存在热点问题. 插入不受影响
ElasticSearch受限于内存建索引速度+ 并发写入
zookeeper同步串行写
redis串行写(redis cluster可并发)
hbase完全并发写(更新+删除+新增都是 插入新纪录)

数据读优化

中间件数据读优化
kafka由于kafka的消息基于分区消费, 不需要对消息额外的处理, 可以使用零拷贝技术, 直接发送到网卡。无从库,但有分区
mysql索引+ buffer 缓冲池。查询走从库
ElasticSearch分片 副本可读。segement只读属性, 可以方便加载进pagecache  (mmap后可不用上下文切换), es使用mmap读取索引文件, pageCache 最好要占用OS一半以上内存。(mysql和es的数据从pagecache检索到 需要用户态处理下, 所以无法使用零拷贝。mysql select,分页, Es 对数据聚合处理) 路由+ 指定搜索主节
zookeeperzookeeper 将DataTree 数据维护在ConcurrentHashMap中, 其中path是key, dataNote是value, 这样每次读取请求都是访问用户态内存.
redis
hbase

存储文件的创建方式

中间件存储文件的创建方式
kafka追加写入日志可能采用预分配机制,kafka segemnt日志文件大小固定, 完全可能采用了预分配机制
mysql追加写入日志采用预分配机制。性能对比 采用预分配高并发下可快10倍 。好处: 提高速度, 减少磁盘碎片化, 不足: 磁盘利用率不高。mysql innodb redo采用了预分配。binlog则是追加写入,没有分割文件
ElasticSearches 中translog 可能采用了预分配机制.
zookeeperzookeeper创建事务日志文件时, 会预分配磁盘空间,这样防止当连续空间不足时, 磁盘重新寻道。文件系统为文件分配磁盘常见: 连续分配、链接分配和索引分配. (一个文件系统一般使用一种方式)连续分配磁盘空间, 可保证高并发写时, 磁盘不会为了分配新空间, 重新寻道
redis
hbase

读写性能如何

中间件读写性能如何
kafka写入高吞吐, 分片数足够多,撑 10w+写入
mysqlTps 5000 qps 就有压力了(看索引和查询数据量)
ElasticSearch
zookeeper写入串行,读取比较快完全是内存访问,但客户端太多,心跳难以维持。不能应用用户端流量。
redis读写都很快10ms以下。 redis cluster 超高并发千万w+读取,10w+写入,和集群规模有关
hbase写入 10ms 以下, 大规模 1w+写入不在话下,读取 100ms+ 级别

故障处理

中间件故障处理
kafka
mysql
ElasticSearch
zookeeper
redis
hbaseHbase regionServer可能会挂掉, 挂掉后该分片不可用, 恢复时间要看WAL 日志有多少需要恢复。客户端为了避免雪崩可以考虑1. hbase 设置备份集群, 当挂掉后, client 直接访问备份集群 2. client 使用 hystrix 做熔断降级,避免读接口雪崩。3.写入可以使用 redis 或者 mysql 做一层备份的

数据清理

中间件隔离性
kafka淘汰策略
mysql基于时间, 默认七天. 也可以基于日志大小
ElasticSearch
zookeeper
redis
hbase
## 隔离性

集群管理(是否主从模式、无主模式)

如何选主

中间件隔离性
kafka使用Zookeeper轩主。 控制器 Controller选举zk 指定路径下创建临时节点, 谁成功谁是主节点。分区 Leader  选主由Controller Leader 控制, 根据 ISR 集合, 调用配置的分区选择算法 选举 leader 。2. ISR 中有幸存的分区, 选择幸存的ISR 没有幸存的, 选择其他幸存的(此时会选中不在 ISR 中的幸存者, 会丢失一部分数据)都没有幸存的 支持配置 选择 ISR 中第一个复活的选择第一个复活的 ,默认配置 会丢数据
mysql主从模式
ElasticSearchbully 选举
zookeeperZAB 一致性协议选主
redisredis cluster  无主。redis  哨兵 选 主从
hbase

选举算法

中间件隔离性
kafka依赖zookeeper选举
mysql无,依赖于其他系统选主,需要进行存活检测选主
ElasticSearchbully
zookeeperzab
redisredis sentinel raft算法。redis cluster
hbase
## 隔离性

何时选主

如何避免脑裂

元数据管理

数据分片

如何分片

中间件如何分片
kafka基于partition. 默认 1 个分区,
mysql有分区, 创建表时指定分区策略, 使用较少, 主要使用分表
ElasticSearch创建索引时指定分片数. 分片数应该基于数据量设置。数据量过大时, 可以考虑索引别名. 创建多个索引优化查询
zookeeper
redis
hbase

如何自动分片

中间件如何自动分片
kafka不会自动分片
mysql不会自动分片
ElasticSearch使用别名来替代分片
zookeeper
redis
hbaseHbase 定义了每个 Region 文件数大小, 超过后自动分片.hbase.hregion.max

客户端如何发送请求到指定的master节点

中间件隔离性
kafka客户端每次初始化时会从任意节点获取 元数据信息。缓存下来将指定分区的消息发送到指定的 节点一旦出现错误 "非分区首领的错误响应", 重新更新元数据到本地重定向等通知也会更新缓存分区信息 + 错误重定向 + 重试
mysql
ElasticSearches 节点会转发请求 到 master  分片
zookeeperfollower 节点会转发写请求到 leader
redissmart redis 客户端每次初始化会从集群拉取所有的槽和节点映射。当节点发送到错误的节点,redis 节点会返回 moved 重定向, 此时客户端会重新拉取元数据信息. 重试N次直到 成功缓存槽信息+ 错误重定向+ 重试
hbase

分片的叫法

中间件分片的叫法
kafka分区partition
mysqlmysql分区表
ElasticSearchShard
zookeeper
redisredis cluster模式下叫 Slot(槽)
hbaseRegion

扩缩容过程

中间件隔离性
kafka直接修改分片数量即可, 不需要迁移数据, 因为 kafka 基本上不存在根据分区获取消息的场景.
mysql分表
ElasticSearchEs 扩容分片操作很重量级. 有三种实现1. split 将一个表的分片扩容到 2,3,4 倍,但是需要停止写入。2. reindex 重新将一个索引数据重建到新索引,新索引分片数增加即可。需要保证新旧分片双写. 同时不存在 delete 操作
zookeeper很少扩容
redis扩容需要迁移分片和数据
hbase分为 RegionServer 扩容和 Region 扩容。RegionServer 扩容时由于 region 存储和计算分离, 只是 meta 信息的更改而已。hbase region 会自动分裂和扩容, 扩容发生时, 不影响使用, hfile 不会立即分裂, 子 region 使用 reference 文件引用父 region 的 hfile 文件。 读取时不受影响,写入直接归并进新的hFile 文件。最终将会在major campaction时合并所有的 hFile 文件,此时引用文件也会被合并.