【发布时间】:2017-03-10 17:31:27
【问题描述】:
一大堆文本文件(A、B 和 C 类型)放在我的胸口,缓慢而冷酷地拒绝我急需的空气。多年来,每个类型规范都有增强功能,因此昨天的 typeA 文件比去年的 typeA 具有更多的属性。为了构建一个能够处理这些文件类型十年来的长期演变的解析器,迭代地、冷静地检查所有 1400 万个文件是有意义的,但在它们被压死之前。
我构建了一个运行计数器,这样每次我看到属性(熟悉与否)我都会在其计数中加 1。 sqlite 计数板如下所示:
在特殊事件中,我看到一个不熟悉的属性,我将它们添加到计数中。在一个看起来像这样的 typeA 文件上:
我把这个系统搞砸了!但是在一个过程中@ 3M 个文件/36 小时很慢。最初我使用this trick 向sqlite 传递需要递增的属性列表。
placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for dummy_var in properties)
sql = """UPDATE tally_board
SET %s = %s + 1
WHERE property IN (%s)""" %(type_name, type_name, placeholders)
cursor.execute(sql, properties)
我知道这是个坏主意,因为
-
sqlite字符串搜索比索引搜索慢很多 - 数百个属性(大约 160 个字符长)构成真正长 sql 查询
- 使用
%s而不是?是不好的安全做法...(不是问题 ATM)
“修复”是维护脚本端 property-rowid 此循环中使用的计数的哈希:
- 为
new_properties读取文件 - 阅读
tally_board获取rowid、property - 从 2 的读取中生成脚本端
client_hash - 为每个
new_property而不是property写入行到tally_board(还没有增加)。使用新属性更新client_hash - 使用
client_hash查找rowid中new_properties中的每一行 - 将增量写入每个
rowid(现在是property的代理)到tally_board
第 6 步。看起来像
sql = """UPDATE tally_board
SET %s = %s + 1
WHERE rowid IN %s""" %(type_name, type_name, tuple(target_rows))
cur.execute
这个问题是
- 还是很慢!
- 它在并行处理中表现出一种竞争条件,只要线程 A 在线程 B 完成第 6 步之前开始第 2 步,就会在
property列中引入重复项。
竞争条件的解决方案是在 db 上为步骤 2-6 提供排他锁虽然看起来读取无法获得这些 Lock A Read。
另一次尝试uses a genuineUPSERT 一举增加预先存在的property 行并插入(和增加)新的property 行。
something like this 可能有运气,但我不确定如何重写它以增加计数。
【问题讨论】:
标签: python python-3.x sqlite multiprocessing