值得信赖的区块链资讯!
以太坊2.0的致命缺陷
继比特币减半以后,币圈又有一个大事件在酝酿,那就是以太坊要升级了。在开始聊这个以太坊2.0之前,我先来问一个问题。
假如一家银行一共有10000元的储备金,在A和B城市分别有一个独立的ATM机。一个人在A城市用ATM取5000元,但同时另外一个人在B城市也取5000元。请问现在银行里还剩下多少钱?

这是一道很简单的数学题。我相信所有人都能给出正确的答案。银行总共就10000块钱,A和B分别取了5000元,所以加起来一共是取走了1万元整。10000-10000=0。所以银行还剩下0元。
这道题对我们来说很简单,但是对计算机来说却没有那么容易。比如我们要编写一个应用程序来实现上述功能。它的难点就在于,我们如何保证用户在提款过程中,银行的数据是实时同步的。因为如果不同步的话,那当B在操作ATM的时候,他读取到的储备金额可能还是10000,而没有扣掉A提走的5000。这样一来显然就乱套了!我用一段Java程序来模拟一下,你就明白了。
我们先定义一个简单的类,名字就叫做Bank。其中自定义变量只有一个就是balance,用来指代账户的余额。操作函数只有两个,分别是提款withdraw,和查询余额getBalance。
public class Bank
{
//银行余额
private int balance;
public Bank(int balance){
this.balance = balance;
}
//用户提款
public void withdraw (int value) {
try {
Thread.sleep(300); //0.3秒的模拟时延
} catch (InterruptedException e) {
e.printStackTrace();
}
this.balance -= value;
}
//查询当前余额
public int getBalance(){
return this.balance;
}
}
接下来就是用来演示的主程序:
public class Demo {
public static void main(String args[]) throws InterruptedException {
Bank bank = new Bank(10000); //银行的初始余额
Runnable Atm1 = () -> {
bank.withdraw(5000);
System.out.println("A 提款 5000");
};
Runnable Atm2 = () -> {
bank.withdraw(5000);
System.out.println("B 提款 5000");
};
Thread A = new Thread(Atm1); //提款人A的操作线程
Thread B = new Thread(Atm2);//提款人B的操作线程
A.start();//A开始提款
B.start();//B开始提款
A.join();//等待A操作结束
B.join();//等待B操作结束
//显示余额
System.out.println("银行余额:"+bank.getBalance());
}
}
在这段程序里,我们设定了初始余额是10000。然后我们模拟了A和B两个操作线程。两者几乎在同一时间进行提款操作,都取了5000块。我们来看看该程序的运行结果:

但问题是B提了5000以后,银行已没有任何结余,所以应该显示0。但这里仍是5000。这就是问题所在!有趣的是,你如果重复运行这个程序,会发现每一次的结果可能是不一样的。有时候显示0,有时候显示5000。这种现象在计算机里有个典型的名词叫竞态条件(race condition)。指的就是有多个计算机线程在争夺同一个资源,造成数据更新的紊乱。像我们这个情况,A和B通过不同的ATM,开启了两个提款操作线程。这两个线程都要对银行余额进行修改。这种情况下,B相当于抢夺了A的数据更改权,导致刚更新的数据立马被B覆盖掉了。
那为什么会发生这种情况呢?这是由于计算机CPU的特殊架构所决定的。计算机的任何一条指令都需要知道它的操作对象是谁,值是多少,否则这条指令就没有意义。那这个操作对象去哪里找?CPU里有一个叫做寄存器的元件专门负责存储这个信息。任何一条指令都需要访问这个寄存器,获取它操作对象的值,指令才能完整地执行。比如下图中的AX就是CPU的一个寄存器。里面可存一个16位的二进制数。

就拿我们这个例子来说,withdraw就是提款的指令,而它的操作对象就是银行余额balance。那这个balance的值是多少呢?这就要到寄存器里去找。
获取了这个值以后,指令就开始执行了。在此过程中,它会对寄存器的内容进行修改,完成数据的更新。所以我们的银行余额就是这样被更新的。这时候如果有新的指令进来获取当前的余额,我们再回到那个寄存器里找答案就行了。
但问题是我们电脑在单位时间内不单单执行一条指令。在很多情况下,是多条指令同时运行的。不然你怎么可以做到边听音乐边上网呢?所以为了实现“并行操作”,我们的CPU引入了多线程管理的机制。就是把这些指令封装在不同的线程里,通过合理地调度,来并行地运作多个程序。就比如我们可以用一个线程来执行提款(withdraw)的指令,同时可以再分配一个线程来查询当前的余额(getBalance),如下图所示:

