Kafka 是一个 分布式、可持久化的消息队列系统,但它比传统消息队列更强:能存储大量数据、支持高吞吐、还能重复消费历史消息。
Kafka 集群由多个 Broker 组成(Broker 就是一台 Kafka 服务器)。
消息通过 Producer 发送到 Broker 的某个 Topic 里;
Consumer 从 Topic 拉取消息进行消费。
Producer1 ──┐ Producer2 ──┼──► Broker1 (Leader) ──┬──► Consumer Group A (成员1) Producer3 ──┘ Broker2 (Follower) └──► Consumer Group A (成员2) Broker3
order-topic,“用户行为”发到 user-action-topic。acks 参数控制可靠性:
acks=0:不等待确认,最快,可能丢数据。acks=1:Leader 写入成功即确认。acks=-1/all:Leader 和所有 ISR(同步副本)都写入才确认,最可靠。group.id 的 Consumer 实例。举例:Topic order-topic 有 3 个分区(P0, P1, P2)。
__consumer_offsets 中。| 特性 | Topic | Consumer Group |
|---|---|---|
| 角色 | 消息的存放地 | 消费者的组织单位 |
| 关键属性 | 分区数、副本数 | group.id |
| 消息流向 | Producer → Topic → Consumer | 组内消费者共同承担分区消费 |
| 消息消费范围 | 每个分区只能被同一个组内的一个消费者消费 | 不同组可以独立消费全部消息 |
一个经典的提问:如果消费者组内的消费者数量 > Topic 的分区数量,会怎样?
→ 多余的消费者将空闲(没有分区可分配)。因此一般设置消费者数 ≤ 分区数。
| 概念 | 一句话解释 |
|---|---|
| Topic | 逻辑上的消息分类 |
| Partition | Topic 的物理分片,提供顺序和并行能力 |
| Producer | 发消息的人 |
| Consumer | 收消息的人 |
| Consumer Group | 一组消费者,共同消费一个 Topic,彼此分担负载 |
| Broker | Kafka 服务器节点 |
| Offset | 分区内消息的序号 |
| ISR | 与 Leader 保持同步的副本集合 |
下面解析雪花算法(Snowflake) 的核心原理,以及它的优势与局限。
这是Twitter在2010年开源的分布式ID生成算法,旨在解决分布式系统中需要全局唯一、趋势递增、高性能的ID生成问题。它的输出是一个64位的long型整数,结构清晰,非常经典。
雪花算法的64位(bit)被划分为以下几个部分(从左到右,高位到低位):
| 位数 | 字段名 | 含义与作用 |
|---|---|---|
| 1位 | 符号位 | 固定为0。因为ID是正数,最高位为0。 |
| 41位 | 时间戳 | 记录当前时间与某个起始时间的差值(毫秒级)。可支持约69年。 |
| 10位 | 工作机器ID | 用于区分不同的节点/机器,最多支持1024个节点(2^10)。实际常拆分为5位数据中心ID + 5位机器ID。 |
| 12位 | 序列号 | 同一毫秒内生成的不同ID的序号,支持每毫秒每节点最多4096个ID(2^12)。 |
整体结构示意图:
0 | 41位时间戳(毫秒) | 10位机器ID | 12位序列号
示例(假设起始时间为2010-11-04 09:42:54.657):
1456192189189701632 的ID。由于时间戳在高位,生成的ID趋势上是递增的(但不严格连续,同毫秒内序列号连续)。
全局唯一,分布式友好
只需确保每个节点拥有唯一的机器ID,就可以独立生成ID,无需中心化协调。非常适合微服务、分库分表场景。
趋势递增,利于数据库索引
生成的ID在毫秒级时间上递增(同毫秒内序列号递增)。对数据库B+树索引非常友好,减少页分裂,写入性能高。
高性能
生成一个ID仅需几次位运算和内存操作,无网络IO、无锁竞争(单节点内常用atomic实现)。单机轻松达到数十万甚至百万级QPS。
灵活性
位数分配可按需调整。例如增加机器位或序列号位,适应不同规模集群。
无外部依赖
算法只依赖当前机器的时间戳,不依赖数据库、ZooKeeper、Redis等第三方服务,轻量可靠。
强依赖机器时钟
如果节点的系统时钟发生回拨(向前调整时间),那么有可能生成重复ID或导致新ID小于已生成的ID。
机器ID的分配与管理问题
每个节点需要独立的机器ID(10位)。如何安全、动态地分配这1024个ID而不冲突?
时间跨度有限
41位时间戳(毫秒级,起始点固定)可用约69年。如果服务持续运行超过69年,需要变更起始时间或扩展位数(通常业务系统远达不到这个时限)。
序列号容量限制
每毫秒每节点最多4096个ID。若瞬时并发超过这个量,可能产生少量等待(自旋到下一毫秒)。但在绝大多数场景下足够。
无法保证全局严格递增
“趋势递增”≠“单调递增”。不同节点由于时钟差异,可能会出现后生成的ID比先生成的ID数值小的情况(跨节点比较)。如果要求所有ID全局严格递增,雪花算法不满足。
单节点的redis并发能力有限,因此我们需要搭建主从集群,实现读写分离。一般是主节点进行写,从节点进行读。
主从同步:
高可用:
哨兵模式(sentinel):
脑裂:
redis实现的分布式锁:
因此我们使用第三方工具redisson: