目標は、メモリの制約内でプロセスを管理できるようにすることです。ZODB をツールとして使用してこれを行うには、ZODB トランザクションがどのように機能し、どのように使用するかを理解する必要があります。
ZODB が大きくなる理由
まず、トランザクション コミットがここで何をするかを理解する必要があります。これは、Data.fs が非常に大きくなる理由も説明します。
ZODB は、変更された永続オブジェクトがディスクに書き込まれるトランザクションごとにデータを書き出します。ここで重要な詳細は、変更された永続オブジェクトです。ZODB は永続オブジェクト単位で動作します。
すべての python 値が永続オブジェクトであるとは限りません。まっすぐな python クラスを定義すると、永続化されず、int や list などの組み込みの python 型も永続化されません。一方、継承元として定義したクラスpersistence.Persistent
は永続オブジェクトです。コードで使用するクラスBTrees
と同様に、一連のクラスはから継承します。PeristentList
Persistent
これで、トランザクションのコミット時に、変更されたすべての永続オブジェクトがそのトランザクションの一部としてディスクに書き込まれます。したがって、PersistentList
追加されたオブジェクトはすべてディスクに完全に書き込まれます。BTrees
これをもう少し効率的に処理します。それらは、それ自体が永続的なバケットを保存し、実際に保存されたオブジェクトを保持します。したがって、作成するいくつかの新しいノードごとに、BTree 構造全体ではなく、バケットがトランザクションに書き込まれます。ツリーに保持されているアイテム自体が永続オブジェクトであるため、それらへの参照のみがバケット レコードに格納されることに注意してください。
現在、ZODB はトランザクション データをData.fs
ファイルに追加して書き込みますが、古いデータは自動的に削除されません。ストアから特定のオブジェクトの最新バージョンを見つけることで、データベースの現在の状態を構築できます。これが、あなたが非常に成長している理由であり、トランザクションがコミットされるData.fs
につれて、ますます大きなPersistentList
インスタンスの新しいバージョンを書き出しています。
古いデータを削除することをパッキングVACUUM
と呼びます。これはPostgreSQL やその他のリレーショナル データベースのコマンドに似ています。単に変数の.pack()
メソッドを呼び出してすべての古いリビジョンdb
を削除するか、そのメソッドのおよびパラメータを使用して、保持する履歴の量に制限を設定します。最初はタイムスタンプ (エポックからの秒数) であり、その前にパックできます。現在の時刻から保持する過去の日数、または指定されている場合。古いトランザクションの部分的なリストが削除されるため、パッキングによってデータ ファイルが大幅に削減されます。パッキングはコストのかかる操作であるため、データセットのサイズによっては時間がかかる場合があることに注意してください。t
days
time.time()
days
t
トランザクションを使用してメモリを管理する
永続性を使用してメモリの制約を回避し、トランザクションを使用して物事をディスクにフラッシュしようとすることで、非常に大きなデータセットを構築しようとしています。ただし、通常、トランザクション コミットを使用すると、データセットの構築が完了したことが通知されます。これは、1 つのアトミックな全体として使用できるものです。
ここで使用する必要があるのはsavepointです。セーブポイントは基本的にサブトランザクションであり、トランザクション全体の中でデータを一時的にディスクに保存するよう要求できるポイントです。トランザクションをコミットすると、永続化されます。セーブポイントを作成するには、トランザクションで.savepoint
メソッドを呼び出します。
for Gnodes in G.nodes(): # Gnodes iterates over 10000 values
Gvalue = someoperation(Gnodes)
for Hnodes in H.nodes(): # Hnodes iterates over 10000 values
Hvalue =someoperation(Hnodes)
score = SomeOperation on (Gvalue,Hvalue)
btree_container.setdefault(Gnodes, PersistentList()).append(
[Hnodes, score, -1 ])
transaction.savepoint(True)
transaction.commit()
上記の例では、optimistic
フラグを True に設定しました。つまり、このセーブポイントにロールバックするつもりはありません。一部のストレージはロールバックをサポートしていないため、これが不要であることを通知することで、そのような状況でコードを機能させることができます。
transaction.commit()
また、データセット全体が処理されたときに発生することにも注意してください。これは、コミットが達成するはずのものです。
セーブポイントが行うことの 1 つは、ZODB キャッシュのガベージ コレクションを呼び出すことです。これは、現在使用されていないデータがメモリから削除されることを意味します。
そこの「現在使用されていない」部分に注意してください。コードのいずれかが変数に大きな値を保持している場合、データをメモリからクリアできません。あなたが私たちに示したコードから判断できる限り、これは問題ないようです。しかし、あなたの操作がどのように機能するか、またはノードをどのように生成するかはわかりません。たとえば、イテレータが行う場合に完全なリストをメモリ内に構築しないように注意してください。または、たとえば、リストのすべてのリストが参照される大きな辞書を構築します。
セーブポイントを作成する場所について少し実験できます。を処理するたびに作成することも、上記で行ったようにループを処理HNodes
したときにのみ作成することもできます。GNodes
ごとにリストを構築しているGNodes
ため、とにかくすべてをループしている間はメモリに保持され、H.nodes()
ディスクへのフラッシュはおそらく、完全に構築を完了して初めて意味があります。
ただし、より頻繁にメモリをクリアする必要がある場合は、の代わりにBTrees.OOBTree.TreeSet
クラスまたはクラスを使用して、データをより永続的なオブジェクトに分割することを検討する必要があります。Aは順序付けされていますが (簡単に) インデックス可能ではありませんが、 aは単純なインクリメント インデックス キーを使用してリストとして使用できます。BTrees.IOBTree.BTree
PersistentList
TreeSet
BTree
for i, Hnodes in enumerate(H.nodes()):
...
btree_container.setdefault(Gnodes, IOBTree())[i] = [Hnodes, score, -1]
if i % 100 == 0:
transaction.savepoint(True)
上記のコードは、PersistentList の代わりに BTree を使用し、100HNodes
処理ごとにセーブポイントを作成します。BTree は、それ自体が永続的なオブジェクトであるバケットを使用するため、構造全体をより簡単にセーブポイントにフラッシュできます。すべてH.nodes()
を処理するためにメモリにとどまる必要はありません。