Kafka가 대용량 트래픽에 뛰어난 성능을 보이는 이유
Kafka가 메시징 애플리케이션을 넘어서 이벤트 스트리밍 플랫폼으로 군림할 수 있었던 이유가 뭘까?
그야말로 신강을 넘어 중원 무림을 점령해버린 천마..

통상적인 인식으로, Apache Kafka에 따라오는 키워드는 확장성, 고성능과 높은 처리량이다.
분산 메시징 시스템으로 처리량이 높은건 건 알겠는데, Redis와 다르게 Kafka는 데이터를 Broker의 disk에 저장한다.
disk I/O 비용이 상당히 큰 출혈인데, 얜 도대체 왜 빠른거지?????????????
왜빠른데요?왜Kafka는신이죠?왜다들이거에목메는거죠?왜성능이좋은건데요?전문엔지니어가필요한데도왜다들공부하는건데요???
순차적 I/O(Sequential I/O)
disk 접근은 어떻게 사용하는지에 따라 느릴수도 있고 빠를 수도 있다.

순차적으로 디스크에 접근하는 속도는 random access보다 150,000배 빠르고, 이는 메모리에 random access하는것 보다도 빠르다. 이 순차적 접근은 데이터를 segments로 저장하기에 가능하다.
- immutable : 한 번 쓰이면 변하지 않는 성질. 소비해도 데이터가 지워지지 않는다
- append-only : 오직 맨 끝에 추가될뿐
- sequential(↔fragments) : disk에 조각으로 나뉘지 않고 가능한 연속적인 블록에 저장
따라서, Kafka는 데이터를 메시지 큐로 저장하기에 순차적 I/O 혜택을 볼 수 있어 빠른 성능을 제공한다.
이 경우 Sequential Access가 항상 보장되는 것이 아니라, 다른 애프리케이션으로 인해 디스크 단편화가 발생할 소지가 있다.
Kafka의 데이터를 가급적 독립된 파일 시스템에 유지할 것을 권장하는 이유
Page Cache
- 디스크 검색을 줄이고 처리량을 높히기 위해 최신 OS들은 디스크 캐시(Page Cache)를 위해 더욱 공격적으로 메모리를 사용하게 되었다.
- 모든 disk 작업은 페이지 캐시를 거치며 유저 모드와 커널 모드의 중복 저장 없이 2배 정도의 캐시를 저장한다.
- page cache는 파일이나 소켓 I/O 연산에 대한 캐시로 사용된다.

또 Kafka는 Java와 Scala로 개발되었다.
JVM 위에서 작동하기에 Heap 메모리에 객체를 저장하는 비용이 크고, 힙이 커질수록 GC가 느려지는 단점이 있다.
그치만!!! 순차적 작업을 할 수 있는 Page cache를 사용하는게 다른 구조를 쓰거나, 메모리 캐시를 사용하는 것보다 좋은 성능을 낼 수 있다.
Zero Copy
zero copy는 서로 다른 메모리에서 복사되는 횟수를 줄여서 성능을 크게 향상 시키는 기술
일반적으로 네트워크를 통해 데이터가 전달되는 과정
- OS는 Disk로부터 데이터를 읽어 커널 영역의 page cache에 저장
- 애플리케이션은 page cache의 데이터를 사용자 영역으로 읽는다
- 애플리케이션은 커널의 socket buffer로 데이터를 쓴다
- OS는 socket buffer에 있는 데이터를 NIC buffer로 복사하고, 네트워크를 통해 전송

원래대로면 4번의 복사와 2번의 system call, 4번의 컨텍스트 스위칭이 발생한다.
Zero Copy 원칙을 따르면, 데이터를 read buffer→socket buffer 직접 보내서 컨텍스트 스위칭이 2번밖에 없어지게 된다.
Zero-copy 원칙을 구현하는 방법
1. Memory mapping - mnap()

사용자의 가상 메모리 주소를 커널 메모리 주소에 Mapping하는 방식
2. Direct memory access(DMA) - sendfile()
Topic에서 메시지를 소비할때, 데이터를 별도의 buffer로 복사하지 않고 Log를 직접 읽는다.
Linux의 sendfile() : read()와 write()를 대체한 새로운 system call

page cache에서 데이터를 직접 네트워크로 보내서 중복 복사 방지한다.
데이터를 page cache에서 socekt으로 전송하여 NIC buffer로의 복사만 이뤄진다
Context-Switching 절감하니 당연히 성능적 이점을 가져간다.
이외에도 몇몇 요소들이 있는데, 좀 중요하게 생각되는게 Topic Partitioning 정도라고 생각한다.
Topic Partitioning
Log Segments는 순차적이라 여러 쓰레드가 동시에 접근하는게 의미 없다.
최근의 것만 한 번 읽으면 되기에 병렬 처리가 불가능하다
같은 Topic이라도 데이터를 Sharding하여 저장하고, 각 파티션에 독립된 쓰레드를 붙여서 작업하도록 구성
→ 결과적으로 Topic 하나에 여러 쓰레드가 나눠서 병렬적으로 작업 수행 (=안됐는데 됐어요)
한 건의 경우 성능에 큰 영향이 없지만, 수십- 수백만 이상의 대량의 데이터가 Kafka를 통해 전달될 수록 더욱 큰 성능 차이를 맛보여 줄테다
Data Batch & Compression
데이터에 대한 배치와 압축을 지원해서 네트워킹 횟수를 대폭 줄였다.
+ 추가적인 자료
Java 11에서 여러 Zero-copy간의 성능 퍼포먼스를 비교하는 글을 찾았다.
도식에 따르면, mmap()으로 81% 성능 향상과 sendfile()로는 91%의 향상이 있다고 한다.
Meanwhile, there are also spaces to improve, such as the high expense of mapping operation, the limited use cases of sendfile() and the space restrictions.
Mapping 의 비용, sendfile()의 좁은 사용처나 공간 제약 등의 개선 여지가 있다고 한다.
인용 및 사진 자료 출처.
paka
https://docs.confluent.io/home/overview.html - Kafka and the File System