Kafka 面试里问顺序消费,最容易答错成"Kafka 天然有序"。准确说法是:Kafka 只能保证单个分区内部有序,不能保证跨分区全局有序。
一句话概括:Kafka 的顺序性来自分区内递增 Offset;同一个消费者组里,一个分区同一时刻只会被一个消费者消费。如果业务要求同一订单、同一用户、同一会话有序,就要让同一业务 Key 的消息进入同一个分区。

#mermaid-svg-Auzxfwc4nRRCR7Ai{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Auzxfwc4nRRCR7Ai .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Auzxfwc4nRRCR7Ai .error-icon{fill:#552222;}#mermaid-svg-Auzxfwc4nRRCR7Ai .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Auzxfwc4nRRCR7Ai .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Auzxfwc4nRRCR7Ai .marker.cross{stroke:#333333;}#mermaid-svg-Auzxfwc4nRRCR7Ai svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Auzxfwc4nRRCR7Ai p{margin:0;}#mermaid-svg-Auzxfwc4nRRCR7Ai .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Auzxfwc4nRRCR7Ai .cluster-label text{fill:#333;}#mermaid-svg-Auzxfwc4nRRCR7Ai .cluster-label span{color:#333;}#mermaid-svg-Auzxfwc4nRRCR7Ai .cluster-label span p{background-color:transparent;}#mermaid-svg-Auzxfwc4nRRCR7Ai .label text,#mermaid-svg-Auzxfwc4nRRCR7Ai span{fill:#333;color:#333;}#mermaid-svg-Auzxfwc4nRRCR7Ai .node rect,#mermaid-svg-Auzxfwc4nRRCR7Ai .node circle,#mermaid-svg-Auzxfwc4nRRCR7Ai .node ellipse,#mermaid-svg-Auzxfwc4nRRCR7Ai .node polygon,#mermaid-svg-Auzxfwc4nRRCR7Ai .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Auzxfwc4nRRCR7Ai .rough-node .label text,#mermaid-svg-Auzxfwc4nRRCR7Ai .node .label text,#mermaid-svg-Auzxfwc4nRRCR7Ai .image-shape .label,#mermaid-svg-Auzxfwc4nRRCR7Ai .icon-shape .label{text-anchor:middle;}#mermaid-svg-Auzxfwc4nRRCR7Ai .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Auzxfwc4nRRCR7Ai .rough-node .label,#mermaid-svg-Auzxfwc4nRRCR7Ai .node .label,#mermaid-svg-Auzxfwc4nRRCR7Ai .image-shape .label,#mermaid-svg-Auzxfwc4nRRCR7Ai .icon-shape .label{text-align:center;}#mermaid-svg-Auzxfwc4nRRCR7Ai .node.clickable{cursor:pointer;}#mermaid-svg-Auzxfwc4nRRCR7Ai .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Auzxfwc4nRRCR7Ai .arrowheadPath{fill:#333333;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Auzxfwc4nRRCR7Ai .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Auzxfwc4nRRCR7Ai .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Auzxfwc4nRRCR7Ai .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Auzxfwc4nRRCR7Ai .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Auzxfwc4nRRCR7Ai .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Auzxfwc4nRRCR7Ai .cluster text{fill:#333;}#mermaid-svg-Auzxfwc4nRRCR7Ai .cluster span{color:#333;}#mermaid-svg-Auzxfwc4nRRCR7Ai div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Auzxfwc4nRRCR7Ai .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Auzxfwc4nRRCR7Ai rect.text{fill:none;stroke-width:0;}#mermaid-svg-Auzxfwc4nRRCR7Ai .icon-shape,#mermaid-svg-Auzxfwc4nRRCR7Ai .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Auzxfwc4nRRCR7Ai .icon-shape p,#mermaid-svg-Auzxfwc4nRRCR7Ai .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Auzxfwc4nRRCR7Ai .icon-shape .label rect,#mermaid-svg-Auzxfwc4nRRCR7Ai .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Auzxfwc4nRRCR7Ai .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Auzxfwc4nRRCR7Ai .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Auzxfwc4nRRCR7Ai :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} key=orderId
key=orderId
订单 1001 的消息
Partition 0
订单 2002 的消息
Partition 1
Consumer 1 按 Offset 顺序消费
Consumer 2 按 Offset 顺序消费
Kafka 的顺序边界
Kafka 中一个 Topic 可以拆成多个 Partition。每个 Partition 内部都有自己的 Offset,消息按追加顺序写入。
#mermaid-svg-xwQBUFYyI8Qt1xf3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .error-icon{fill:#552222;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .marker.cross{stroke:#333333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xwQBUFYyI8Qt1xf3 p{margin:0;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .cluster-label text{fill:#333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .cluster-label span{color:#333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .cluster-label span p{background-color:transparent;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .label text,#mermaid-svg-xwQBUFYyI8Qt1xf3 span{fill:#333;color:#333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .node rect,#mermaid-svg-xwQBUFYyI8Qt1xf3 .node circle,#mermaid-svg-xwQBUFYyI8Qt1xf3 .node ellipse,#mermaid-svg-xwQBUFYyI8Qt1xf3 .node polygon,#mermaid-svg-xwQBUFYyI8Qt1xf3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .rough-node .label text,#mermaid-svg-xwQBUFYyI8Qt1xf3 .node .label text,#mermaid-svg-xwQBUFYyI8Qt1xf3 .image-shape .label,#mermaid-svg-xwQBUFYyI8Qt1xf3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .rough-node .label,#mermaid-svg-xwQBUFYyI8Qt1xf3 .node .label,#mermaid-svg-xwQBUFYyI8Qt1xf3 .image-shape .label,#mermaid-svg-xwQBUFYyI8Qt1xf3 .icon-shape .label{text-align:center;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .node.clickable{cursor:pointer;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .arrowheadPath{fill:#333333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xwQBUFYyI8Qt1xf3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xwQBUFYyI8Qt1xf3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xwQBUFYyI8Qt1xf3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .cluster text{fill:#333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .cluster span{color:#333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xwQBUFYyI8Qt1xf3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .icon-shape,#mermaid-svg-xwQBUFYyI8Qt1xf3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .icon-shape p,#mermaid-svg-xwQBUFYyI8Qt1xf3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .icon-shape .label rect,#mermaid-svg-xwQBUFYyI8Qt1xf3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xwQBUFYyI8Qt1xf3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xwQBUFYyI8Qt1xf3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xwQBUFYyI8Qt1xf3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Topic: order-events
Partition 0
0,1,2,3,4
Partition 1
0,1,2,3,4
Partition 2
0,1,2,3,4
这里的关键是:Partition 0 的 Offset 0、1、2 有顺序;Partition 1 的 Offset 0、1、2 也有顺序。但 Partition 0 的消息和 Partition 1 的消息之间没有全局先后关系。
消费者组如何影响顺序
在同一个消费者组里,一个分区同一时刻只能分配给一个消费者。
#mermaid-svg-ZqvGbsoK9eyY5Gnh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .error-icon{fill:#552222;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .marker.cross{stroke:#333333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZqvGbsoK9eyY5Gnh p{margin:0;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .cluster-label text{fill:#333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .cluster-label span{color:#333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .cluster-label span p{background-color:transparent;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .label text,#mermaid-svg-ZqvGbsoK9eyY5Gnh span{fill:#333;color:#333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .node rect,#mermaid-svg-ZqvGbsoK9eyY5Gnh .node circle,#mermaid-svg-ZqvGbsoK9eyY5Gnh .node ellipse,#mermaid-svg-ZqvGbsoK9eyY5Gnh .node polygon,#mermaid-svg-ZqvGbsoK9eyY5Gnh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .rough-node .label text,#mermaid-svg-ZqvGbsoK9eyY5Gnh .node .label text,#mermaid-svg-ZqvGbsoK9eyY5Gnh .image-shape .label,#mermaid-svg-ZqvGbsoK9eyY5Gnh .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .rough-node .label,#mermaid-svg-ZqvGbsoK9eyY5Gnh .node .label,#mermaid-svg-ZqvGbsoK9eyY5Gnh .image-shape .label,#mermaid-svg-ZqvGbsoK9eyY5Gnh .icon-shape .label{text-align:center;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .node.clickable{cursor:pointer;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .arrowheadPath{fill:#333333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZqvGbsoK9eyY5Gnh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZqvGbsoK9eyY5Gnh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZqvGbsoK9eyY5Gnh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .cluster text{fill:#333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .cluster span{color:#333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZqvGbsoK9eyY5Gnh rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .icon-shape,#mermaid-svg-ZqvGbsoK9eyY5Gnh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .icon-shape p,#mermaid-svg-ZqvGbsoK9eyY5Gnh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .icon-shape .label rect,#mermaid-svg-ZqvGbsoK9eyY5Gnh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZqvGbsoK9eyY5Gnh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZqvGbsoK9eyY5Gnh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZqvGbsoK9eyY5Gnh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Partition 0
Consumer 1
Partition 1
Consumer 2
Partition 2
Consumer 3
这个规则保证了分区内不会被多个消费者并发乱序处理。但如果一个消费者负责多个分区,它只能保证各分区内部顺序,不能保证多个分区之间的业务顺序。
为什么跨分区不能保证顺序
假设充值和转账两个事件属于同一个账户,但被发到了不同分区:
Consumer 2 Consumer 1 Partition 1 Partition 0 Producer Consumer 2 Consumer 1 Partition 1 Partition 0 Producer #mermaid-svg-O1HdrwZw8kvOZ4vk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O1HdrwZw8kvOZ4vk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O1HdrwZw8kvOZ4vk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O1HdrwZw8kvOZ4vk .error-icon{fill:#552222;}#mermaid-svg-O1HdrwZw8kvOZ4vk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O1HdrwZw8kvOZ4vk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O1HdrwZw8kvOZ4vk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O1HdrwZw8kvOZ4vk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O1HdrwZw8kvOZ4vk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O1HdrwZw8kvOZ4vk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O1HdrwZw8kvOZ4vk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O1HdrwZw8kvOZ4vk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O1HdrwZw8kvOZ4vk .marker.cross{stroke:#333333;}#mermaid-svg-O1HdrwZw8kvOZ4vk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O1HdrwZw8kvOZ4vk p{margin:0;}#mermaid-svg-O1HdrwZw8kvOZ4vk .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O1HdrwZw8kvOZ4vk text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-O1HdrwZw8kvOZ4vk .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-O1HdrwZw8kvOZ4vk .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-O1HdrwZw8kvOZ4vk .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-O1HdrwZw8kvOZ4vk .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-O1HdrwZw8kvOZ4vk #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-O1HdrwZw8kvOZ4vk .sequenceNumber{fill:white;}#mermaid-svg-O1HdrwZw8kvOZ4vk #sequencenumber{fill:#333;}#mermaid-svg-O1HdrwZw8kvOZ4vk #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-O1HdrwZw8kvOZ4vk .messageText{fill:#333;stroke:none;}#mermaid-svg-O1HdrwZw8kvOZ4vk .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O1HdrwZw8kvOZ4vk .labelText,#mermaid-svg-O1HdrwZw8kvOZ4vk .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-O1HdrwZw8kvOZ4vk .loopText,#mermaid-svg-O1HdrwZw8kvOZ4vk .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-O1HdrwZw8kvOZ4vk .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-O1HdrwZw8kvOZ4vk .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-O1HdrwZw8kvOZ4vk .noteText,#mermaid-svg-O1HdrwZw8kvOZ4vk .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-O1HdrwZw8kvOZ4vk .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O1HdrwZw8kvOZ4vk .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O1HdrwZw8kvOZ4vk .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-O1HdrwZw8kvOZ4vk .actorPopupMenu{position:absolute;}#mermaid-svg-O1HdrwZw8kvOZ4vk .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-O1HdrwZw8kvOZ4vk .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-O1HdrwZw8kvOZ4vk .actor-man circle,#mermaid-svg-O1HdrwZw8kvOZ4vk line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-O1HdrwZw8kvOZ4vk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 账户A 充值 +100账户A 转账 -50先被消费后被消费
虽然生产时充值先发送,但两个分区被不同消费者处理,实际业务处理顺序可能变成先转账再充值。
顺序消费怎么做
常见方案有三种:
| 方案 | 做法 | 代价 |
|---|---|---|
| Topic 只建一个分区 | 所有消息都进同一个分区 | 全局有序,但吞吐最低 |
| 指定分区号 | 生产者明确指定 partition | 灵活,但业务要维护规则 |
| 使用相同业务 Key | 同一 Key 进入同一分区 | 最常用,保证局部有序 |
项目里最常见的是使用业务 Key,比如订单 ID、用户 ID、会话 ID。
java
// 同一个 orderId 的消息会按相同 key 路由到同一个分区,从而保证该订单维度有序。
ProducerRecord<String, String> record =
new ProducerRecord<>("order-events", orderId, messageBody);
如果要求"所有消息全局严格有序",只能使用一个分区。但这会牺牲 Kafka 最核心的并行能力,通常只适合吞吐不高但顺序要求极强的场景。
生产者重试也可能影响顺序
顺序问题不只发生在消费者端。生产者如果开启重试,同时同一个连接上允许多个请求并发在路上,也可能出现后发批次先写入成功、前发批次重试后才写入的情况。
Kafka Producer Kafka Producer #mermaid-svg-qhvg1hR7jBCRC5pC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qhvg1hR7jBCRC5pC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qhvg1hR7jBCRC5pC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qhvg1hR7jBCRC5pC .error-icon{fill:#552222;}#mermaid-svg-qhvg1hR7jBCRC5pC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qhvg1hR7jBCRC5pC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qhvg1hR7jBCRC5pC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qhvg1hR7jBCRC5pC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qhvg1hR7jBCRC5pC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qhvg1hR7jBCRC5pC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qhvg1hR7jBCRC5pC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qhvg1hR7jBCRC5pC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qhvg1hR7jBCRC5pC .marker.cross{stroke:#333333;}#mermaid-svg-qhvg1hR7jBCRC5pC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qhvg1hR7jBCRC5pC p{margin:0;}#mermaid-svg-qhvg1hR7jBCRC5pC .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qhvg1hR7jBCRC5pC text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-qhvg1hR7jBCRC5pC .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qhvg1hR7jBCRC5pC .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-qhvg1hR7jBCRC5pC .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-qhvg1hR7jBCRC5pC .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-qhvg1hR7jBCRC5pC #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-qhvg1hR7jBCRC5pC .sequenceNumber{fill:white;}#mermaid-svg-qhvg1hR7jBCRC5pC #sequencenumber{fill:#333;}#mermaid-svg-qhvg1hR7jBCRC5pC #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-qhvg1hR7jBCRC5pC .messageText{fill:#333;stroke:none;}#mermaid-svg-qhvg1hR7jBCRC5pC .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qhvg1hR7jBCRC5pC .labelText,#mermaid-svg-qhvg1hR7jBCRC5pC .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-qhvg1hR7jBCRC5pC .loopText,#mermaid-svg-qhvg1hR7jBCRC5pC .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-qhvg1hR7jBCRC5pC .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qhvg1hR7jBCRC5pC .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-qhvg1hR7jBCRC5pC .noteText,#mermaid-svg-qhvg1hR7jBCRC5pC .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-qhvg1hR7jBCRC5pC .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qhvg1hR7jBCRC5pC .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qhvg1hR7jBCRC5pC .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qhvg1hR7jBCRC5pC .actorPopupMenu{position:absolute;}#mermaid-svg-qhvg1hR7jBCRC5pC .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-qhvg1hR7jBCRC5pC .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qhvg1hR7jBCRC5pC .actor-man circle,#mermaid-svg-qhvg1hR7jBCRC5pC line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-qhvg1hR7jBCRC5pC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分区内顺序可能变成 B 在 A 前面 批次 A 发送失败批次 B 发送成功批次 A 重试成功
新版本客户端开启生产者幂等后,可以在允许范围内保持单分区顺序;如果没有开启幂等,就要特别小心 retries 和 max.in.flight.requests.per.connection 的组合。
消费端也可能破坏顺序
即使同一业务 Key 进入同一分区,消费端也不能随便开线程池乱跑。
#mermaid-svg-GBGKRh3sJnAtEBKt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GBGKRh3sJnAtEBKt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GBGKRh3sJnAtEBKt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GBGKRh3sJnAtEBKt .error-icon{fill:#552222;}#mermaid-svg-GBGKRh3sJnAtEBKt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GBGKRh3sJnAtEBKt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GBGKRh3sJnAtEBKt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GBGKRh3sJnAtEBKt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GBGKRh3sJnAtEBKt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GBGKRh3sJnAtEBKt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GBGKRh3sJnAtEBKt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GBGKRh3sJnAtEBKt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GBGKRh3sJnAtEBKt .marker.cross{stroke:#333333;}#mermaid-svg-GBGKRh3sJnAtEBKt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GBGKRh3sJnAtEBKt p{margin:0;}#mermaid-svg-GBGKRh3sJnAtEBKt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GBGKRh3sJnAtEBKt .cluster-label text{fill:#333;}#mermaid-svg-GBGKRh3sJnAtEBKt .cluster-label span{color:#333;}#mermaid-svg-GBGKRh3sJnAtEBKt .cluster-label span p{background-color:transparent;}#mermaid-svg-GBGKRh3sJnAtEBKt .label text,#mermaid-svg-GBGKRh3sJnAtEBKt span{fill:#333;color:#333;}#mermaid-svg-GBGKRh3sJnAtEBKt .node rect,#mermaid-svg-GBGKRh3sJnAtEBKt .node circle,#mermaid-svg-GBGKRh3sJnAtEBKt .node ellipse,#mermaid-svg-GBGKRh3sJnAtEBKt .node polygon,#mermaid-svg-GBGKRh3sJnAtEBKt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GBGKRh3sJnAtEBKt .rough-node .label text,#mermaid-svg-GBGKRh3sJnAtEBKt .node .label text,#mermaid-svg-GBGKRh3sJnAtEBKt .image-shape .label,#mermaid-svg-GBGKRh3sJnAtEBKt .icon-shape .label{text-anchor:middle;}#mermaid-svg-GBGKRh3sJnAtEBKt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GBGKRh3sJnAtEBKt .rough-node .label,#mermaid-svg-GBGKRh3sJnAtEBKt .node .label,#mermaid-svg-GBGKRh3sJnAtEBKt .image-shape .label,#mermaid-svg-GBGKRh3sJnAtEBKt .icon-shape .label{text-align:center;}#mermaid-svg-GBGKRh3sJnAtEBKt .node.clickable{cursor:pointer;}#mermaid-svg-GBGKRh3sJnAtEBKt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GBGKRh3sJnAtEBKt .arrowheadPath{fill:#333333;}#mermaid-svg-GBGKRh3sJnAtEBKt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GBGKRh3sJnAtEBKt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GBGKRh3sJnAtEBKt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GBGKRh3sJnAtEBKt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GBGKRh3sJnAtEBKt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GBGKRh3sJnAtEBKt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GBGKRh3sJnAtEBKt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GBGKRh3sJnAtEBKt .cluster text{fill:#333;}#mermaid-svg-GBGKRh3sJnAtEBKt .cluster span{color:#333;}#mermaid-svg-GBGKRh3sJnAtEBKt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GBGKRh3sJnAtEBKt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GBGKRh3sJnAtEBKt rect.text{fill:none;stroke-width:0;}#mermaid-svg-GBGKRh3sJnAtEBKt .icon-shape,#mermaid-svg-GBGKRh3sJnAtEBKt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GBGKRh3sJnAtEBKt .icon-shape p,#mermaid-svg-GBGKRh3sJnAtEBKt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GBGKRh3sJnAtEBKt .icon-shape .label rect,#mermaid-svg-GBGKRh3sJnAtEBKt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GBGKRh3sJnAtEBKt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GBGKRh3sJnAtEBKt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GBGKRh3sJnAtEBKt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 同一分区消息 1,2,3
Consumer 拉取
线程池并发处理
消息 2 先处理完成
消息 1 后处理完成
业务结果乱序
如果业务强依赖顺序,消费端要么单线程处理该分区,要么按业务 Key 做串行队列,确保同一个 Key 的任务按顺序执行。
一种常见落地方式是:消费者拉取消息后,按业务 Key 路由到固定的本地执行队列。同一个 Key 永远进入同一个队列,不同 Key 可以并行。
#mermaid-svg-ajtR4qkgT42qIpSt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ajtR4qkgT42qIpSt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ajtR4qkgT42qIpSt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ajtR4qkgT42qIpSt .error-icon{fill:#552222;}#mermaid-svg-ajtR4qkgT42qIpSt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ajtR4qkgT42qIpSt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ajtR4qkgT42qIpSt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ajtR4qkgT42qIpSt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ajtR4qkgT42qIpSt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ajtR4qkgT42qIpSt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ajtR4qkgT42qIpSt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ajtR4qkgT42qIpSt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ajtR4qkgT42qIpSt .marker.cross{stroke:#333333;}#mermaid-svg-ajtR4qkgT42qIpSt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ajtR4qkgT42qIpSt p{margin:0;}#mermaid-svg-ajtR4qkgT42qIpSt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ajtR4qkgT42qIpSt .cluster-label text{fill:#333;}#mermaid-svg-ajtR4qkgT42qIpSt .cluster-label span{color:#333;}#mermaid-svg-ajtR4qkgT42qIpSt .cluster-label span p{background-color:transparent;}#mermaid-svg-ajtR4qkgT42qIpSt .label text,#mermaid-svg-ajtR4qkgT42qIpSt span{fill:#333;color:#333;}#mermaid-svg-ajtR4qkgT42qIpSt .node rect,#mermaid-svg-ajtR4qkgT42qIpSt .node circle,#mermaid-svg-ajtR4qkgT42qIpSt .node ellipse,#mermaid-svg-ajtR4qkgT42qIpSt .node polygon,#mermaid-svg-ajtR4qkgT42qIpSt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ajtR4qkgT42qIpSt .rough-node .label text,#mermaid-svg-ajtR4qkgT42qIpSt .node .label text,#mermaid-svg-ajtR4qkgT42qIpSt .image-shape .label,#mermaid-svg-ajtR4qkgT42qIpSt .icon-shape .label{text-anchor:middle;}#mermaid-svg-ajtR4qkgT42qIpSt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ajtR4qkgT42qIpSt .rough-node .label,#mermaid-svg-ajtR4qkgT42qIpSt .node .label,#mermaid-svg-ajtR4qkgT42qIpSt .image-shape .label,#mermaid-svg-ajtR4qkgT42qIpSt .icon-shape .label{text-align:center;}#mermaid-svg-ajtR4qkgT42qIpSt .node.clickable{cursor:pointer;}#mermaid-svg-ajtR4qkgT42qIpSt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ajtR4qkgT42qIpSt .arrowheadPath{fill:#333333;}#mermaid-svg-ajtR4qkgT42qIpSt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ajtR4qkgT42qIpSt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ajtR4qkgT42qIpSt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ajtR4qkgT42qIpSt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ajtR4qkgT42qIpSt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ajtR4qkgT42qIpSt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ajtR4qkgT42qIpSt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ajtR4qkgT42qIpSt .cluster text{fill:#333;}#mermaid-svg-ajtR4qkgT42qIpSt .cluster span{color:#333;}#mermaid-svg-ajtR4qkgT42qIpSt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ajtR4qkgT42qIpSt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ajtR4qkgT42qIpSt rect.text{fill:none;stroke-width:0;}#mermaid-svg-ajtR4qkgT42qIpSt .icon-shape,#mermaid-svg-ajtR4qkgT42qIpSt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ajtR4qkgT42qIpSt .icon-shape p,#mermaid-svg-ajtR4qkgT42qIpSt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ajtR4qkgT42qIpSt .icon-shape .label rect,#mermaid-svg-ajtR4qkgT42qIpSt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ajtR4qkgT42qIpSt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ajtR4qkgT42qIpSt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ajtR4qkgT42qIpSt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} key A
key B
key C
Consumer 拉取消息
按业务 Key hash
本地队列 1 串行执行
本地队列 2 串行执行
本地队列 3 串行执行
面试回答模板
可以这样答:
Kafka 不是全局有序,而是分区内有序。每个 Topic 可以有多个分区,每个分区内部消息按 Offset 递增,同一个消费者组里一个分区同一时刻只会被一个消费者消费,所以分区内可以顺序处理。但不同分区之间没有顺序保证。如果业务要求顺序,比如同一个订单的创建、支付、发货事件必须按顺序处理,就要在发送消息时使用相同的业务 Key,比如 orderId,让同一个订单的消息进入同一个分区。生产者侧还要注意重试和并发请求可能带来的乱序,通常配合生产者幂等来保证单分区顺序。如果要求全局顺序,只能设置一个分区,但吞吐会下降。另外消费端也不能随便用线程池打乱同一 Key 的处理顺序,可以按业务 Key 路由到本地串行队列。
小结
Kafka 顺序消费要抓住一个边界:
#mermaid-svg-Om5BDiP54LYpicbD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Om5BDiP54LYpicbD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Om5BDiP54LYpicbD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Om5BDiP54LYpicbD .error-icon{fill:#552222;}#mermaid-svg-Om5BDiP54LYpicbD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Om5BDiP54LYpicbD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Om5BDiP54LYpicbD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Om5BDiP54LYpicbD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Om5BDiP54LYpicbD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Om5BDiP54LYpicbD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Om5BDiP54LYpicbD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Om5BDiP54LYpicbD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Om5BDiP54LYpicbD .marker.cross{stroke:#333333;}#mermaid-svg-Om5BDiP54LYpicbD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Om5BDiP54LYpicbD p{margin:0;}#mermaid-svg-Om5BDiP54LYpicbD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Om5BDiP54LYpicbD .cluster-label text{fill:#333;}#mermaid-svg-Om5BDiP54LYpicbD .cluster-label span{color:#333;}#mermaid-svg-Om5BDiP54LYpicbD .cluster-label span p{background-color:transparent;}#mermaid-svg-Om5BDiP54LYpicbD .label text,#mermaid-svg-Om5BDiP54LYpicbD span{fill:#333;color:#333;}#mermaid-svg-Om5BDiP54LYpicbD .node rect,#mermaid-svg-Om5BDiP54LYpicbD .node circle,#mermaid-svg-Om5BDiP54LYpicbD .node ellipse,#mermaid-svg-Om5BDiP54LYpicbD .node polygon,#mermaid-svg-Om5BDiP54LYpicbD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Om5BDiP54LYpicbD .rough-node .label text,#mermaid-svg-Om5BDiP54LYpicbD .node .label text,#mermaid-svg-Om5BDiP54LYpicbD .image-shape .label,#mermaid-svg-Om5BDiP54LYpicbD .icon-shape .label{text-anchor:middle;}#mermaid-svg-Om5BDiP54LYpicbD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Om5BDiP54LYpicbD .rough-node .label,#mermaid-svg-Om5BDiP54LYpicbD .node .label,#mermaid-svg-Om5BDiP54LYpicbD .image-shape .label,#mermaid-svg-Om5BDiP54LYpicbD .icon-shape .label{text-align:center;}#mermaid-svg-Om5BDiP54LYpicbD .node.clickable{cursor:pointer;}#mermaid-svg-Om5BDiP54LYpicbD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Om5BDiP54LYpicbD .arrowheadPath{fill:#333333;}#mermaid-svg-Om5BDiP54LYpicbD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Om5BDiP54LYpicbD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Om5BDiP54LYpicbD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Om5BDiP54LYpicbD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Om5BDiP54LYpicbD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Om5BDiP54LYpicbD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Om5BDiP54LYpicbD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Om5BDiP54LYpicbD .cluster text{fill:#333;}#mermaid-svg-Om5BDiP54LYpicbD .cluster span{color:#333;}#mermaid-svg-Om5BDiP54LYpicbD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Om5BDiP54LYpicbD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Om5BDiP54LYpicbD rect.text{fill:none;stroke-width:0;}#mermaid-svg-Om5BDiP54LYpicbD .icon-shape,#mermaid-svg-Om5BDiP54LYpicbD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Om5BDiP54LYpicbD .icon-shape p,#mermaid-svg-Om5BDiP54LYpicbD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Om5BDiP54LYpicbD .icon-shape .label rect,#mermaid-svg-Om5BDiP54LYpicbD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Om5BDiP54LYpicbD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Om5BDiP54LYpicbD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Om5BDiP54LYpicbD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 单分区
有序
跨分区
无全局顺序
同业务 Key
进入同分区,局部有序
面试里能讲出"全局有序"和"业务 Key 局部有序"的取舍,就已经比只背"Kafka 分区内有序"更完整。