【发布时间】:2016-02-13 01:49:34
【问题描述】:
我正在尝试创建超过 200GB 的(单个)数据库文件(将定期更新/偶尔部分重新创建/偶尔查询),在我看来相对较大。大约有 16k 个表,大小从几 kb 到 ~1gb;他们有 2-21 列。最长的表有近 1500 万行。
我编写的脚本一个一个地遍历输入文件,进行一系列处理和正则表达式以获取可用数据。它定期发送一批(0.5-1GB)以写入 sqlite3,每个表都有一个单独的 executemany 语句插入数据。在这些执行语句之间没有提交或创建表语句等,所以我相信所有这些都属于一个事务
最初,该脚本的运行速度足以满足我的目的,但随着时间的推移,它在接近完成时会显着减慢 - 不幸的是,我需要进一步减慢它以保持我的笔记本电脑在正常使用中的内存使用可控。
我做了一些快速的基准测试,比较了将相同的样本数据插入空数据库与插入 200GB 数据库。后来的测试执行插入语句的速度慢了约 3 倍(相对速度提交更差,但绝对而言它微不足道)——除此之外没有显着差异
当我研究这个话题之前主要是returned results for indexes slowing down inserts on large tables。答案似乎是在没有索引的表上插入应该保持或多或少相同的速度,而不管大小。因为我不需要对该数据库运行大量查询,所以我没有创建任何索引。我什至仔细检查并检查了索引,如果我做对了,应该将其排除为原因:
c.execute('SELECT name FROM sqlite_master WHERE type="index"')
print(c.fetchone()) #returned none
出现的另一个问题是事务,但我不明白仅针对相同的脚本和要写入的相同数据写入大型数据库会有什么问题。
相关代码缩写:
#process pre defined objects, files, retrieve data in batch -
#all fine, no slowdown on full database
conn = sqlite3.connect(db_path)
c = conn.cursor()
table_breakdown=[(tup[0]+'-'+tup[1],tup[0],tup[1]) for tup in all_tup] # creates list of tuples
# (tuple name "subject-item", subject, item)
targeted_create_tables=functools.partial(create_tables,c) #creates new table if needed
#for new subjects/items-
list(map(targeted_create_tables,table_breakdown)) #no slowdown on full database
targeted_insert_data=functools.partial(insert_data,c) #inserts data for specific
#subject item combo
list(map(targeted_insert_data,table_breakdown)) # (3+) X slower
conn.commit() # significant relative slowdown, but insignificant in absolute terms
conn.close()
及相关插入函数:
def insert_data(c,tup):
global collector ###list of tuples of data for a combo of a subject and item
global sql_length ###pre defined dictionary translating the item into the
#right length (?,?,?...) string
tbl_name=tup[0]
subject=tup[1]
item=tup[2]
subject_data=collector[subject][item]
if not (subject_data==[]):
statement='''INSERT INTO "{0}" VALUES {1}'''.format(tbl_name,sql_length[item])
c.executemany(statement,subject_data)#massively slower, about 80% of
#inserts > twice slower
subject_data=[]
编辑:每个 CL 请求的表创建功能。我知道这是低效的(以这种方式检查表名是否存在与创建表所需的时间大致相同),但这对减慢速度并不重要。
def create_tables(c,tup):
global collector
global title #list of column schemes to match to items
tbl_name=tup[0]
bm_unit=tup[1]
item=tup[2]
subject_data=bm_collector[bm_unit][item]
if not (subject_data==[]):
c.execute('SELECT * FROM sqlite_master WHERE name = "{0}" and type="table"'.format(tbl_name))
if c.fetchone()==None:
c.execute('CREATE TABLE "{0}" {1}'.format(tbl_name,title[item]))
标题字典中有 65 种不同的列方案,但这是它们的外观示例:
title.append(('WINDFOR','(TIMESTAMP TEXT, SP INTEGER, SD TEXT, PUBLISHED TEXT, WIND_CAP NUMERIC, WIND_FOR NUMERIC)'))
有人对在哪里查看或可能导致此问题的原因有任何想法吗?如果我遗漏了重要信息或遗漏了一些可怕的基本信息,我深表歉意,我完全冷漠地进入了这个主题领域。
【问题讨论】:
-
显示表定义。
-
你打算用 1500 万行且没有索引的 sqlite 表做什么?这将是根本不可能查询的。
-
抱歉@CL,您能否通过表格定义扩展您的需求/意思?
-
@Falmarri 绝对无法查询,还是很慢?我不会定期或频繁地查询大多数表(尤其是那个,如果我需要它,它几乎就在那里),我的印象是不使用索引然后在执行后添加它们会更快插入
-
CREATE TABLE 语句。