Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Kafka Storage Engine

Kafka's storage engine provides durable, high-throughput message persistence through an append-only log structure.


Each partition is stored as an ordered, append-only sequence of records in log segments.

Topic: orders, Partition: 0Segment 0(00000000000000000000.log)offsets 0-999Segment 1(00000000000000001000.log)offsets 1000-1999Segment 2 (Active)(00000000000000002000.log)offsets 2000-currentActive segmentreceives new writesoldernewer
/var/kafka-logs/
├── orders-0/
│ ├── 00000000000000000000.log # Segment file
│ ├── 00000000000000000000.index # Offset index
│ ├── 00000000000000000000.timeindex # Timestamp index
│ ├── 00000000000000000000.txnindex # Transaction index
│ ├── 00000000000000000000.snapshot # Producer state
│ ├── 00000000000000001000.log
│ ├── 00000000000000001000.index
│ ├── 00000000000000001000.timeindex
│ ├── leader-epoch-checkpoint
│ └── partition.metadata
├── orders-1/
│ └── ...
└── orders-2/
└── ...

FilePurposeContent
.logMessage dataRecord batches
.indexOffset indexOffset → position mapping
.timeindexTimestamp indexTimestamp → offset mapping
.snapshotProducer stateIdempotency data
.txnindexTransaction indexAborted transactions

New segments are created when:

ConditionConfiguration
Size thresholdlog.segment.bytes (default: 1GB)
Time thresholdlog.roll.ms / log.roll.hours (broker defaults for segment.ms)
Index fulllog.index.size.max.bytes
# Segment configuration
log.segment.bytes=1073741824 # 1GB
log.roll.hours=168 # 7 days
log.index.size.max.bytes=10485760 # 10MB
log.index.interval.bytes=4096 # Index entry every 4KB

RecordBatch:
├── baseOffset: int64
├── batchLength: int32
├── partitionLeaderEpoch: int32
├── magic: int8 (2 for current version)
├── crc: int32
├── attributes: int16
│ ├── compression (bits 0-2)
│ ├── timestampType (bit 3)
│ ├── isTransactional (bit 4)
│ └── isControlBatch (bit 5)
├── lastOffsetDelta: int32
├── firstTimestamp: int64
├── maxTimestamp: int64
├── producerId: int64
├── producerEpoch: int16
├── baseSequence: int32
├── records: [Record]
Record:
├── length: varint
├── attributes: int8
├── timestampDelta: varlong
├── offsetDelta: varint
├── keyLength: varint
├── key: byte[]
├── valueLength: varint
├── value: byte[]
└── headers: [Header]

Maps logical offsets to physical file positions for efficient seeking.

Offset Index(.index)Log Segment(.log)offset: 0 → position: 0offset: 100 → position: 4096offset: 200 → position: 8192...Records 0-99Records 100-199Records 200-299Sparse indexOne entry perlog.index.interval.bytes

Maps timestamps to offsets for time-based seeking.

Terminal window
# Seek to offset by timestamp
kafka-consumer-groups.sh --bootstrap-server kafka:9092 \
--group my-group \
--topic orders \
--reset-offsets \
--to-datetime 2024-01-15T10:00:00.000 \
--execute

Delete segments older than retention period.

log.retention.ms=604800000 # 7 days (default)
log.retention.check.interval.ms=300000 # Check every 5 min

Retention aliases

log.retention.hours and log.retention.minutes are legacy aliases for log.retention.ms.

Delete oldest segments when partition exceeds size limit.

log.retention.bytes=107374182400 # 100GB per partition
PartitionSegment 0(7 days old)Segment 1(5 days old)Segment 2(3 days old)Segment 3(active)log.retention.hours=168 (7 days)Segment 0 eligible for deletionDELETE(exceeds retention)

Retains only the latest value for each key, useful for changelog/table semantics.

Before CompactionAfter CompactionK1:V1K2:V1K1:V2K3:V1K1:V3K2:V2K1:V3K2:V2K3:V1Only latest valueper key retainedcompact
# Enable compaction
log.cleanup.policy=compact
# Or both delete and compact
log.cleanup.policy=compact,delete
# Broker compaction defaults
log.cleaner.enable=true
log.cleaner.threads=1
log.cleaner.min.cleanable.ratio=0.5
log.cleaner.min.compaction.lag.ms=0
log.cleaner.delete.retention.ms=86400000 # 24h tombstone retention
# Segment eligibility
log.segment.bytes=1073741824
min.cleanable.dirty.ratio=0.5
<div class="admonition note">
<p class="admonition-title">Scope</p>
`log.cleaner.*` settings are broker defaults. `min.cleanable.dirty.ratio` is a topic-level override.
</div>

Delete a key by producing a record with null value (tombstone).

producer.send(new ProducerRecord<>("topic", "key-to-delete", null));

Tombstones are retained for log.cleaner.delete.retention.ms before removal.


For complete performance tuning including batching, compression selection, and thread model optimization, see Performance Internals.

Kafka uses sendfile() to transfer data directly from page cache to network socket.

Traditional CopyZero-Copy (sendfile)DiskPage CacheApplication BufferSocket BufferNetworkDiskPage CacheNetworkEliminates 2 copiesand context switches(disabled with TLS)1. read2. copy3. copy4. send1. read2. sendfile()

Kafka relies heavily on OS page cache for read performance.

Recommendation (Repository Guidance)Rationale
Allocate 25-50% RAM to page cacheCaches active segments
Use SSDsFaster random reads for index lookups
Separate disks for log.dirsParallel I/O

MetricDescription
kafka.log:type=Log,name=SizePartition size in bytes
kafka.log:type=Log,name=NumLogSegmentsSegment count
kafka.log:type=LogCleaner,name=cleaner-recopy-percentCompaction efficiency
kafka.log:type=LogCleaner,name=max-clean-time-secsCompaction duration
Terminal window
# Check partition sizes
du -sh /var/kafka-logs/*/
# List segment files
ls -la /var/kafka-logs/orders-0/
# Dump log segment
kafka-dump-log.sh --files /var/kafka-logs/orders-0/00000000000000000000.log \
--print-data-log