查询操作并不影响寄存器的状态,所以两条线程可以相安无事,但如果此时再引入第三个线程也来进行提款操作,那事情就会变得很棘手了。因为它可能会和线程1抢夺同一个寄存器的资源。如下图所示,线程1和3在同一时刻对寄存器的状态进行更新,那很有可能当线程3执行的时候,线程1还没有来得及更新balance的值,所以它读到的值还是更新之前的,即balance=10000。而当线程1运行完以后,虽然将balance更新至5000,但这已于事无补,因为线程3已经在操作了。所以线程3对寄存器的更新还是基于原来的旧值:10000,导致最终的余额仍旧是5000。 (10000-5000=5000)

所以为了避免这种情况,我们必须要保证这个寄存器的状态在多线程运行中是同步的。虽然它们都是共享同一块数据资源,但是必须要有一个先来后到。就拿上述情况来说,我们必须要保证线程1操作寄存器的时候,其他线程无法访问。只有当线程1结束以后,线程3才可以进行操作。这样一来,每条线程所读取的寄存器数据就是同步的。
为了实现多线程之间的同步,我们的CPU引入了一个“保护锁”(lock)的机制。就是针对这种共享的寄存器资源,标记一个“锁存”状态。任何一个线程在访问寄存器的时候,都可以给它“上锁”。这样一来其他线程就无法访问,只能乖乖地等待。只有当前线程执行完毕以后,这个“保护锁”才会被释放。然后其余的线程就会被自动唤醒,开始访问这个寄存器资源。像我们这个例子就可以让线程1在访问寄存器的时候“上锁”,那线程3就会被迫等待。等线程1执行完毕以后,balance就会更新为10000-5000=5000。然后寄存器释放保护锁,线程3被唤醒,开始访问balance这个变量。类似地,它再套上一个保护锁。这时候它获取的数值就是5000。等它执行完毕以后,balance就会更新至5000-5000=0。如下所示:

对应的,我们的Java源码只要做如下修改,就能实现这套“保护锁”的机制:
public class Bank
{
//银行余额
private int balance;
private final ReentrantLock lock = new ReentrantLock();
…….
//用户提款
public void withdraw (int value) {
lock.lock(); //加上保护锁
try {
Thread.sleep(300); //0.3秒的模拟时延
} catch (InterruptedException e) {
e.printStackTrace();
}
this.balance -= value;
lock.unlock(); //释放保护锁
}
}
运行结果如下:

根据这次的运行结果,你可以看到我们的银行余额在A和B两次提款之后,已经正确地更新至0。
所以我们可以看到,虽然每条线程都是独立的,但是整个线程的调度是中心化的。CPU就好比是一个大脑。它得给不同的线程进行合理的资源分配,安排执行的先后顺序,这样才能保证数据的同步。所以这个大脑必须得知道哪些寄存器上了锁,哪些线程在进行访问,哪些线程在等待。也就说它具备一个“上帝视角”可以实时监测每一条线程,以及每一个寄存器的状态。
单个电脑的程序运行是这样的,但如果我们往大了说,多台电脑的节点部署也是这样的。就拿淘宝网站来说,它在双11那天得处理几千万条交易请求,所以一个服务器肯定是不够的。它肯定得部署多个服务器节点,然后通过负载均衡器(Load Balancer),把这些请求均匀地分配至每个服务器上。如下图所示:

但无论有多少个服务器节点,无论有多大数量的请求,最终它们访问的是同一个数据库!这点非常重要。因为只有这样,你才可以引入“保护锁”的机制,在交易的过程中给对应数据库表单加锁,保证读写的同步。比如说现在一个天猫店有10个香奈儿的包,打8折,100个人抢购,当第一个人下单了已经开始交易了,那数据库就必须要把其他的购买请求放在等待队列里。这样才能保证下一个人看到的是9个包而不是开始的10个。
所以淘宝网的节点部署,虽是分布式架构,但是本质上是中心化的。这种中心化体现在节点线程的监控与调度,以及数据库的解决方案上。也就是说淘宝服务器的背后有一个控制中心,它可以实时地检测每个节点的状态,并且这些节点访问的是同一个数据库。正是这样的中心化架构,才可以让那么多节点并行处理这么多请求,同时还能保证数据的同步。
但是公有链的分布式架构就完全不同了,因为它本质上是去中心化的,每个节点各自为战。所以它没有一个控制面板,掌控所有节点的状态。其次它没有一个中心化的数据库让这些节点去访问,而是每个节点都独立配置一个自己的数据库。所以就会有多个账本同时存在,我们只能引入投票机制来确定一个最终账本,间接地实现节点间的同步。比特币是通过算力来投票,选出最长的那条区块链作为最终账本。虽然不同的节点会产生多个区块链账本,引发拜占庭将军问题,但比特币的算法是行得通的。因为它是单链结构,并且在单位时间内只能产生一个区块。虽然同时间可以有不同的节点播报区块,但是比特币的挖矿机制保证了这个区块的唯一性。所以比特币本质上是一个单线程的数据库读写操作。
以太坊本来也没有问题,因为它和比特币一样也是单链结构,使用POW共识。但是升级到2.0以后问题就很大了。因为以太坊2.0它引入一个叫“分片(sharding)”的机制。简单的来说就是借鉴淘宝网的这种负载均衡器(Load Balancer)的机制——设置多个节点,批量处理不同的请求。比如说现在有10000个交易请求,我让A节点处理5000个,B节点处理余下的5000个,那这样一来速度不就快了嘛。我承认这个初衷是好的,但是实际上是行不通的。根据以太坊2.0的介绍,它首先引入了一个主链叫(Beacon),这个主链负责记录所有交易的状态,相当于账本的核心。然后它把整个节点网络划分成不同区域,每个区域作为一个分片,相当于Load Balancer。每个分片都处理不同的交易请求,最终分别记录在主链上。如下图所示:

