Python中使用QSqlQuery操作SQLite出现性能问题该如何优化?
问题定位
性能瓶颈和QSql本身无关,核心是两个问题:1. SQLite默认自动提交事务的机制导致频繁刷磁盘;2. 你的代码存在逻辑bug,循环插入的记录量远多于实际数据量。
具体优化方案
1. 显式使用事务批量提交
SQLite默认每执行一条exec()语句就自动开启并提交一个独立事务,每次事务提交需要触发磁盘持久化同步,耗时是毫秒级,上千次提交就会达到分钟级耗时。
只需要在插入操作开始前开启事务,所有插入完成后统一提交即可:
query = QSqlQuery() # 插入逻辑开始前开启事务 query.exec("BEGIN;") try: # 原有所有插入逻辑放在这里 ... # 全部插入完成后统一提交 query.exec("COMMIT;") except Exception as e: # 出错时回滚事务 query.exec("ROLLBACK;") # 原有异常处理逻辑 ErrorLogger.WriteError(traceback.format_exc()) ...
2. 修复去重逻辑的严重bug
你当前把temp_phone_list和temp_email_list定义在了遍历联系人的循环外面,每次处理新联系人时都会把之前所有联系人的号码、邮箱追加到列表中,导致插入的号码、邮箱和关联表数据呈指数级增长,这也是168条联系人跑15分钟的核心原因之一。
把两个临时列表的初始化放到循环内部即可:
for contact in contacts: temp_phone_list = [] temp_email_list = [] # 后续处理逻辑不变
3. 替换手动查询主键为API直接获取
不需要每次插入后执行SELECT max(Id)查询主键,QSqlQuery自带lastInsertId()方法可以直接返回刚插入记录的主键,减少大量无效查询:
# 插入person表后直接拿id,不需要额外查询 query.exec("INSERT INTO person (forename, lastname, mergedAddress) VALUES (?, ?, ?)", [contact.FirstName, contact.LastName, contact.Address]) contact_id = query.lastInsertId().toInt()[0]
4. 使用预处理语句替代字符串拼接
你当前用f-string拼接SQL不仅有SQL注入风险,遇到带单引号的联系人姓名、地址等数据还会直接执行失败,同时执行效率也低于预处理语句。改用预处理绑定参数的写法:
# 可以提前prepare复用查询对象,进一步提升效率 query.prepare("INSERT INTO phonenumbers (phonenumber) VALUES (?)") for number in phone_list: query.addBindValue(number) query.exec() phone_id = query.lastInsertId().toInt()[0] # 插入关联表同理
以上优化全部做完后,168条联系人的插入耗时会降到秒级甚至毫秒级。
内容的提问来源于stack exchange,提问作者accpert.com
相关产品推荐
相关产品推荐

