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: いくつかの細部を更新し、いくつかのタイプミスを修正した。