kafka的设计
动机
建⽴一个⾼吞吐量、低延迟、分布式的消息系统。但从设计来看,它可能更像是⼀个数据库的⽇志系统。
持久性
文件系统其实也挺快!
⾸先,SSD硬盘的顺序读写速度可以达到⼏百兆(普遍在300M-600M,有些好的硬盘读取的速度甚⾄可以接近千兆)。 其次,操作系统也做了许多策略来优化硬盘的读写速度。
为什么不使用内存来存储数据呢?理由如下:
1. 现代操作系统为了提高随机读硬盘的性能,会”胆大包天“地使⽤内存:将空的内存来作为硬盘的⻚缓存,所以,即使基于文件系统,可能也会获得不错的性能。如果再在进程里维护⼀个数据内存,可能会导致同一份数据在内存中存储了两份。
2. 此外,kafka是基于JVM开发的(使⽤了scala语言)。JVM的内存管理有两个显而易见的问题:
a) 存储对象的时候,jvm还需要存储对象头等额外的信息,经常会导致需要使用接近数据⼤小两倍的内存。
b) 当内存增⻓的时候,内存的垃圾回收将越来越慢
综上所述,基于⽂件系统并依赖于页缓存来设计一个系统,会⽐基于内存更优。有⼏个优势:
a) 32G内存的机器,可以利用到的页缓存可达到 28-30G,⽽且没有GC的困扰
b) 重启服务时⾮常快,⽽纯内存的系统,10G的数据在服务重启时至少需要10分钟
c) 硬盘和内存之间的数据映射更为简单了,因为操作系统已经帮你做了大部分的工作
性能
在很多系统中,由于某些下游基础服务很容易成为瓶颈,涉及到这些瓶颈服务的小变化也会导致产生问题。
硬盘的顺序读写性能,我们已经阐述过了,事实上,硬盘的顺序读写性能是很有保证的,并不会成为系统瓶颈。除此之外,可能还存在着两个隐藏的瓶颈:
1. 过多的⼩数据的零碎的IO操作
2. 昂贵的字节复制:例如要将一段数据发送到⽹卡,要经过以下步骤:
1) 从硬盘读取到内核空间(read系统调⽤用)
2) 从内核空间复制到⽤户空间(read系统调用)
3) 从⽤户空间⼜写⼊内核空间(需要写入socket buffer)
4) 从socket buffer复制到NIC buffer
如何解决以上两个问题?
1. 使用“message set”的模型,⼀次性传输/写入/读取多条数据
2. 使用sendfile系统调⽤,将数据从⻚缓存(pagecache)直接拷⻉到NIC缓存 (NIC:Network Interface Card),省去中间的许多步骤
消费者的消费position消费者的消费记录对⼀个消息系统的来说,也是性能关键之⼀。
按照传统的消息系统(例如beanstalkd),消费者每成功消费一条消息后,会给服务器器broker返回⼀一个ACK,然后broker将对应的消息删除掉。这样其实会有一些问题:
1) ACK丢失会导致消息被处理多次
2) 性能会受影响,因为每条消息都需要维护多种状态
kafka对消费的position有着更好的处理方式: ⾸先,我们知道,kafka对每个topic可以分成多个partition,每个partition限制了最多只能有⼀个cosumer来处理(如果有多个consumer,那么其余的consumer将收不到数据),同时消费完的消息也不用删除,这就使得每个consumer在某个partition中的消费position是一个简单的整数,⽽不用维护各种 状态。此外,还有⼀个好处是,消费者可以⾃行调整它在某个partition的po