Kafka采用了独特的日志分段(Log Segment)存储策略,这是其高性能的核心基础。
Topic: user-events, Partition: 0
├── 00000000000000000000.log # 数据文件
├── 00000000000000000000.index # 偏移量索引
├── 00000000000000000000.timeindex # 时间戳索引
├── 00000000000001000000.log
├── 00000000000001000000.index
└── 00000000000001000000.timeindex
分段机制优势:
// 偏移量索引结构
class OffsetIndex {
private int relativeOffset; // 相对偏移量
private int position; // 在log文件中的物理位置
}
// 时间戳索引结构
class TimeIndex {
private long timestamp; // 时间戳
private int relativeOffset; // 对应的相对偏移量
}
索引查找流程:
Kafka利用Linux的sendfile()
系统调用实现零拷贝,大幅提升I/O性能。
// 传统方式:4次拷贝
// 1. 磁盘 -> 内核缓冲区
// 2. 内核缓冲区 -> 用户空间
// 3. 用户空间 -> Socket缓冲区
// 4. Socket缓冲区 -> 网卡
// 零拷贝:2次拷贝
// 1. 磁盘 -> 内核缓冲区
// 2. 内核缓冲区 -> 网卡(DMA直接传输)
FileChannel.transferTo(position, count, socketChannel);
Kafka支持多种压缩算法,在存储和网络传输中都能显著减少资源消耗。
# Producer配置
compression.type=snappy # 可选:gzip, snappy, lz4, zstd
# 压缩性能对比
# gzip: 压缩率高,CPU消耗大
# snappy: 压缩速度快,压缩率中等
# lz4: 解压速度极快
# zstd: 平衡压缩率和速度
压缩机制:
# 查看页缓存使用情况
free -h
total used free shared buff/cache available
Mem: 15Gi 2.1Gi 8.2Gi 264Mi 5.4Gi 12Gi
Kafka充分利用操作系统的页缓存:
# 数据保留时间(7天)
log.retention.hours=168
log.retention.minutes=10080
log.retention.ms=604800000
# 检查间隔
log.retention.check.interval.ms=300000
# 分区最大大小(1GB)
log.retention.bytes=1073741824
# 分段文件大小(1GB)
log.segment.bytes=1073741824
# 启用日志压缩
log.cleanup.policy=compact
# 压缩配置
log.cleaner.enable=true
log.cleaner.threads=1
log.segment.ms=604800000
压缩原理:
原始日志:
key1 -> value1
key2 -> value2
key1 -> value3 # 更新
key3 -> value4
key1 -> value5 # 再次更新
压缩后:
key2 -> value2
key3 -> value4
key1 -> value5 # 只保留最新值
1. 默认分区器(DefaultPartitioner)
public class DefaultPartitioner implements Partitioner {
public int partition(String topic, Object key, byte[] keyBytes,
Object value, byte[] valueBytes, Cluster cluster) {
if (keyBytes == null) {
// 无key:轮询分区
return stickyPartitionCache.partition(topic, cluster);
}
// 有key:hash分区
return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
}
}
2. 自定义分区器
public class CustomPartitioner implements Partitioner {
@Override
public int partition(String topic, Object key, byte[] keyBytes,
Object value, byte[] valueBytes, Cluster cluster) {
String keyStr = (String) key;
if (keyStr.startsWith("VIP")) {
return 0; // VIP用户固定分区0
}
// 普通用户使用默认策略
return (keyStr.hashCode() & Integer.MAX_VALUE) %
cluster.partitionCountForTopic(topic);
}
}
3. 分区策略对比
策略 | 适用场景 | 优势 | 劣势 |
---|---|---|---|
轮询 | 无key消息 | 负载均衡 | 无序性保证 |
Hash | 有key消息 | 相同key有序 | 可能数据倾斜 |
自定义 | 特殊业务需求 | 灵活控制 | 复杂度高 |
acks
参数与重试机制Properties props = new Properties();
// acks=0: 不等待确认,最高性能,可能丢失数据
props.put("acks", "0");
// acks=1: 等待Leader确认,平衡性能和可靠性
props.put("acks", "1");
// acks=all/-1: 等待所有ISR副本确认,最高可靠性
props.put("acks", "all");
props.put("min.insync.replicas", "2"); // 最少同步副本数
acks机制对比:
acks=0:
Producer -> Leader (不等待)
性能:★★★★★
可靠性:★☆☆☆☆
acks=1:
Producer -> Leader -> ACK
性能:★★★☆☆
可靠性:★★★☆☆
acks=all:
Producer -> Leader -> Follower1 -> Follower2 -> ACK
性能:★★☆☆☆
可靠性:★★★★★
// 重试配置
props.put("retries", Integer.MAX_VALUE); // 重试次数
props.put("retry.backoff.ms", 100); // 重试间隔
props.put("delivery.timeout.ms", 120000); // 总超时时间
props.put("request.timeout.ms", 30000); // 单次请求超时
// 幂等性保证
props.put("enable.idempotence", true);
幂等性实现原理:
// Producer ID + Sequence Number 确保幂等
class ProducerRecord {
private long producerId; // 生产者唯一ID
private short epoch; // 生产者世代
private int sequenceNumber; // 序列号
}
linger.ms
优化// 批量配置
props.put("batch.size", 16384); // 批次大小(字节)
props.put("linger.ms", 5); // 等待时间(毫秒)
props.put("buffer.memory", 33554432); // 缓冲区大小
批量发送流程:
消息1 ──┐
消息2 ──┤
消息3 ──┼──> RecordBatch ──> 网络发送
消息4 ──┤ (16KB或5ms)
消息5 ──┘
1. 吞吐量优化
# 高吞吐量配置
batch.size=65536
linger.ms=20
compression.type=snappy
acks=1
2. 低延迟优化
# 低延迟配置
batch.size=0
linger.ms=0
compression.type=none
acks=1
3. 平衡配置
# 平衡配置
batch.size=16384
linger.ms=5
compression.type=lz4
acks=1
// 消费者组配置
Properties props = new Properties();
props.put("group.id", "user-analytics-group");
props.put("client.id", "consumer-1");
消费组特性:
1. Range策略(默认)
// 3个分区,2个消费者
// Consumer1: [0, 1]
// Consumer2: [2]
props.put("partition.assignment.strategy",
"org.apache.kafka.clients.consumer.RangeAssignor");
2. RoundRobin策略
// 轮询分配
// Consumer1: [0, 2]
// Consumer2: [1]
props.put("partition.assignment.strategy",
"org.apache.kafka.clients.consumer.RoundRobinAssignor");
3. Sticky策略
// 粘性分配,减少重新分配
props.put("partition.assignment.strategy",
"org.apache.kafka.clients.consumer.StickyAssignor");
4. CooperativeSticky策略
// 协作式粘性分配(推荐)
props.put("partition.assignment.strategy",
"org.apache.kafka.clients.consumer.CooperativeStickyAssignor");
// 消费者主动拉取
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
// 处理消息
processMessage(record);
}
}
Pull vs Push对比:
特性 | Pull模式 | Push模式 |
---|---|---|
控制权 | 消费者控制 | 生产者控制 |
背压处理 | 天然支持 | 需要额外机制 |
批量处理 | 容易实现 | 相对困难 |
实时性 | 可能有延迟 | 实时性好 |
1. 自动提交
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "5000");
2. 手动提交
props.put("enable.auto.commit", "false");
// 同步提交
consumer.commitSync();
// 异步提交
consumer.commitAsync((offsets, exception) -> {
if (exception != null) {
log.error("Commit failed", exception);
}
});
3. 指定位移提交
Map<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();
offsets.put(new TopicPartition("user-events", 0),
new OffsetAndMetadata(1000));
consumer.commitSync(offsets);
# 查看消费者组位移
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--group user-analytics-group --describe
GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG
user-analytics-group user-events 0 1000 1500 500
user-analytics-group user-events 1 800 1200 400
1. 减少Rebalance频率
// 增加会话超时时间
props.put("session.timeout.ms", "30000");
// 减少心跳间隔
props.put("heartbeat.interval.ms", "3000");
// 增加poll间隔
props.put("max.poll.interval.ms", "300000");
2. 使用CooperativeSticky策略
// 增量式Rebalance,减少停顿时间
props.put("partition.assignment.strategy",
"org.apache.kafka.clients.consumer.CooperativeStickyAssignor");
3. 监听Rebalance事件
consumer.subscribe(Arrays.asList("user-events"), new ConsumerRebalanceListener() {
@Override
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
// 分区被回收前的清理工作
consumer.commitSync();
log.info("Partitions revoked: {}", partitions);
}
@Override
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
// 分区分配后的初始化工作
log.info("Partitions assigned: {}", partitions);
}
});
// 生产者高吞吐量配置
Properties producerProps = new Properties();
producerProps.put("bootstrap.servers", "localhost:9092");
producerProps.put("acks", "1");
producerProps.put("batch.size", 65536);
producerProps.put("linger.ms", 20);
producerProps.put("compression.type", "snappy");
producerProps.put("buffer.memory", 67108864);
// 消费者高吞吐量配置
Properties consumerProps = new Properties();
consumerProps.put("bootstrap.servers", "localhost:9092");
consumerProps.put("group.id", "high-throughput-group");
consumerProps.put("fetch.min.bytes", 50000);
consumerProps.put("fetch.max.wait.ms", 500);
consumerProps.put("max.poll.records", 1000);
// 生产者低延迟配置
Properties producerProps = new Properties();
producerProps.put("acks", "1");
producerProps.put("batch.size", 0);
producerProps.put("linger.ms", 0);
producerProps.put("compression.type", "none");
// 消费者低延迟配置
Properties consumerProps = new Properties();
consumerProps.put("fetch.min.bytes", 1);
consumerProps.put("fetch.max.wait.ms", 0);
consumerProps.put("max.poll.records", 1);
通过深入学习Kafka核心原理,我们掌握了:
✅ 存储机制:分段存储、索引机制、零拷贝技术
✅ 生产者原理:分区策略、可靠性保证、批量优化
✅ 消费者原理:消费组管理、位移机制、再均衡流程
✅ 性能优化:针对不同场景的参数调优策略
核心思想:Kafka的设计哲学体现了"简单即美"的工程思维。理解其核心原理,不仅能帮助我们更好地使用Kafka,更能启发我们在系统设计中的思考。
如果这篇文章对你有帮助,欢迎点赞、收藏和分享。你的支持是我持续创作的动力!