使用Python3和SQLite的大容量插入性能较差
我有几个包含URL的文本文件。我正在尝试创建一个SQLite数据库来将这些URL存储在一个表中。URL表有两列,即主键(整数)和URL(文本) 我尝试在一个insert命令和循环中插入100000个条目,直到完成URL列表。基本上,读取所有文本文件内容并保存在列表中,然后我使用创建100000个条目的较小列表并插入到表中 文本文件中的URL总数为4591415,文本文件总大小约为97.5 MB 问题:使用Python3和SQLite的大容量插入性能较差,python,sqlite,Python,Sqlite,我有几个包含URL的文本文件。我正在尝试创建一个SQLite数据库来将这些URL存储在一个表中。URL表有两列,即主键(整数)和URL(文本) 我尝试在一个insert命令和循环中插入100000个条目,直到完成URL列表。基本上,读取所有文本文件内容并保存在列表中,然后我使用创建100000个条目的较小列表并插入到表中 文本文件中的URL总数为4591415,文本文件总大小约为97.5 MB 问题: 当我选择文件数据库时,插入大约需要7-7.5分钟。我觉得这不是一个很快的插入,因为我有固态硬盘
表,th,td{
边框:1px纯黑;
边界塌陷:塌陷;
}
th,td{
填充:15px;
文本对齐:左对齐;
}
配置
时间
50000-带日记账=删除且无交易0:12:09.888404
50000-带日记账=删除,带事务处理0:22:43.613580
50000-带日志=内存和事务0:09:01.140017
50000-带日志=内存0:07:38.820148
50000-日志=内存,同步=0 0:07:43.587135
50000-日志=内存,同步=1,页面大小=65535 0:07:19.778217
50000-日志=内存,同步=0,页面大小=65535 0:07:28.186541
50000-日志=删除,同步=1,页面大小=65535 0:07:06.539198
50000-日志=删除,同步=0,页面大小=65535 0:07:19.810333
50000-日记账=wal,同步=0,页码=65535 0:08:22.856690
50000-日记账=wal,同步=1,页码=65535 0:08:22.326936
50000-日志=删除,同步=1,页面大小=4096 0:07:35.365883
50000-日志=内存,同步=1,页面大小=4096 0:07:15.183948
1,00000-日记账=删除,同步=1,页面大小=65535 0:07:13.402985
列“url”上的唯一约束是在url上创建隐式索引。这可以解释尺寸增加的原因
我不认为您可以填充表,然后添加唯一约束
你的瓶颈当然是CPU。请尝试以下操作:
pip安装工具Z
from toolz import partition_all
def add_blacklist_url(self, urls):
# print('add_blacklist_url:: entries = {}'.format(len(urls)))
start_time = datetime.now()
for batch in partition_all(100000, urls):
try:
start_commit = datetime.now()
self.cursor.executemany('''INSERT OR IGNORE INTO blacklist(url) VALUES(:url)''', batch)
end_commit = datetime.now() - start_commit
print('add_blacklist_url:: total time for INSERT OR IGNORE INTO blacklist {} entries = {}'.format(len(templist), end_commit))
except sqlite3.Error as e:
print("add_blacklist_url:: Database error: %s" % e)
except Exception as e:
print("add_blacklist_url:: Exception in _query: %s" % e)
self.db.commit()
time_elapsed = datetime.now() - start_time
print('add_blacklist_url:: total time for {} entries = {}'.format(records, time_elapsed))
未测试代码。默认情况下,SQLite使用自动提交模式。这允许省略
开始事务
。但是这里我们希望所有的插入都在一个事务中,唯一的方法是使用begintransaction
启动一个事务,这样所有要运行的语句都在该事务中
方法executemany
只是在Python外部完成的一个循环execute
,它只调用SQLite prepare语句函数一次
以下是从列表中删除最后N项的非常糟糕的方法:
templist = []
i = 0
while i < self.bulk_insert_entries and len(urls) > 0:
templist.append(urls.pop())
i += 1
slice和del slice即使在空列表上也能工作
两者可能具有相同的复杂性,但100K次追加和弹出调用的成本远远高于让Python在解释器外部执行的成本。是否有一个选项可以在完成后修剪索引?其次,我认为瓶颈可能是数据库本身,因为我已尝试关闭所有应用程序并仅运行脚本,在Task Manager中,我看到大约3-4 MB/s的写入速度。但当我只是阅读文本文件时,它显示大约30-35MB/sA的bootleneck必须是您计算机的一个资源。它可能是磁盘或CPU。如果你看不到CPU 100%使用一个内核,那就是磁盘。修剪索引是什么意思?像是一种压实?我认为这是不可能的,但您可以尝试完全真空。当我运行脚本时,任务管理器中也没有100%运行脚本。CPU接近50%,磁盘在5-7%之间。通过所有优化和单次提交=0:02:13.202081通过所有优化并一次性提交每批100k=0:04:03.772711,通过使用新的列表机制和添加事务,没有多少收益。1.无事务原始列表机制=0:05:13.974366 2。无事务切片/del机制=0:05:05.689453 3。仅在事务和切片/删除列表机制中包装insert语句=0:07:27.693308 4。while循环和切片/删除列表机制之外的事务=0:05:07.213928
templist = urls[-self.bulk_insert_entries:]
del urls[-self.bulk_insert_entries:]
i = len(templist)