kafka速度快的原因

我们都知道Kafka非常快,比绝大多数的市场上其他消息中间件都要快。这里来研究下那么为什么Kafka那么快(当然不会是因为它用了Scala)。

Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间。

但是实际上,Kafka其中一个特性却是高吞吐率,即使是普通的服务器,Kafka也能轻松支持每秒百万级的写入请求,超过了大部分的消息中间件。这种特性使得Kafka在日志处理等海量数据场景中应用广泛。那么为什么Kafka速度那么快,可以从数据写入和数据读取两方面来分析。

Kafka的数据写入(生产者)

生产者(Producer)是负责向Kafka提交数据的,Kafka会把收到的消息都写入到磁盘中,因此可以认为它绝对不会丢失数据。

而为了优化写入速度,Kafka采用了两种技术,一种是顺序写入,一种是MMFile。

顺序写入

磁盘读写的快慢取决于你怎么使用它,一般可以分为顺序读写或者随机读写。

因为硬盘是机械结构,每次读写都会经过一个【寻址->写入】的过程,其中的寻址是一个十分耗时的机械动作,所以硬盘最讨厌随机I/O,最喜欢顺序I/O。为了提高读写硬盘的速度,Kafka就是使用的顺序I/O。而且Linux对于磁盘的读写优化也比较多,包括read-ahead、write-behind和磁盘缓存等。更多的,对Java的内存管理和垃圾回收会有优化,因为如果在内存做这些操作的时候,一个会导致Java对象的内存开销很大,另一个是随着堆内存数据的增多,Java的GC时间会变得很长。

因此可以总结出使用磁盘操作有以下几个好处:

1.磁盘顺序读写速度超过内存随机读写。

2.JVM的GC效率低,内存占用大,使用磁盘可以避免这一问题。

3.系统冷启动后,磁盘上的缓存依然可用(内存一旦关机数据就会清空,持久化到磁盘上则不会)。

上图就展示了Kafka是如何写入数据的,每一个Partition其实都是一个文件,收到消息后Kafka会把数据插入到文件的末尾(虚线框的部分)。

但是这种方法存在一个缺陷:没有办法删除数据。一次Kafka是不会删除数据的,它只会把所有的数据都保留下来,每个消费者(Consumer)对每个Topic都有一个offset用来表示读取到了第几条数据。

上图中有两个消费者,Consumer1有两个offset分别对应Partition0和Partition1(假设每一个Topic是一个Partition);Consumer2有一个offset对应Partition2.这个offset是由客户端SKD负责保存的,Kafka的Broker完全无视这个东西的存在;一般情况下SDK会把它保存到Zookeeper里面。(所以需要给Consumer提供Zookeeper的地址)。

如果数据完全不删除,那么肯定会导致硬盘爆满,所以Kafka提供了两种策略来删除数据,一是基于时间,二是基于Partition文件大小。具体配置可以参看它的配置文档。

MMFiles(Memory Mapped Files)

即便是顺序写入磁盘,磁盘的访问速度还是不可能追上内存的。所以Kafka的数据并不是实时的写入硬盘,它充分利用了现代操作系统的分页存储来利用内存,以此来提高I/O效率。Memory Mapped Files(后面简称MMAP)也被翻译成内存映射文件,在64位操作系统中一般可以表示20G的数据文件。它的工作原理是直接利用操作系统的Page来实现文件到物理内存的直接映射。完成映射之后,你对物理内存的操作会被同步到硬盘上(操作系统在适当的时候)。

通过MMAP,进程就可以像读写硬盘一样读写内存(当然是虚拟机内存),也不必关系内存的大小,因为有虚拟内存为我们兜底。使用这种方式可以获取很大的I/O提升,省去了用户空间到内核空间复制的开销(调用文件的read会有把数据先放到内核空间的内存中,然后再复制到用户空间的内存中)。但是这样也有一个很明显的缺陷:不可靠,因为写到MMAP中的数据并没有被真正地写入到硬盘中,操作系统会在程序主动调用flush命令的时候才会把数据真正地写入到硬盘中。Kafka提供了一个参数prducer.type来控制是不是主动flush,如果Kafka写入到MMAP之后就立即flush然后再返回Producer,就叫做同步(sync);如果Kafka写入到MMAP之后立即返回Producer不调用flush,就叫做异步(async)。

MMAP其实是Linux中的一个函数,就是用来实现内存映射的。Java的NIO提供了一个MappedByteBuffer类来实现内存映射(因此Kafka是沾了Java的光,而不是Scala)。

Kafka的数据读取(消费者)

为什么Kafka使用磁盘文件还能那么快——一个用硬盘的比用内存的还快,这绝对违反常识,因为Kafka作弊了,无论是顺序写入还是MMAP,其实都是Kafka作弊前的准备工作。