说到这里你可能会有点迷,但是我换一种方式来解释你就明白了。只要看了我前面的介绍,你就应该会对多线程同步有一个简单的认识。以太坊2.0也是类似的架构,你可以把Beacon链理解为中央数据库,每一个分片相当于一个独立的线程。每个线程播报的区块都是不一样的,比如说分片1的区块所包含的交易序列是1到3000,那分片2就是3000到6000。所以以太坊2.0相当于一个多线程的数据库读写操作。这是和比特币本质上的不同。
如果多个线程对同一个数据库进行操作,容易出现数据不同步的问题,所以正确的做法就是在每个线程执行的过程中,给这个数据库加上一个保护锁,从而避免其他线程同时访问。所以对于我们这个情况也是一样的,就是当每个分片对Beacon链进行更新的时候,必须要给这条主链加上一个“保护锁”,从而迫使其他的分片进入等待队列。V神确实也考虑到了这点,准备引入这个“保护锁”的机制。但错就错在,这个Beacon链不是唯一的中央数据库。
我们要知道以太坊是公链,公链是去中心化的!所以每一个挖矿节点都有自己的一条Beacon链。所以这里的“上锁”,是加在自己那条Beacon链上的锁。这个锁存的状态显然没有和其他节点同步,所以其余的分片节点仍旧会继续访问主链。这个时候,不同的分片之间就会产生我之前说的竞争状态(race condition)。分片1的更新有可能就会被分片2给覆盖掉。如下图所示:

