消息中间件(kafka)如何实现每秒几十万的高并发写入?

本文深入探讨Kafka如何利用页缓存技术和磁盘顺序写,结合零拷贝技术,实现高吞吐量和低延迟的消息处理,适用于大规模数据传输场景。

文章来源:石杉的架构笔记(id:shishan100)

“ 这篇文章来聊一下Kafka的一些架构设计原理,这也是互联网公司面试时非常高频的技术考点。

Kafka是高吞吐低延迟的高并发、高性能的消息中间件,在大数据领域有极为广泛的运用。配置良好的Kafka集群甚至可以做到每秒几十万、上百万的超高并发写入。

那么Kafka到底是如何做到这么高的吞吐量和性能的呢?这篇文章我们来一点一点说一下。”

目录

1、页缓存技术 + 磁盘顺序写

2、零拷贝技术

3、最后的总结

 

1、页缓存技术 + 磁盘顺序写

首先Kafka每次接收到数据都会往磁盘上去写,如下图所示。

 

 

那么在这里我们不禁有一个疑问了,如果把数据基于磁盘来存储,频繁的往磁盘文件里写数据,这个性能会不会很差?大家肯定都觉得磁盘写性能是极差的。

 

没错,要是真的跟上面那个图那么简单的话,那确实这个性能是比较差的。

 

但是实际上Kafka在这里有极为优秀和出色的设计,就是为了保证数据写入性能,首先Kafka是基于操作系统的页缓存来实现文件写入的。

 

操作系统本身有一层缓存,叫做page cache,是在内存里的缓存,我们也可以称之为os cache,意思就是操作系统自己管理的缓存。

 

你在写入磁盘文件的时候,可以直接写入这个os cache里,也就是仅仅写入内存中,接下来由操作系统自己决定什么时候把os cache里的数据真的刷入磁盘文件中。

 

仅仅这一个步骤,就可以将磁盘文件写性能提升很多了,因为其实这里相当于是在写内存,不是在写磁盘,大家看下图。

 

接着另外一个就是kafka写数据的时候,非常关键的一点,他是以磁盘顺序写的方式来写的。也就是说,仅仅将数据追加到文件的末尾,不是在文件的随机位置来修改数据。

 

普通的机械磁盘如果你要是随机写的话,确实性能极差,也就是随便找到文件的某个位置来写数据。

 

但是如果你是追加文件末尾按照顺序的方式来写数据的话,那么这种磁盘顺序写的性能基本上可以跟写内存的性能本身也是差不多的。

 

所以大家就知道了,上面那个图里,Kafka在写数据的时候,一方面基于了os层面的page cache来写数据,所以性能很高,本质就是在写内存罢了。

 

另外一个,他是采用磁盘顺序写的方式,所以即使数据刷入磁盘的时候,性能也是极高的,也跟写内存是差不多的。

 

基于上面两点,kafka就实现了写入数据的超高性能。

 

那么大家想想,假如说kafka写入一条数据要耗费1毫秒的时间,那么是不是每秒就是可以写入1000条数据?

 

但是假如kafka的性能极高,写入一条数据仅仅耗费0.01毫秒呢?那么每秒是不是就可以写入10万条数?

 

所以要保证每秒写入几万甚至几十万条数据的核心点,就是尽最大可能提升每条数据写入的性能,这样就可以在单位时间内写入更多的数据量,提升吞吐量。

 

 

 

2、零拷贝技术

说完了写入这块,再来谈谈消费这块。

 

大家应该都知道,从Kafka里我们经常要消费数据,那么消费的时候实际上就是要从kafka的磁盘文件里读取某条数据然后发送给下游的消费者,如下图所示。

 

那么这里如果频繁的从磁盘读数据然后发给消费者,性能瓶颈在哪里呢?

假设要是kafka什么优化都不做,就是很简单的从磁盘读数据发送给下游的消费者,那么大概过程如下所示:

 

先看看要读的数据在不在os cache里,如果不在的话就从磁盘文件里读取数据后放入os cache。

 

接着从操作系统的os cache里拷贝数据到应用程序进程的缓存里,再从应用程序进程的缓存里拷贝数据到操作系统层面的Socket缓存里,最后从Socket缓存里提取数据后发送到网卡,最后发送出去给下游消费。

 

整个过程,如下图所示:

大家看上图,很明显可以看到有两次没必要的拷贝吧!

 

一次是从操作系统的cache里拷贝到应用进程的缓存里,接着又从应用程序缓存里拷贝回操作系统的Socket缓存里。

 

而且为了进行这两次拷贝,中间还发生了好几次上下文切换,一会儿是应用程序在执行,一会儿上下文切换到操作系统来执行。

 

所以这种方式来读取数据是比较消耗性能的。

 

Kafka为了解决这个问题,在读数据的时候是引入零拷贝技术

 

也就是说,直接让操作系统的cache中的数据发送到网卡后传输给下游的消费者,中间跳过了两次拷贝数据的步骤,Socket缓存中仅仅会拷贝一个描述符过去,不会拷贝数据到Socket缓存。

 

大家看下图,体会一下这个精妙的过程:

