🚨 序章:好心办坏事的高可用
前几天刚解决了单点故障的隐患,为了让系统达到 99.99% 的可用性(High Availability),我给 Scanner 做了升级:
-
主力:AWS AMB 节点(稳重,就在自家内网)。
-
备胎:Infura 和 Alchemy(外部公共节点集群,全球都有)。
我的逻辑是:"三个臭皮匠顶个诸葛亮。AMB 要是挂了,备胎立刻顶上,保证 Scanner 永远有数据抓。"
结果,早上 10 点,客服主管冲进来说:"Henry!有个用户只充了 10 ETH ,但咱们给人家加了 20 ETH!这钱是天上掉下来的吗?"
🎬 第一章:消失的唯一性
我第一时间查了数据库,看到了让我窒息的两行记录:
| 记录ID | 交易哈希 (TxHash) | 金额 | 区块哈希 (BlockHash) | 数据来源 | 时间 |
|---|---|---|---|---|---|
| #101 | 0xabc... |
+10 | 0x111... (宇宙A) | AWS AMB | 10:00:01 |
| #102 | 0xabc... |
+10 | 0x222... (宇宙B) | Infura | 10:00:03 |
同一笔交易,被记了两次账。 唯一的区别是,它们属于两个完全不同的区块哈希。
这意味着:在那个瞬间,我们的系统同时相信了两个"平行的世界"。
🕵️♂️ 第二章:原理复盘 ------ 村口的黑板报
为了搞懂为什么会有两个世界,我给团队画了一个**"区块链村"**的比喻。
1. P2P 网络(传播是有时差的)
区块链村没有广播站,消息全靠村民(节点)之间口口相传。
-
现象:村东头发生了什么事,传到村西头需要几秒钟。
-
后果 :在某一秒钟,东头的人以为"王五赢了",西头的人以为"孙七赢了"。这就是分叉(Fork)。
2. 节点的性格分析
-
AWS AMB(我们的老村长):
-
性格:稳重、死板。
-
行为:他一定要等全村大部分人都确认了(12 个确认数),才肯在自家的黑板上写字。写上去就是**"铁案"**,基本不会擦掉。
-
缺点:反应慢半拍。
-
-
Infura(消息灵通的八卦王):
-
性格:急躁、甚至有点"听风就是雨"。
-
行为 :他眼线多,只要听到一点风声(比如产生了一个新块),他立刻就会写在黑板上(打草稿)。
-
风险:如果过了一会儿发现消息是假的(分叉被回滚),他会立刻拿黑板擦擦掉,重写正确的。
-
3. 事故真相
我们的 Scanner(抄账员)犯了两个错误:
-
乱跑:先去了老村长家(AMB)抄了一笔确定的账(宇宙 A)。
-
轻信草稿 :紧接着又跑到了八卦王家(Infura),看到黑板上写了个不一样的区块(宇宙 B),以为是新消息,赶紧又抄了一遍。殊不知,那只是 Infura 打的草稿,还没来得及擦掉。
🛠️ 第三章:Henry 的三板斧修复方案
为了防止"假传圣旨",我进行了架构级重构:
1. 数据库层面的"死锁" (The Hard Stop)
我在 RDS 数据库里执行了一条死命令:
ALTER TABLE deposits ADD CONSTRAINT unique_tx UNIQUE (tx_hash);
-
逻辑 :不管外面有多少个平行宇宙,不管 Scanner 从哪个节点抄来了数据,同一个 TxHash 在数据库里只能存一次。
-
效果 :如果 Scanner 试图插入第二笔
0xabc...,数据库会直接报错,物理隔绝重复充值。
2. "主从锁定"策略 (Primary First)
-
旧逻辑:随机轮询(Round Robin)。谁有空问谁。
-
新逻辑 :独宠 AMB。
-
Scanner 永远只问 AMB。
-
只有当 AMB 彻底断气(Timeout > 1min),才允许去问 Infura。
-
Henry 语录 :"老村长的消息虽然慢,但是准。管钱的事,要准不要快。"
-
3. "回溯校验"机制 (Look-Back Check)
如果被迫切换到了 Infura,Scanner 必须执行一套**"对齐仪式"**:
-
动作 :不查最新块,先查上一个块(N-1)。
-
判定:问 Infura:"你眼里的 N-1 块哈希,跟老村长(AMB)之前记的一样吗?"
-
如果一样 -> 说明你们在一个频道,继续工作。
-
如果不一样 -> 说明 Infura 正在看"别的频道",立即停机,等待它同步回来。
-
📚 第四章:运维深度问答 (Q&A)
Q1: 为什么 Infura 的数据会不准?是它质量不好吗?
- Henry: 不是质量不好,是角色不同 。Infura 是为了给前端钱包(Metamask)提供极速体验的,所以它必须快,哪怕偶尔有分叉也没关系。但我们做的是后端入账,我们需要的是 Finality(最终确定性)。用跑车的速度去拉货,必然会翻车。
Q2: 为什么 AMB 需要"等很多个同步一样"才发给我们?
- Henry: AMB 节点通常只连接了有限的几个 Peers(邻居)。它内部有校验机制,会等待这些 Peers 达成共识后,才更新自己的区块头。这就相当于它是经过了"二次过滤"的数据,虽然慢,但过滤掉了大部分网络噪音。
Q3: 所谓的"草稿"和"封存"在技术上是什么?
-
Henry:
-
打草稿 = 只有一个块头(Block Header),状态是
Canonical但没经过足够多确认,随时可能变成Uncle Block(叔块/孤块)。 -
封存 = 区块后面连接了 12 个以上的新块,攻击成本极高,被全网回滚的概率接近 0。
-
💡 Henry 的日记结语
"第 7 天的教训告诉我:在分布式系统里,'一致性'是奢侈品。
以前我觉得高可用就是多接几根管子。今天我才知道,管子接多了,流进来的不一定是水,可能是洪水。
给数据库加上'唯一锁',并且学会区分'草稿'和'正文',是每个区块链运维必须修满的学分。"