节点要块的节奏:比特币下载调度里的在途窗口与卡住的来源 图 1
节点要块的节奏:比特币下载调度里的在途窗口与卡住的来源 · 图 1

下载一张图,而不是一个队列

很多人想象比特币节点同步是”要一块、来一块、再要下一块”的传送带。真实模型更像调度器:节点维护着一条按高度排好序的下载清单,然后同时向多个邻居派发任务,每个邻居手里都开着一个小额度的”在途窗口”——发给它但还没回来的请求名额。比特币核心源码里,每个对端的在途区块数默认上限是 16 个(常量 MAX_BLOCKS_IN_TRANSIT_PER_PEER),窗口用满就先不给这个邻居派新活。区块回来的顺序取决于各家带宽与网络路径,落盘顺序因此是乱的;但校验必须严格按高度推进,因为每块的正确性定义在前一块结束时的账本状态上。

活是怎么派的

调度器每次决定”下一个向谁要哪个块”时,依次权衡几件事。第一是清单优先级:总是从下载清单里挑高度最低且尚未持有的块,保证链尽量不留洞。第二是邻居状态:该对端在途窗口没满、当前没被判定为危险(日志里的”staller”检测会盯住那些迟迟不交块、却占着窗口的对端),才会被派活。第三是防重复:同一个块原则上只登记给一个对端,谁先交回算谁,省掉带宽浪费——这也是后来引入显式点名接口(getblockfrompeer)的原因:当调度器和现实脱节,运维者可以手动指定”向这个邻居再要一次这个块”。派活发出的是带起点哈希的批量请求消息,对端按自己的数据把一段块或区块头送回来。

卡住的块与被记性的对端

一个块若长期悬空,整个链条校验就堵在它后面。核心的应对分两层:轻的一层是把该块重新放回清单,允许换别的邻居重派——前提是先把旧请求的登记释放掉;重的一层是针对”占着窗口不交货”的对端,识别出它拖累下载后直接断连,让连接槽让给更快的邻居。对端被断开不等于被拉黑,多数情况下它只是线路差或负载高。日志里出现反复重派同一高度、或不断换邻居的描述时,基本能断定问题在对端供给而不是本地校验:本地校验慢的表现是块明明到了磁盘、链尖却不动,两者的排查方向完全不同。

顺序校验这台机器怎么喂饱

既然校验必须按序,调度器的全部技巧就在于”让块先于校验需求到达”。只要磁盘写入跟得上,在途窗口就是流水线缓冲:16 个块的名额乘上十几个活跃邻居,足够让校验线程手边始终有活。这也是机械硬盘上同步慢的根本原因——瓶颈从网络挪到了随机写入,窗口里的块到了也得排队入库。另一条工程路线是改变校验本身的时序:快照类同步方案把一大段历史校验交给后台链状态去做,主链状态先接受快照再验证,前台的下载调度因此只需补上快照之后的增量。

同步快慢的几条经验线

同一台机器上,同步曲线会呈现阶段感:区块头阶段以千块每秒计,几乎不受磁盘影响;主体下载阶段吞吐由磁盘与带宽短板决定,邻居数量在个位数时进度尤其难看;越接近链尖,单位时间块数越密、相对校验成本越高,进度观感越拖。想让曲线好看,可动的旋钮就那么几个:用固态硬盘、保证出站长连接数量、给进程足够的内存做区块预取缓冲。至于”假设旧块已验证”或”从快照起步”这类加速,属于改变信任前提的功能,是否启用、默认状态如何,都以你所用版本文档为准。

快速问答

问:同一高度重复向两个邻居要块,会造成分叉或双花吗? 答:不会。区块内容以哈希为身份证,收到哪份先做格式与哈希检查,两份一致就是重复,不一致其中一份当场作废并给来源记过。

问:在途窗口能手动调大吗? 答:它是源码常量,配置里没有公开参数;调它属于改代码,官方不提供这扇门,普通运维应该从磁盘和连接数入手。

问:下载乱序会不会让钱包扫错高度? 答:钱包只看已被按序接受的链,未排好序的块在重组队列里,不会提前进钱包视野。

风险提示:本文为节点软件机制科普,不构成投资建议;联网对外提供节点服务涉及网络暴露面,请遵循官方文档的安全建议。