Zero Copy

Kafka使用了基于sendfile的Zero Copy提高Web Server静态文件的速度。

传统模式下,从硬盘读取一个文件是这样的:

1.调用read函数,文件数据被copy到内核的缓冲区(read是系统调用,放到了DMA,所以用内核空间)。

2.read函数返回,文件数据从内核缓冲区copy到用户缓冲区。

3.write函数调用,将文件数据从用户缓冲区copy到内核与Socket相关的缓冲区。

4.数据从Socket缓冲区copy到相关协议引擎(网卡)。

以上细节是传统的read/write方式进行网络传输的方式,我们可以看到,在这个过程当中,文件数据实际上是经过了四次copy操作:硬盘—>内核buf—>用户buf—>socket相关缓冲区—>协议引擎。而sendfile系统调用则是提供了一种减少以上多次copy,提升文件传输性能的方法。Kafka在内核版本2.1中,引用了sendfile系统调用,以此简化网络上和两个本地文件之间的数据传输。sendfile的引入不仅减少了数据复制,还减少了上下文的切换:sendfile(socket, file, len)。

运行流程如下:

1.sendfile系统调用,文件数据被copy至内核缓冲区。

2.再从内核缓冲区copy至内核中socket相关的缓冲区。

3.最后再socket相关的缓冲区copy到协议引擎。

相较传统read/write方式,2.1版本内核引进的sendfile已经减少了内核缓冲区到user缓冲区,再由user缓冲区到socket相关缓冲区的文件copy,而在内核版本2.4之后,文件描述符结果被改变,sendfile实现了更简单的方式,再次减少了一次copy操作。

在apache,nginx,lighttpd等web服务器当中,都有一项sendfile相关的配置,使用sendfile可以大幅提升文件传输性能。

Kafka把所有的消息都存放在一个一个的文件中,当消费者需要数据的时候Kafka直接把文件发送给消费者,配合MMAP作为文件读写方式,直接把它传给sendfile。

Java的NIO提供了FileChannle,它的transferTo()方法和transferFrom()方法就是Zero Copy。

Kafka的批量压缩

在很多情况下,系统的瓶颈不是CPU或磁盘,而是网络IO,对于需要在广域网上的数据中心之间发送消息的数据流水线尤其如此。进行数据压缩会消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑。

1.如果每个消息都压缩,但是压缩率相对很低,所以Kafka使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩。

2.Kafka允许使用递归的消息集合,批量的消息可以通过压缩的形式传输并且在日志中也可以保持压缩格式,直到被消费者解压缩。

3.Kafka支持多种压缩协议,包括Gzip和Snappy压缩协议。

Kafka速度快的秘密——作弊

Kafka把所有的消息都存放在一个一个的文件中,当消费者需要数据的时候Kafka直接把文件发送给消费者。这就是秘诀所在,比如:10W的消息组合在一起是10MB的数据量,然后Kafka用类似于发文件的方式直接扔出去了,如果消费者和生产者之间的网络非常好(只要网络稍微正常一点10MB根本不是事。。。家里上网都是100Mbps的带宽了),10MB可能只需要1s。所以答案是——10W的TPS,Kafka每秒钟处理了10W条消息。
可能你会说:不可能把整个文件发出去吧?里面还有一些不需要的消息呢?是的,Kafka作为一个【高级作弊分子】自然要把作弊做的有逼格。Zero Copy对应的是sendfile这个函数(以Linux为例),这个函数接受:

1.out_fd作为输出(一般及时socket的句柄)。

2.in_fd作为输入文件句柄。

3.off_t表示in_fd的偏移(从哪里开始读取)。

4.size_t表示读取多少个。

没错,Kafka是用MMAP作为文件读写方式的,它就是一个文件句柄,所以直接把它传给sendfile;偏移也好解决,用户会自己保持这个offset,每次请求都会发送这个offset。(还记得吗?放在zookeeper中的);数据量更容易解决了,如果消费者想要更快,就全部扔给消费者。如果这样做一般情况下消费者肯定直接就被压死了;所以Kafka提供了的两种方式——Push,我全部扔给你了,你死了不管我的事情;Pull,好吧你告诉我你需要多少个,我给你多少个。

总结

Kafka速度的秘诀在于,它把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络IO的损耗,通过MMAP提高I/O的速度。写入数据的时候,由于单个Partition(分区)是末尾添加的所以速度最优;读取数据的时候配合sendfile直接暴力输入。阿里的RocketMQ也是这种模式,只不过是用Java写的。

"你可以迷茫,但是请你不要虚度。"

原文地址:https://www.cnblogs.com/yanggb/p/11063942.html

时间: 2024-11-02 12:04:03

