когда фиксировать данные в ZODB

Я пытаюсь передать данные, сгенерированные следующим фрагментом кода:

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 ]) 

Поскольку словарь большой (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() 

Что делать, если я называю это за пределами обоих циклов? Что-то вроде:

  ...... ...... score = SomeOperation on (Gvalue,Hvalue) btree_container.setdefault(Gnodes,[]).append([Hnodes, score, -1 ]) transaction.commit() 

Будут ли все данные храниться в памяти до тех пор, пока я не вызову transaction.commit () ? Опять же, я не уверен, почему, но это приводит к меньшему размеру файла на диске.

Я хочу минимизировать данные, хранящиеся в памяти. Любое руководство будет оценено!

  • Как правильно управлять памятью с помощью ZODB?
  • ZODB не может выполнить
  • Метод индексирования базы данных объекта
  • zc.lockfile.LockError в ZODB
  • 2 Solutions collect form web for “когда фиксировать данные в ZODB”

    Ваша цель – сделать ваш процесс управляемым в пределах ограничений памяти. Чтобы иметь возможность сделать это с помощью ZODB в качестве инструмента, вам нужно понять, как работают транзакции ZODB и как их использовать.

    Почему ваш ZODB растет настолько большой

    Прежде всего, вам нужно понять, что совершает транзакция здесь, что также объясняет, почему ваш Data.fs становится настолько большим.

    ZODB записывает данные за транзакцию, где любой постоянный объект, который был изменен, записывается на диск. Важная деталь здесь – постоянный объект, который изменился ; ZODB работает в единицах постоянных объектов .

    Не каждое значение python является постоянным объектом. Если я определяю прямолинейный класс python, он не будет постоянным и не будет встроенных типов python, таких как int или list. С другой стороны, любой класс, который вы определяете, наследуемый от persistence.Persistent постоянный объект. Набор классов BTrees , а также класс PeristentList вы используете в своем коде , наследуются от Persistent .

    Теперь, при фиксации транзакции, любой постоянный объект, который был изменен , записывается на диск как часть этой транзакции. Таким образом, любой объект PersistentList , который был добавлен, будет полностью записан на диск. BTrees обрабатывают это немного более эффективно; они хранят ведра, сами по себе стойкие, которые, в свою очередь, хранят фактически сохраненные объекты. Поэтому для каждого нескольких новых узлов, которые вы создаете, в транзакцию записывается Bucket, а не вся структура BTree. Обратите внимание, что поскольку элементы, хранящиеся в дереве, сами являются постоянными объектами, только ссылки на них хранятся в записях Bucket.

    Теперь ZODB записывает данные транзакций, добавляя их в файл Data.fs и не удаляет старые данные автоматически. Он может построить текущее состояние базы данных, найдя самую последнюю версию данного объекта из магазина. Вот почему ваш Data.fs растет так сильно, вы пишете новые версии более крупных и больших экземпляров PersistentList когда транзакции совершаются.

    Удаление старых данных называется упаковкой , которая аналогична команде VACUUM в PostgreSQL и других реляционных базах данных. Просто вызовите метод .pack() в переменной db чтобы удалить все старые ревизии или использовать параметры t и days этого метода, чтобы установить ограничения на сохранение истории, первая – time.time() timestamp (seconds начиная с эпохи), перед которым вы можете упаковать, а days – это количество дней в прошлом, чтобы сохранить текущее время или t если это указано. Упаковка должна значительно уменьшить ваш файл данных, поскольку частичные списки в более старых транзакциях будут удалены. Имейте в виду, что упаковка является дорогостоящей операцией и, следовательно, может занять некоторое время, в зависимости от размера вашего набора данных.

    Использование транзакции для управления памятью

    Вы пытаетесь создать очень большой набор данных, используя постоянство, чтобы обойти ограничения с памятью, и используете транзакции, чтобы попытаться сбросить все на диск. Обычно, однако, используя сигналы фиксации транзакции, вы завершили построение своего набора данных, что вы можете использовать как одно атомное целое.

    То, что вам нужно использовать здесь, – это точка сохранения . Savepoints – это по существу субтранзакции, точка во время всей транзакции, где вы можете запросить временное сохранение данных на диске. При совершении транзакции они станут постоянными. Чтобы создать .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 значение «Истина», что означает: я не намерен откатываться к этой точке сохранения ; некоторые хранилища не поддерживают откатывание, и сигнализация о том, что вам не нужна, заставляет ваш код работать в таких ситуациях.

    Также обратите внимание, что transaction.commit() происходит, когда весь набор данных был обработан, что и должно делать фиксация.

    Одна вещь, которую выполняет точка сохранения, – это вызов сборщика мусора в кэшах ZODB, что означает, что любые данные, которые в настоящее время не используются, удаляются из памяти.

    Обратите внимание на часть «не используется в настоящее время»; если какой-либо из ваших кодов содержит большие значения в переменной, данные не могут быть удалены из памяти. Насколько я могу определить из кода, который вы нам показали, это выглядит отлично. Но я не знаю, как работают ваши операции или как вы создаете узлы; будьте осторожны, чтобы избежать создания полных списков в памяти там, когда будет выполняться итератор, или построить большие словари, на которые ссылаются все ваши списки списков.

    Вы можете немного поэкспериментировать о том, где вы создаете свои точки сохранения; вы можете создавать его каждый раз, когда вы обрабатывали один HNodes , или только когда это делается с помощью цикла GNodes как я сделал выше. Вы GNodes список для GNodes , поэтому он будет храниться в памяти, и все равно будет циклически H.nodes() по всем H.nodes() , и удаление на диск, вероятно, будет иметь смысл только после завершения его полного завершения.

    Если, однако, вы обнаружите, что вам нужно чаще очищать память, вам следует использовать либо класс BTrees.OOBTree.TreeSet либо класс 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) 

    В приведенном выше коде используется BTree вместо PersistentList и создается HNodes сохранения каждые 100 обработанных HNodes . Поскольку BTree использует ведра, которые являются постоянными объектами сами по себе, вся структура может быть легко H.nodes() до точки сохранения без необходимости оставаться в памяти для всех H.nodes() .

    То, что составляет транзакцию, зависит от того, что должно быть «атомарным» в вашем приложении. Если транзакция завершится неудачно, она будет отклонена в предыдущее состояние (сразу после последнего фиксации). Из вашего кода приложения видно, что вы хотите вычислить значение для каждого Gnodes. Итак, ваша фиксация может идти в конце цикла Gnodes следующим образом:

     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 ]) # once we calculate the value for a Gnodes, commit transaction.commit() 

    Из вашего кода видно, что комбинация «Hvalue» не зависит от Gvalue или Gnodes. Если это дорогостоящая операция, вы вычисляете ее 1000 раз для каждого Gnodes, даже если это не влияет на его вычисление. Итак, я бы вытащил его из цикла.

     # Hnodes iterates over 10000 values hvals = dict((Hnodes, someoperation(Hnodes)) for Hnodes in H.nodes()) # now you have mapping of Hnodes and Hvalues for Gnodes in G.nodes(): # Gnodes iterates over 10000 values Gvalue = someoperation(Gnodes) for Hnodes, Hvalue in hvals.iteritems(): score = SomeOperation on (Gvalue,Hvalue) btree_container.setdefault(Gnodes,[]).append([Hnodes, score, -1 ]) # once we calculate the value for a given Gnodes, commit transaction.commit() 
    Python - лучший язык программирования в мире.