進階
Reindex
深入了解 Bitcoin Core 的 reindex 功能,如何重建區塊鏈索引和 UTXO 集。
10 分鐘
什麼是 Reindex?
Reindex 是 Bitcoin Core 的重建索引功能,用於從本地區塊文件重新處理所有區塊, 重建 UTXO 集、區塊索引和其他資料庫。當資料庫損壞或需要驗證完整性時特別有用。
Reindex 的用途
資料庫修復
- • UTXO 資料庫損壞
- • 區塊索引損壞
- • LevelDB 錯誤
升級/變更
- • 啟用新的索引
- • 升級後的完整性檢查
- • 更改 assumevalid
Reindex 模式
-reindex
完整重建模式,從本地區塊文件重新處理所有區塊,重建所有資料庫。
# 完整重建索引
bitcoind -reindex
# 配合其他選項
bitcoind -reindex -dbcache=4096 -txindex -reindex 過程
- 1 清除資料庫
刪除 chainstate(UTXO 集)和 blocks/index
- 2 掃描區塊文件
讀取 blocks/blk*.dat 文件找出所有區塊
- 3 重建區塊索引
按順序排列區塊,建立 blocks/index
- 4 處理每個區塊
驗證交易並更新 UTXO 集
-reindex-chainstate
僅重建 UTXO 集(chainstate),保留區塊索引。比完整 reindex 更快。
# 僅重建 chainstate
bitcoind -reindex-chainstate
# 適用於 chainstate 損壞但區塊索引完好的情況 兩種模式比較
| 特性 | -reindex | -reindex-chainstate |
|---|---|---|
| 重建區塊索引 | 是 | 否 |
| 重建 UTXO 集 | 是 | 是 |
| 掃描區塊文件 | 是 | 否 |
| 所需時間 | 較長 | 較短 |
| 適用情況 | 區塊索引損壞 | 僅 chainstate 損壞 |
實現細節
區塊掃描
// 區塊文件結構
interface BlockFile {
path: string; // blocks/blk00000.dat
size: number;
blocks: BlockLocation[];
}
interface BlockLocation {
hash: string;
fileNumber: number;
dataPos: number; // 在文件中的位置
undoPos: number; // undo 數據位置
}
// 區塊文件格式
// 每個區塊前有 8 字節頭
// [4 bytes: magic] [4 bytes: size] [block data]
const MAINNET_MAGIC = 0xD9B4BEF9;
const TESTNET_MAGIC = 0x0709110B;
async function scanBlockFile(
filePath: string,
network: 'mainnet' | 'testnet'
): Promise {
const magic = network === 'mainnet' ? MAINNET_MAGIC : TESTNET_MAGIC;
const blocks: BlockLocation[] = [];
const file = await openFile(filePath);
let pos = 0;
while (pos < file.size) {
// 讀取魔數
const fileMagic = await file.readUint32LE(pos);
if (fileMagic !== magic) {
// 可能是填充或損壞,跳過
pos++;
continue;
}
// 讀取區塊大小
const blockSize = await file.readUint32LE(pos + 4);
// 讀取區塊數據並計算哈希
const blockData = await file.read(pos + 8, blockSize);
const blockHash = calculateBlockHash(blockData);
blocks.push({
hash: blockHash,
fileNumber: extractFileNumber(filePath),
dataPos: pos + 8,
undoPos: 0, // 稍後填充
});
pos += 8 + blockSize;
}
return blocks;
} Chainstate 重建
interface UTXO {
txid: string;
vout: number;
value: number;
scriptPubKey: Buffer;
height: number;
coinbase: boolean;
}
class ChainstateRebuilder {
private utxoSet: Map = new Map();
private currentHeight: number = 0;
async processBlock(block: Block): Promise {
// 1. 處理 coinbase 交易(新創建的幣)
const coinbaseTx = block.transactions[0];
for (let i = 0; i < coinbaseTx.outputs.length; i++) {
const output = coinbaseTx.outputs[i];
if (!isUnspendable(output.scriptPubKey)) {
this.addUtxo({
txid: coinbaseTx.txid,
vout: i,
value: output.value,
scriptPubKey: output.scriptPubKey,
height: this.currentHeight,
coinbase: true,
});
}
}
// 2. 處理一般交易
for (let i = 1; i < block.transactions.length; i++) {
const tx = block.transactions[i];
// 移除被花費的 UTXO
for (const input of tx.inputs) {
this.removeUtxo(input.txid, input.vout);
}
// 添加新的 UTXO
for (let j = 0; j < tx.outputs.length; j++) {
const output = tx.outputs[j];
if (!isUnspendable(output.scriptPubKey)) {
this.addUtxo({
txid: tx.txid,
vout: j,
value: output.value,
scriptPubKey: output.scriptPubKey,
height: this.currentHeight,
coinbase: false,
});
}
}
}
this.currentHeight++;
}
private addUtxo(utxo: UTXO): void {
const key = `${utxo.txid}:${utxo.vout}`;
this.utxoSet.set(key, utxo);
}
private removeUtxo(txid: string, vout: number): void {
const key = `${txid}:${vout}`;
this.utxoSet.delete(key);
}
} 效能優化
使用 dbcache
增加 dbcache 可以顯著加快 reindex 速度,因為更多 UTXO 可以保持在記憶體中。
# 使用 4GB dbcache(推薦用於 reindex)
bitcoind -reindex -dbcache=4096
# 使用 8GB dbcache(如果有足夠記憶體)
bitcoind -reindex -dbcache=8192
# 建議:留出至少 2-4GB 給系統和其他進程 Reindex 時間估算
| dbcache | 磁碟類型 | 估計時間 |
|---|---|---|
| 450MB(預設) | HDD | 數天 |
| 450MB(預設) | SSD | 10-20 小時 |
| 4096MB | SSD | 4-8 小時 |
| 8192MB+ | NVMe | 2-4 小時 |
其他優化選項
# 組合優化選項
bitcoind -reindex \
-dbcache=4096 \
-par=0 \ # 使用所有 CPU 核心進行腳本驗證
-checkblocks=0 \ # 跳過啟動時的區塊檢查
-checklevel=0 # 最小檢查級別
# 注意:這些選項會減少驗證,僅在信任本地數據時使用 常見場景
資料庫損壞
常見錯誤訊息
Error: Error opening block database.
Error: Corrupted block database detected.
Error: A fatal internal error occurred, see debug.log for details
LevelDB error: Corruption: block checksum mismatch 修復步驟
# 1. 停止 Bitcoin Core
bitcoin-cli stop
# 2. 備份錢包(重要!)
cp -r ~/.bitcoin/wallets ~/bitcoin-wallets-backup
# 3. 嘗試 reindex-chainstate(較快)
bitcoind -reindex-chainstate
# 4. 如果仍然失敗,使用完整 reindex
bitcoind -reindex
# 5. 如果還是失敗,可能需要重新下載
rm -rf ~/.bitcoin/blocks ~/.bitcoin/chainstate
bitcoind 啟用 txindex
# 首次啟用 txindex 需要 reindex
bitcoind -txindex -reindex
# 或者添加到配置文件
echo "txindex=1" >> ~/.bitcoin/bitcoin.conf
bitcoind -reindex 版本升級
某些版本升級可能需要 reindex,通常會在發布說明中提及。
提示: 升級前請閱讀發布說明。大多數小版本升級不需要 reindex, 但某些涉及資料庫格式變更的升級可能需要。
監控進度
# 查看 reindex 進度
tail -f ~/.bitcoin/debug.log | grep -E "(Reindexing|progress=)"
# 使用 RPC 查看區塊鏈資訊
bitcoin-cli getblockchaininfo
# 查看驗證進度
bitcoin-cli getblockchaininfo | jq '.verificationprogress'
# 持續監控
watch -n 5 'bitcoin-cli getblockchaininfo | jq "{blocks, headers, verificationprogress}"' 日誌輸出示例
Reindexing block file blk00000.dat...
Reindexing block file blk00001.dat...
...
UpdateTip: new best=0000000000000000000... height=800000 progress=0.999854
Pre-allocating up to position 0x8000000 in blk02345.dat 總結
- ✓ -reindex:完整重建區塊索引和 UTXO 集
- ✓ -reindex-chainstate:僅重建 UTXO 集,更快
- ✓ 效能優化:增加 dbcache 可顯著加快 reindex
- ⚠ 注意:Reindex 前請務必備份錢包
已複製連結