知识卡片
"幽灵复现"现象:乱序确认带来的日志诡异重现,及StartWorking日志的解决方案
内容
Multi-Paxos允许多条日志乱序确认(不要求Leader必须按顺序拿到每条日志的多数派确认才能继续写后续日志),这个宽松性带来了一种被称为”幽灵复现”的诡异现象:第一轮A当选Leader,写下1~10号日志,其中1~5号已形成多数派并回复了客户端,6~10号客户端超时没等到应答;第二轮A宕机,B当选新Leader,B和C此时已知的最大LogID都是5,所以B判断不需要去重新确认6~10号日志,而是直接从6号开始写全新的日志内容——这时如果客户端来查询,是查不到上一轮6~10号日志的(它们看起来”消失”了);第二轮又写入了6~20号新日志,但只有6号和20号真正形成了多数派;第三轮A重新当选Leader,从多数派里查到的最大LogID是20,因此要对7~20号日志重新执行”重确认”,这其中恰好包含了A在第一轮写下、后来”消失”的7~10号日志——一旦这些日志重确认成功,如果客户端再来查询,会发现上次查不到的7~10号日志像幽灵一样重新出现了。这个现象的根源在于:日志的LogID是连续分配的,但”这个LogID到底对应哪一轮Leader写下的哪个具体内容”在乱序确认的机制下是可能发生变化的,同一个LogID位置在不同轮次里可能被不同的内容占据过。解决方案是引入一条StartWorking日志:新任Leader在完成日志重确认、开始写入全新Redo Log之前,先写出一条StartWorking日志,记录当前Leader的EpochID(可以直接用ProposalID的值),此后Leader写的每条日志都要携带当前的EpochID;回放时一旦经过了一条StartWorking日志,后续再遇到EpochID比它更小的日志就直接忽略——这样即使某条日志的内容曾经在乱序确认的过程中发生过变化,回放逻辑也能通过EpochID的比较,准确识别出哪个版本才是真正应该生效的最终内容,不会被”幽灵复现”的旧版本内容干扰。
结构图:
sequenceDiagram
participant A as Leader A(第1、3轮)
participant B as Leader B(第2轮)
participant Log as 日志状态(LogID 6~10)
Note over A: 第1轮:A写1~10号日志<br/>1~5号形成多数派并应答客户端<br/>6~10号超时未应答
A->>Log: 写入6~10号(未确认)
Note over B: 第2轮:A宕机,B当选<br/>B/C已知最大LogID=5
B->>Log: 判断无需重确认6~10<br/>直接覆盖写全新的6~20号
Note over Log: 客户端此时查不到<br/>原6~10号内容("消失")
Note over A: 第3轮:A重新当选<br/>多数派最大LogID=20
A->>Log: 对7~20号执行"重确认"
Note over Log: 原A写的7~10号内容<br/>竟被重新确认("幽灵复现")
Note over A: 解法:写StartWorking日志<br/>携带EpochID,回放时<br/>忽略EpochID更小的日志