我试图通过以下代码生成数据:
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)
dic_score.setdefault(Gnodes,[]).append([Hnodes, score, -1 ])
Run Code Online (Sandbox Code Playgroud)
由于字典很大(10000个键X 10000个列表,每个包含3个元素),因此很难将其保存在内存中.我正在寻找一个解决方案,它存储密钥:值(以列表的形式)对生成后立即.这里建议,以特定格式(Python)编写和阅读字典,以将ZODB与Btree结合使用.
如果这太天真,请耐心等待,我的问题是,何时应该调用transaction.commit()提交数据?如果我在内部循环结束时调用它,则生成的文件非常大(不确定原因).这是一个片段:
storage = FileStorage('Data.fs')
db = DB(store)
connection = db.open()
root = connection.root()
btree_container = IOBTree
root[0] = btree_container
for nodes in G.nodes()
btree_container[nodes] = PersistentList () ## I was loosing data prior to doing this
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,[]).append([Hnodes, score, -1 ])
transaction.commit()
Run Code Online (Sandbox Code Playgroud)
如果我在两个循环之外调用它会怎么样?就像是:
......
......
score = SomeOperation on (Gvalue,Hvalue)
btree_container.setdefault(Gnodes,[]).append([Hnodes, score, -1 ])
transaction.commit()
Run Code Online (Sandbox Code Playgroud)
将所有数据保存在内存中,直到我调用transaction.commit()?同样,我不确定为什么,但这会导致磁盘上的文件较小.
我想最小化内存中保存的数据.任何指导将不胜感激 !
Mar*_*ers 28
您的目标是使您的流程在内存限制内可管理.为了能够使用ZODB作为工具,您需要了解ZODB事务的工作方式以及如何使用它们.
首先,您需要了解事务提交在这里做了什么,这也解释了为什么您的Data.fs变得如此之大.
ZODB按事务写出数据,其中任何已更改的持久对象都会写入磁盘.这里的重要细节是已经改变的持久对象 ; ZODB以持久对象为单位工作.
并非每个python值都是持久对象.如果我定义一个直接的python类,它将不是持久的,也不是任何内置的python类型,如int或list.另一方面,您定义的任何继承的类persistence.Persistent 都是持久对象.BTrees类的集合以及PeristentList您在代码中使用的类确实继承自Persistent.
现在,在事务提交上,任何已更改的持久对象都将作为该事务的一部分写入磁盘.因此,任何PersistentList已追加到将被写入对象在它的整个磁盘.BTrees处理这个更高效; 他们存储Buckets,它们本身是持久的,而Buckets又保存了实际存储的对象.因此,对于您创建的每个新节点,都会将一个Bucket写入事务,而不是整个BTree结构.请注意,因为树中保存的项本身是持久对象,所以只有对它们的引用才会存储在Bucket记录中.
现在,ZODB通过将事务数据附加到Data.fs文件来写入事务数据,并且它不会自动删除旧数据.它可以通过从商店中查找给定对象的最新版本来构造数据库的当前状态.这就是为什么你Data.fs的成长如此之多,你PersistentList在提交事务时写出更大和更大实例的新版本.
删除旧数据称为打包,类似于VACUUMPostgreSQL和其他关系数据库中的命令.只需调用该.pack()方法的db变量删除所有的旧版本,或者使用t与days该方法来设置多少历史保留限制的参数,第一个是time.time()时间戳(秒从epoch),它之前,你可以打包,并days为从当前时间或t指定的过去保留的天数.随着旧事务中的部分列表被删除,打包应该会大大减少您的数据文件.请注意,打包是一项昂贵的操作,因此可能需要一段时间,具体取决于数据集的大小.
您正在尝试构建一个非常大的数据集,使用持久性来解决内存约束,并使用事务来尝试将事物刷新到磁盘.但是,通常情况下,使用事务提交信号已经完成了数据集的构建,您可以将其用作一个原子整体.
你需要在这里使用的是一个保存点.保存点本质上是子事务,在整个事务期间您可以请求将数据临时存储在磁盘上.当您提交交易时,它们将被永久化.要创建保存点,请在事务上调用.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()
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,我将optimistic标志设置为True,这意味着:我不打算回滚到这个保存点 ; 某些存储不支持回滚,并且您不需要这样的信号使您的代码在这种情况下工作.
还要注意,transaction.commit()当处理完整个数据集时会发生这种情况,这就是提交应该实现的目的.
保存点所做的一件事是调用ZODB缓存的垃圾收集,这意味着当前未使用的任何数据都将从内存中删除.
注意那里的'当前没有使用'部分; 如果您的任何代码保留变量中的大值,则无法从内存中清除数据.据我向您展示的代码我可以确定,这看起来很好.但我不知道您的操作如何工作或如何生成节点; 例如,小心避免在迭代器执行时在内存中构建完整列表,或者构建大型字典,其中引用了所有列表列表.
您可以尝试一下创建保存点的位置; 你可以在每次处理一个时创建一个HNodes,或者只在GNodes我完成上面的循环时创建一个.你正在构建一个列表GNodes,所以H.nodes()无论如何它都将被保存在内存中,并且一旦你完全构建它,刷新到磁盘可能才有意义.
但是,如果您发现需要更频繁地清除内存,则应考虑使用BTrees.OOBTree.TreeSet类或BTrees.IOBTree.BTree类而不是a PersistentList来将数据分解为更持久的对象.A TreeSet是有序的但不是(容易)可索引的,而a BTree可以通过使用简单的递增索引键用作列表:
for i, Hnodes in enumerate(H.nodes()):
...
btree_container.setdefault(Gnodes, IOBTree())[i] = [Hnodes, score, -1]
if i % 100 == 0:
transaction.savepoint(True)
Run Code Online (Sandbox Code Playgroud)
上面的代码使用BTree而不是PersistentList,并且每100次HNodes处理就创建一个保存点.因为BTree使用桶本身作为持久对象,所以整个结构可以更容易地刷新到保存点,而不必留在内存中供所有H.nodes()处理.