Python 从一个SQL数据库创建另一个SQL数据库时内存使用过度

Python 从一个SQL数据库创建另一个SQL数据库时内存使用过度,python,pandas,sqlite,Python,Pandas,Sqlite,我试图遍历一个SQLite数据库(我称之为数据库A),从其中的数据创建一些新变量,然后将这些新数据写入一个新的SQLite数据库(数据库B) 数据库A由表组成,这些表由关于特定月份特定术语的tweet组成(每个tweet及其元数据是一行,并且包括该月的每一天)。每个表的大小大约为0.5 GB 因此,我迭代这些表,创建一个变量,然后将这些新数据写入/提交到数据库B 问题是,在遍历几个表之后,我正在使用的服务器上的工作内存(我有16 GB的RAM)完全用完了(使用BASH中的free-m命令,我可以

我试图遍历一个SQLite数据库(我称之为数据库A),从其中的数据创建一些新变量,然后将这些新数据写入一个新的SQLite数据库(数据库B)

数据库A由表组成,这些表由关于特定月份特定术语的tweet组成(每个tweet及其元数据是一行,并且包括该月的每一天)。每个表的大小大约为0.5 GB

因此,我迭代这些表,创建一个变量,然后将这些新数据写入/提交到数据库B

问题是,在遍历几个表之后,我正在使用的服务器上的工作内存(我有16 GB的RAM)完全用完了(使用BASH中的
free-m
命令,我可以看到大约一半的RAM被“buff/cache”使用)。这不会生成我在输出文件(通常显示Python错误消息)中看到的任何错误,但脚本停止运行

我认为这是SQLite()创建的临时文件的结果,随着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循环继续时RAM就不会被占用?我原以为commit()会释放一些内存,但显然它释放的内存不够


提前谢谢你

我会尝试直接在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()
这里唯一可能消耗资源的操作是
selectdistinct
,它需要扫描整个表,以便在给定的一天只保留不同的行,但每次提交时都应该释放资源。这可能需要一些时间,具体取决于表的数量和大小,但不会崩溃


这个答案添加得太晚了,但我刚刚意识到有很多请求使用了
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 ###
...

我相信SQLite比熊猫更能节省内存。我多次成功地处理了大于可用内存的表,但当然我没有尝试同时加载内存中的所有内容(熊猫就是这么做的)。嗨,谢谢参与。我也有同样的想法,这就是为什么我在表中使用
pandas.read\u sql\u query()
按天查询,但即使这样,也可能读取整个表(由一个月中的所有天组成)。不过,奇怪的是,脚本在遍历了几个表之后失败了(不是在第一个表之后),这对我来说表明,问题不是读取带有熊猫的表,而是一些缓存系统的大小继续增长。不过,也许我错过了什么!我看到你没有关闭你的光标。它们有单独的资源分配。可能连接没有跟踪其游标,因此无法清理它们。所以也试着关闭它们。看看这是否有什么不同。我刚刚又看了一遍你的代码。有件事让我担心:您反复关闭并重新打开数据库连接。顺便说一句,如果关闭在数据库完全释放所有内容之前返回,那么它是无用的,甚至可能是有害的,因为它在下次打开时可能会出现问题。这可能就是发生的情况,因为我无法想象代码中有什么东西会占用这么多内存。buff/cache?别担心。它实际上与空闲内存相同。操作系统尝试使用任何可用内存缓存文件系统以提高性能。也就是说,从内存缓存中读取要比从磁盘中读取快。如果任何进程需要比“空闲”中更多的内存,那么buff/缓存中的内存将被释放,以便为进程让路。buff/cache占用了大量内存,这意味着您对许多或大型文件进行了大量的I/O操作——您就是这样。再次感谢您在这方面提供的帮助!我开始监控正在使用的内存,并在提取之前添加索引,从而大大减少了内存使用。