Чрезмерное использование памяти при создании одной базы данных SQL из другой - PullRequest
0 голосов
/ 04 июня 2019

Я пытаюсь перебрать одну базу данных SQLite (я назову ее базой данных A), создать несколько новых переменных из данных внутри нее, а затем записать эти новые данные в новую базу данных SQLite (база данных B).

База данных A состоит из таблиц, которые состоят из твитов об определенных терминах для определенного месяца (каждый твит и его метаданные представляют собой строку, и каждый день месяца включается).Размер каждой таблицы примерно 0,5 ГБ.

Итак, я перебираю эти таблицы, создаю переменную и затем записываю / фиксирую эти новые данные в базе данных B.

Проблема заключается в том, что после перебора нескольких таблиц рабочая память(У меня 16 ГБ ОЗУ) на сервере, который я использую, полностью израсходовано (используя команду free -m в BASH, я вижу, что около половины ОЗУ используется буфером / кэшем).Это не генерирует никаких ошибок, которые я вижу в моем выходном файле (который обычно показывает сообщения об ошибках Python), но скрипт останавливается.

Я думаю, что это результат временных файлов, созданных SQLite (https://www.sqlite.org/tempfiles.html),, которые продолжают расти по мере продолжения цикла for. Итак, я попытался перебирать строки в таблице день за днеми каждый день фиксирую новые данные в базе данных B, так что журнал отката (см. ссылку выше) - один из этих временных файлов SQL - удаляется (тем самым освобождая память). Однако даже после внесения этих изменений я сталкиваюсь ста же проблема (остановка скрипта).

Я не уверен, насколько сильно здесь поможет код, но вот основные принципы того, что я делаю:

import sqlite3
import pandas

#this defines the SQL query; [long list of columns] is just comma separated column names: id, date, time, etc.
sql_query = ("SELECT DISTINCT [long list of columns] "
            "FROM term "
            "WHERE date = 'day';")

### HERE I GET ALL TABLES IN DATABASE A ###

#go through all the tables in Database A
for t in tables:

   term = t

   ### HERE I GET THE DAYS IN THE CURRENT TABLE ###

   #go through each day in the current table in Database A
   for day in days:

      #open the databases
      connection = sqlite3.connect("../SQL_database/Database_A.db3")
      lite_cursor = connection.cursor()
      connection_new = sqlite3.connect("../SQL_database/Database_B.db3")
      lite_cursor_new = connection_new.cursor()

      #change SQL query to match current day and term
      sql_query = sql_query.replace('day', day)

      #extract the data from the database and put it in the new database
      for chunk in pandas.read_sql_query(sql_query, connection, chunksize = 10000):

         ### HERE I PROCESS THE DATA ###

         #add the current data set to Database B
         new_table = term
         chunk.to_sql(new_table, connection_new, if_exists='append', index=True)

         #redefine SQL query; [long list of columns] is just comma separated column names: id, date, time, etc.
         sql_query = ("SELECT DISTINCT [long list of columns] "
                     "FROM term "
                     "WHERE date = 'day';")

         #commit the changes
         connection_new.commit()

         #close the databases
         connection.close()
         connection_new.close()  

Конечно, чтоЯ хочу, чтобы скрипт выполнялся без пауз / сбоев! Есть ли способ очистить кэш памяти SQLite, чтобы память не была израсходована при продолжении цикла for? Я думал, что commit () освободит некоторую память, нопо-видимому, это не достаточно для выпуска.

Заранее спасибо!

1 Ответ

1 голос
/ 05 июня 2019

Я бы попытался сделать это непосредственно на уровне sqlite.

Sqlite имеет возможность подключать дополнительную базу данных к текущему соединению, что позволяет легко копировать таблицы между различными базами данных.Поскольку вы не добавляете много обработки, pandas довольно бесполезен, и ATTACH DATABASE должно быть достаточно:

import sqlite3

#this defines the SQL query; [long list of columns] is just comma separated column names: id, date, time, etc.
sql_query = ("SELECT DISTINCT [long list of columns] "
            "FROM term "
            "WHERE date = 'day';")

#open the databases
connection = sqlite3.connect("../SQL_database/Database_A.db3")
connection.execute("ATTACH DATABASE '../SQL_database/Database_B.db3' as db_B")

### HERE I GET ALL TABLES IN DATABASE A ###

#go through all the tables in Database A
for t in tables:

   term = t

   ### HERE I GET THE DAYS IN THE CURRENT TABLE ###

   #go through each day in the current table in Database A
   for day in days:

      #change SQL query to match current day and term
      # but don't change original query because we'll need it on next iteration
      sql_query2 = sql_query.replace('day', day) 
      sql_query2 = sql_query2.replace('term', term) 

      # print(sql_query2, end=' ')           # uncomment to make sure of what happens

      # copy table values
      try:
          connection.execute("INSERT INTO db_B.{} ".format(term) + sql_query2)
          # print('inserted')                    # uncomment for traces
      except sqlite3.OperationalError:  # table does not exists
          connection.rollback()
          connection.execute("CREATE TABLE db_B.{} AS ".format(term) + sql_query2)
          # print('created')                     # uncomment for traces

   connection.commit()

connection.close()

Здесь единственной возможной ресурсоемкой операцией является SELECT DISTINCT, которая требует сканирования всей таблицы.сохранить только отдельные строки для данного дня, но ресурсы должны быть освобождены при каждом коммите.Это может занять некоторое время в зависимости от количества и размера ваших таблиц, но это не должно привести к сбою.


Позднее добавление к этому ответу, но я только что понял, что было несколько запросов с использованием DISTINCT ключевое слово и WHERE date =.Производительность базы данных может быть значительно увеличена индексами.Здесь добавление индекса перед извлечением информации окажет значительное влияние на время и память:

...
for t in tables:

   term = t

   connection.execute("CREATE INDEX IF NOT EXISTS I{0} ON {0}(date)"
                      .format(term))

   ### HERE I GET THE DAYS IN THE CURRENT TABLE ###
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...