blkindex.datのビルドこそがすべてのディスクアクティビティを引き起こしている。 […] Berkeley DBの設定を調整して、キャッシュメモリーを有効化または増加できるかもしれない。
AddToBlockIndex(main.cpp) の以下のコードは恐ろしく非効率で、初回ブロックダウンロードを劇的に遅くしている:
CTxDB txdb;
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
// New best
if (pindexNew->bnChainWork > bnBestChainWork)
if (!SetBestChain(txdb, pindexNew))
return false;
txdb.Close();
これでは、データベース(db4でもSQLでも何でも)に大量のレコードをロードする標準的な手法 ―― 複数のレコード挿入を1つのデータベーストランザクションでラップする ―― が使えない。理想的には、Bitcoinは初回ブロックダウンロード中、1000ブロックほどごとに1回だけTxnCommit() を発行すればよい。クラッシュが起きてもデータベースは一貫した状態を保つ。
さらに、新しいブロックごとにデータベースのopen + closeを行うのはとてつもなく高コストだ。データベースのopenとclose操作のたびに、db4は
- データベースの健全性を診断して、リカバリが必要か判定する。このテストにはデータコピーが必要になる場合がある。
- メモリープールを再初期化する
- データベースファイルのメタデータを読む
- ファイルロックを取得する
- b-treeまたはハッシュ固有のメタデータを読み込んで初期化する。ハッシュテーブル/b-treeのルートを構築する。
- たとえトランザクションがDB_TXN_NOSYNCで呼ばれていてもsyncを強制する
- メモリープールをfsyncする
加えて、Bitcoinはデータベースチェックポイントを強制し、ログから全トランザクションをメインデータベースに押し込む。
そう、その長い一連の操作は、データベースのclose+openサイクルにおいて、environment単位(DB_ENV)ではなくデータベース単位(DB)で実行される。Bitcoinにとっては、つまり新しいブロックごとにこれをやっているということだ。途方もなく非効率で、db4が想定する使い方ではない。
推奨:
-
Bitcoinはプログラム起動時にenvironmentだけでなくデータベースも開き、プログラム終了時にデータベースを閉じるべきだ。db4は適切なトランザクション利用が維持されていればクラッシュを扱えるように設計されている ―― そしてBitcoinはすでにdb4トランザクションを適切に使っている。
-
初回ブロックダウンロードでは、txnコミットはレコードごとではなくNレコードごとに1回行うべきだ。N=1000を提案する。
EDIT: いくつかの細部を更新し、いくつかのタイプミスを修正した。