kafka速度快的原因的相关文章

使用left join比直接使用where速度快的原因

多表使用left join只是把主表里的所有数据查询出来,其他表只查询表中的符合条件的某一条记录,所以速度非常快:而多表使用where内联,是把所有表的数据全查出来,然后进行比对,所以速度非常慢. 使用left join要注意确定哪一张表是主表,如果无法确定主表,则选择哪张表查询的字段最多,就把哪张表作为主表. 示例如下: 使用left join,同样的数据量,时间不到1秒钟! SELECT a.projectno,MAX(a.projectname) projectname,max(a.pro

Kafka重复消费和丢失数据研究

Kafka重复消费原因 底层根本原因:已经消费了数据,但是offset没提交. 原因1:强行kill线程,导致消费后的数据,offset没有提交. 原因2:设置offset为自动提交,关闭kafka时,如果在close之前,调用 consumer.unsubscribe() 则有可能部分offset没提交,下次重启会重复消费.例如: try { consumer.unsubscribe(); } catch (Exception e) { } try { consumer.close(); }

分布式消息系统:Kafka

1.为什么要有Kafka?  [出自 Hrq] Kafka是一个消息系统, 原本开发自LinkedIn,用作LinkedIn的活动流(activity stream)和运营数据处理管道(pipeline)的基础.现在主要用作数据管道(data pipeline)和消息系统 Kafka出现的原因: l  传统的日志文件统计分析对离线处理(如报表和批处理)不错,但对于实时处理来说其时延太大,而且还具有较高的运营复杂度. l  现有的消息队列系统虽很适合于在实时或近实时(near-real-time)

kafka kerberos 认证访问与非认证访问共存下的ACL问题

在一个正在运行的kafka集群中添加kerberos认证和ACL权限控制,同时保证以前所有的producer\consumer服务不中断 解决方式: 使kafka集群监听两个端口,一个为无认证连接,另一个为kerberos的认证连接 这时候在配置ACL的时候出了问题: 假如我以kerberos认证的方式连接kafka集群,那么我的用户名是principal的primary部分.例如principal是  kafka/[email protected] ,那么我的用户名就是kafka. 这时候我只

kafka概念原理

为什么需要消息系统:由于消息发送者不需要等待消息接收者处理数据就可以返回,系统具有更好的响应延时,同时,在网站访问高峰,消息可以暂时存储在消息队列中等待消息接收者根据自己负载处理能力控制消息处理速度,减轻数据库等后端存储的负载压力 1.解耦:允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余:消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险.许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的

Kafka设计理念浅析

本文将从以下两个方面去尝试讲解Kafka的设计理念,主要参考文献在这里: Kafka设计背景及原因 Kafka的设计特色 Kafka设计背景及原因 Kafka最初被LinkedIn设计来处理活动流数据(activity stream data)和系统处理数据(operaitonal data).活动流数据是指像page view.用户搜索关键词等等通过用户操作产生的数据,它的常见场景有时间线(time line)即新鲜事提醒.用户浏览量 搜索量排名等等.系统处理数据是服务器性能相关的数据,如CP

kafka 消费?

前置资料  kafka kafka消费中的问题及解决方法: 情况1: 问题:脚本读取kafka 数据,写入到数据库,有时候出现MySQL server has gone away,导致脚本死掉.再次启动,这过程中的kafka数据丢失. 原因:MySQL server has gone away 出现可能是连接超时,可能超过每秒请求上限-这些异常是小概率事件,难以避免.git kafka 的demo脚本是实时监听的脚本, 简单明了,没有再去针对kafka偏移量研究:但是一旦断掉, 过程中的kafk

kafka消息存储与partition副本原理

消息的存储原理: 消息的文件存储机制: 前面我们知道了一个 topic 的多个 partition 在物理磁盘上的保存路径,那么我们再来分析日志的存储方式.通过 ll /tmp/kafka-logs/testTopic-0/ 命令找到对应 partition 下的日志内容: kafka 是通过分段的方式将 Log 分为多个 LogSegment,LogSegment 是一个逻辑上的概念,一个 LogSegment 对应磁盘上的一个日志文件和一个索引文件,其中日志文件是用来记录消息的.索引文件是用

使用ranger对kafka进行鉴权

使用ranger对kafka进行鉴权测试环境:ranger-kafka-plugin为0.6.3版本,kafka版本为kafka_2.10-0.10.1.1,且kafka broker为一个节点.一.Ranger对kafka进行权限控制,前提需要kafka开启kerberos认证(注意:若kafka不开启kerberos的话Ranger无法获取kafka相关操作的用户,进而无法完成鉴权操作)二.开启kerberos认证包括zookeeper开启kerberos认证,kafka开启zookeepe