今天我就来聊聊 kafka 的存储系统架构设计, 说到存储系统,大家可能对 MySQL 比较熟悉,也知道 MySQL 是基于 B+ tree 来作为它的索引数据结构。
Kafka 又是基于什么机制来存储?为什么要设计成这样?它解决了什么问题?又是如何解决的?里面又用到了哪些高大上的技术?
带着这些疑问,我们就来和你聊一聊 Kafka 存储架构设计背后的深度思考和实现原理。
认真读完这篇文章,我相信你会对 Kafka 存储架构,有更加深刻的理解。也能有思路来触类旁通其他存储系统的架构。
图1: kafka 存储架构大纲
1
kafka 存储场景剖析
在讲解 Kafka 的存储方案之前 ,我们 先来看看 Kafka 官网给的定义:
Apache Kafka is an open-source distributed event streaming platform used by thousands of companies for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.
翻译成中文如下:
Apache kafka 是一个开源的分布式事件流处理平台,由成千上万的公司用于高性能的数据管道流分析、数据集成和关键任务的应用程序。
了解 Kafka 的老司机都知道它是从 Linkedin 内部孵化的项目, 从一开始,Kafka 就是为了解决大数据的实时日志流而生的, 每天要处理的日志量级在千亿规模 。 对于日志流的特点主要包括 1)、数据实时产生 2)、海量数据存储与处理, 所以它必然要面临分布式系统遇到的 高并发、高可用、高性能 等三高挑战。
通过上面的背景可以得出: 一切脱离业务场景谈架构设计都是耍流氓
综上我们看对于 Kafka 的存储需求来说,要保证以下几点:
1. 存储的主要是消息流(可以是简单的文本格式也可以是其他格式,对于 Broker 存储来说,它并不关心数据本身) 2. 要支持海量数据的高效存储、高持久化(保证重启后数据不丢失) 3. 要支持海量数据的高效检索(消费的时候可以通过offset或者时间戳高效查询并处理) 4. 要保证数据的安全性和稳定性、故障转移容错性
2
kafka 存储选型
有了上面的场景需求分析后, 我们接下来分析看看 Kafka 到底基于什么机制来存储的,能否直接用现有我们了解到的关系型数据库来实现呢?我们接着继续深度分析。
1
存储基本知识
我们先来了解下存储的基本知识或者常识, 在我们的认知中, 对于各个存储介质的速度大体同下图所示的,层级越高代表速度越快。很显然,磁盘处于一个比较尴尬的位置, 然而,事实上磁盘可以比我们预想的要快,也可能比我们预想的要慢,这完全取决于我们如何使用它。
图2: 各存储介质对比分布(来自网络)
关于磁盘和内存的 IO 速度, 我们可以从下图性能测试的结果看出普通机械磁盘的顺序I/O性能指标是53.2M values/s,而内存的随机I/O性能指标是36.7M values/s。由此似乎可以得出结论: 磁盘的顺序I/O性能要强于内存的随机I/O性能。
图3:磁盘和内存的 IO 速度对比(来自网络)
另外从整个数据读写性能方面,有不同的实现方式,要么提高读速度,要么提高写速度。
1. 提高读速度:利用索引,来提高查询速度,但是有了索引,大量写操作都会维护索引,那么会降低写入效率。常见的如关系型数据库:mysql等 2. 提高写速度:这种一般是采用日志存储, 通过顺序追加写的方式来提高写入速度,因为没有索引,无法快速查询,最严重的只能一行行遍历读取。常见的如大数据相关领域的基本都基于此方式来实现。
2
Kafka 存储方案剖析
上面从存储基础知识,以及存储介质 IO 速度、读写性能方面剖析了存储类系统的实现方式,那么我们来看看 Kafka 的存储到底该采用哪种方式来实现呢?
对于 Kafka 来说, 它主要用来处理海量数据流,这个场景的特点主要包括:
1. 写操作:写并发要求非常高,基本得达到百万级 TPS,顺序追加写日志即可,无需考虑更新操作
2. 读操作:相对写操作来说,比较简单,只要能按照一定规则高效查询即可(offset或者时间戳)
根据上面两点分析,对于写操作来说,直接采用 顺序追加写日志 的方式就可以满足 Kafka 对于百万TPS写入效率要求。但是如何解决高效查询这些日志呢? 直接采用 MySQL 的 B+ tree 数据结构存储是否可以?我们来逐一分析下:
如果采用 B+ tree 索引结构来进行存储,那么每次写都要维护索引,还需要有额外空间来存储索引、更会出现关系型数据库中经常出现的“数据页分裂”等操作, 对于 Kafka 这种高并发的系统来说,这些设计都太重了,所以并不适合用。
但是在数据库索引中,似乎有一种索引看起来非常适合此场景,即: 哈希索引【底层基于Hash Table 实现】 ,为了提高读速度, 我们只需要 在内存中维护一个映射关系 即可,每次根据 Offset 查询消息的时候, 从哈希表中得到偏移量,再去读文件就可以快速定位到要读的数据位置。但是哈希索引通常是需要常驻内存的,对于Ka