由此可见,去中心化的架构中的保护锁,无疑是形同虚设。而且以太坊2.0还允许分片与分片之间的读写操作,那又会暴露同样的多线程同步问题。
那可能有人会问,我们能不能把这个Beacon链的锁存状态,同步到其他分片中呢?这里又涉及到一个投票问题了。因为每个分片节点如果从自身角度出发,它所看到的主链状态是不一样。比如上图的节点1它给它自己记录的主链上了锁,但是节点2却不这么认为。因为它并没有看到这个锁。所以节点1认为有锁,节点2认为没有锁。拜占庭将军问题再次出现,所以只能投票决定。但是以太坊2.0使用的是POS,已经不是POW了,所以你选择最长链的共识没有意义。因为区块的生产没有成本,只要拿到记账权一次性就能播报多个区块,所以最长的那条链无法代表最多的共识。这时候票数的统计就会变得更加复杂。即使共识算法可以进行正确的票数统计,认定节点1获胜,那与此同时就意味着分片2的区块就被舍弃掉了。此时分片的意义又何在?你如果想保留分片2的区块,那就必须把这条线程放在等待队列里。可问题是你没有一个控制面板一样的东西,能够全局调配不同线程的资源,你连每个线程状态都不知道啊。所以这是不是又要回到中心化的老路?
根据以上分析,我可以断定当以太坊2.0上线以后,势必会出现大量的数据不同步问题。不仅分片之间不同步,各个节点的Beacon链也会不同步。公链和淘宝不一样,淘宝如果出现了数据不同步问题,我顶多修改下数据库,或者重启一下服务器就好了。但是公链上的不同步就会引起矿工阵营的撕裂,会引起分叉,这就是一个很严重的问题了。
来源:北美区块君
比推快讯
更多 >>- 昨日美国比特币 ETF 净流出 1488 枚 BTC,以太坊 ETF 净流出 62184 枚 ETH
- James Wynn 从 Hyperliquid 领取 5565 美元奖励后开设 40 倍杠杆 BTC 空单
- Strategy CEO:摩根士丹利若将 2%资管规模配置 BTC 或给市场带来巨额买盘
- 分析:比特币跌破 6.6 万美元或引发 10%-20%深度下跌
- Michael Saylor:STRC 年化收益率 11.5%,波动率仅 1.7%
- Dan Romero 澄清 MPP 五大误区,并称已提交 IETF Web 标准提案
- 白宫称特朗普没有向伊朗派兵的计划
- Polymarket 上押注 Backpack 上线首日 FDV 超过 3 亿美元的概率降至 58%
- a16z 联创:OpenClaw 和 Pi Coding Agent 是有史以来十大软件突破之一
- Erik Voorhees 关联地址在链上买入 5805.51 枚 ETH,ETH 持仓已接近 11 万枚
- 伊朗最高领袖就伊朗新年发表书面贺词
- 瑞银上调 2026 和 2027 年油价预测,分别升至 86 美元/桶和 80 美元/桶
- 特朗普:想和伊朗谈判,但没有谈判对象
- 分析:比特币或接近阶段性底部,市场关注 RSI 关键信号
- 美国向伊朗增兵引发市场恐慌,金银、美股快速下跌,比特币跌破 7 万美元大关
- Twenty One Capital 向 Bitfinex 转入 392.19 枚 BTC
- 某神秘鲸鱼再次抄底 3618 枚 ETH,目前持有超 10.7 万枚 ETH
- 美 CFTC 主席:已发布加密资产与区块链业务 FAQ,强化监管规则一致性
- Wirex 企业账户集成 Morpho 金库,为其美元或欧元资金提供收益支持
- Binance 将下架 APTUSD 及 OPUSD 币本位永续合约
- 某鲸鱼开设 20 倍杠杆以太坊空单,现仓位价值达 2144 万美元
- 某鲸鱼近 3 小时 20 倍做空 ETH,现持仓价值达 2144 万美元
- 市场预计美联储很可能在 12 月加息
- 现货金银下跌,白银失守 70 美元
- 交易员预计到十月美联储加息的概率为 50%
- 伊朗战争加剧美联储分歧:沃勒转向谨慎观望,鲍曼坚持年内三次降息
- 高盛平台多头投资者周四出现 2022 年以来最大抛售潮
- 美国 10 年期国债收益率升至 4.33%
- 美国向中东增派三艘军舰及数千名海军陆战队员
- 特朗普批北约“懦夫”:不愿协助开海峡,转头怨高油价
- 过去 24 小时,没有装载原油的油轮通过霍尔木兹海峡
- 特朗普:没有美国的领导,北约就是纸老虎
- Based:代币空投将在 TGE 当天接分发到用户 Hyperliquid 钱包
- 美股开盘普跌,加密概念股 BMNR 跌超 2%
- SIGN 推出 1 亿枚代币橙色全民基本收入(OBI)计划
- 两关联地址今日从 Binance 提取价值 300 万美元 SIGN 代币
- 国际能源署警告:海湾地区油气供应恢复可能需要六个月
- 灰度向 Coinbase 转入总计 3979 枚 ETH,价值 852 万美元
- 美联储理事沃勒:因油价上涨“紧急撤回”降息票
- BitFuFu 2025 年财报:总营收 4.76 亿美元,录得净亏损 5740 万美元
- 财新:蚂蚁国际有意单独赴港上市,数字支付和金融服务平台已引入区块链和 AI 技术
- 数据:Polymarket 日活用户超 15 万人,创历史新高
- 美联储理事沃勒:若储备需求下降,可讨论缩表
- 美联储理事沃勒:若形势发展顺利将主张降息
- Spark Q1 协议总收入 3300 万美元,同比增长 10%
- 美联储理事沃勒:希望先观察再决定今年晚些时候是否降息
- USDCNH 突破 6.895 USD,24H 涨幅 0.27%
- 瑞银预测标普 500 指数将飙升至 7700 点
- 美联储理事沃勒:若油价持续数月居高不下,迟早会传导至核心通胀
- Ju.com 第 8 期 Meme 打新项目 SMOKE 上线涨幅超 340 亿倍,第 9 期 APEACE 3 月 21 日 16:00 开启认购
比推专栏
更多 >>观点
比推热门文章
- 昨日美国比特币 ETF 净流出 1488 枚 BTC,以太坊 ETF 净流出 62184 枚 ETH
- James Wynn 从 Hyperliquid 领取 5565 美元奖励后开设 40 倍杠杆 BTC 空单
- Strategy CEO:摩根士丹利若将 2%资管规模配置 BTC 或给市场带来巨额买盘
- 分析:比特币跌破 6.6 万美元或引发 10%-20%深度下跌
- Michael Saylor:STRC 年化收益率 11.5%,波动率仅 1.7%
- Dan Romero 澄清 MPP 五大误区,并称已提交 IETF Web 标准提案
- 白宫称特朗普没有向伊朗派兵的计划
- Polymarket 上押注 Backpack 上线首日 FDV 超过 3 亿美元的概率降至 58%
- a16z 联创:OpenClaw 和 Pi Coding Agent 是有史以来十大软件突破之一
- Erik Voorhees 关联地址在链上买入 5805.51 枚 ETH,ETH 持仓已接近 11 万枚
比推 APP



