跳至主要內容
進階

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 前請務必備份錢包
已複製連結
已複製到剪貼簿