通过零拷贝技术,就不需要把os cache里的数据拷贝到应用缓存,再从应用缓存拷贝到Socket缓存了,两次拷贝都省略了,所以叫做零拷贝。

 

对Socket缓存仅仅就是拷贝数据的描述符过去,然后数据就直接从os cache中发送到网卡上去了,这个过程大大的提升了数据消费时读取文件数据的性能。

 

而且大家会注意到,在从磁盘读数据的时候,会先看看os cache内存中是否有,如果有的话,其实读数据都是直接读内存的。

 

如果kafka集群经过良好的调优,大家会发现大量的数据都是直接写入os cache中,然后读数据的时候也是从os cache中读。

 

相当于是Kafka完全基于内存提供数据的写和读了,所以这个整体性能会极其的高。

 

说个题外话,下回有机会给大家说一下Elasticsearch的架构原理,其实ES底层也是大量基于os cache实现了海量数据的高性能检索的,跟Kafka原理类似。

 

 

 

3、最后的总结

 

通过这篇文章对kafka底层的页缓存技术的使用,磁盘顺序写的思路,以及零拷贝技术的运用,大家应该就明白Kafka每台机器在底层对数据进行写和读的时候采取的是什么样的思路,为什么他的性能可以那么高,做到每秒几十万的吞吐量。

 

这种设计思想对我们平时自己设计中间件的架构,或者是出去面试的时候,都有很大的帮助。

### 三级标题:高并发场景下的系统架构设计与性能优化 在高并发的电商平台秒杀活动中,系统需要应对短时间内几十万甚至上百万的访问请求,确保用户体验的同时,防止超发、数据不一致等问题。为了实现这一目标,系统架构设计和性能优化需从多个层面进行考虑。 #### 三级标题:页面静态化与前端优化 在用户访问高峰期,传统的动态页面渲染方式会导致应用服务器和数据库负载急剧上升。为缓解这一问题,可以通过将秒杀商品页面静态化,使用户请求直接由 CDN 或静态资源服务器处理,而不经过应用服务器。这种方式可以大幅降低后端系统的压力,提高响应速度[^2]。 #### 三级标题:缓存机制与 Redis 的应用 缓存是处理高并发的核心技术之一。使用 Redis 作为缓存中间件,可以将热点数据(如库存信息、用户限购数量)存储在内存中,避免频繁访问数据库。例如,在库存控制中,通过 Redis 的原子操作(如 `DECR`)实现库存的快速扣减,确保不会出现超卖现象。以下是一个使用 Redis 控制库存的示例代码: ```python import redis r = redis.StrictRedis(host='localhost', port=6379, db=0) def reduce_stock(product_id): stock_key = f"stock:{product_id}" result = r.decr(stock_key) if result < 0: r.incr(stock_key) # 回滚操作 return False return True ``` 通过 Redis 的原子性操作,可以在高并发下安全地处理库存变更,避免数据库锁竞争带来的性能瓶颈[^3]。 #### 三级标题:异步处理与消息队列 在秒杀过程中,用户提交订单后,系统需要完成库存扣减、订单生成、支付处理等多个步骤。为了提升系统的响应速度和稳定性,可以引入消息队列(如 RabbitMQ、Kafka)将订单处理流程异步化。用户下单后,订单信息被写入队列,由后台服务逐步处理,从而降低请求的响应时间,并提高系统的可扩展性。 #### 三级标题:数据库优化与锁机制 在高并发场景下,直接使用数据库的悲观锁(如 `SELECT ... FOR UPDATE`)会导致大量线程阻塞,影响系统性能。因此,建议在并发量较高时采用乐观锁机制,通过版本号或时间戳控制数据的一致性。例如,在更新库存时,使用如下 SQL 语句: ```sql UPDATE products SET stock = stock - 1 WHERE id = 1001 AND stock > 0; ``` 只有在库存大于零的情况下才会执行更新操作,从而避免超卖问题。如果并发量较低(<1000 QPS),悲观锁则更简单可靠[^3]。 #### 三级标题:限流与负载均衡 为了防止系统在高并发下崩溃,可以采用限流策略(如令牌桶、漏桶算法)控制请求的处理速率。同时,使用负载均衡技术(如 Nginx、LVS)将请求均匀分配到不同的服务器节点,避免单点故障。例如,Nginx 配置限流的示例: ```nginx http { limit_req_zone $binary_remote_addr zone=one:10m rate=100r/s; server { location /seckill { limit_req zone=one burst=5; proxy_pass http://backend; } } } ``` 该配置限制每秒最多处理 100 个请求,突发请求最多允许 5 个排队,从而保护后端服务免受突发流量冲击。 #### 三级标题:分布式部署与服务隔离 为了提升系统的可用性和扩展性,可以采用微服务架构,将商品服务、订单服务、库存服务等拆分为独立的服务模块,并部署在不同的服务器上。通过服务注册与发现机制(如 Zookeeper、Consul)实现服务的动态管理。此外,通过容器化技术(如 Docker、Kubernetes)实现快速部署和弹性伸缩,进一步提升系统的灵活性和容错能力。 --- ###
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值