【发布时间】:2019-07-25 12:15:20
【问题描述】:
情况
我正在使用带有内置 sqlite3 模块的 Python 3.7.2。 (sqlite3.version == 2.6.0)
我有一个如下所示的 sqlite 数据库:
| user_id | action | timestamp |
| ------- | ------ | ---------- |
| Alice | 0 | 1551683796 |
| Alice | 23 | 1551683797 |
| James | 1 | 1551683798 |
| ....... | ...... | .......... |
其中user_id 是TEXT,action 是任意的INTEGER,timestamp 是代表UNIX 时间的INTEGER。
数据库有 200M 行,有 70K 不同的user_ids。
目标
我需要制作一个 Python 字典,如下所示:
{
"Alice":[(0, 1551683796), (23, 1551683797)],
"James":[(1, 1551683798)],
...
}
具有user_ids 作为键和相应的事件日志作为值,它们是元组(action, timestamp) 的列表。希望每个列表都按timestamp 升序排序,但即使不是,我认为可以通过在制作字典后对每个列表进行排序来轻松实现。
努力
我有以下代码来查询数据库。它首先查询用户列表(user_list_cursor),然后查询属于该用户的所有行。
import sqlite3
connection = sqlite3.connect("database.db")
user_list_cursor = connection.cursor()
user_list_cursor.execute("SELECT DISTINCT user_id FROM EVENT_LOG")
user_id = user_list_cursor.fetchone()
classified_log = {}
log_cursor = connection.cursor()
while user_id:
user_id = user_id[0] # cursor.fetchone() returns a tuple
query = (
"SELECT action, timestamp"
" FROM TABLE"
" WHERE user_id = ?"
" ORDER BY timestamp ASC"
)
parameters = (user_id,)
local_cursor.execute(query, parameters) # Here is the bottleneck
classified_log[user_id] = list()
for row in local_cursor.fetchall():
classified_log[user_id].append(row)
user_id = user_list_cursor.fetchone()
问题
每个用户的查询执行速度太慢。每个user_id 的单行代码(注释为bottleneck)大约需要10 秒。我认为我对查询采取了错误的方法。实现目标的正确方法是什么?
我尝试使用关键字“按列分类 db”、“按列分类 sql”、“sql 日志到字典 python”进行搜索,但似乎没有什么符合我的情况。我认为这不会是一个罕见的需求,所以也许我缺少正确的关键字来搜索。
再现性
如果有人愿意用 200M 行的 sqlite 数据库重现这种情况,下面的代码将创建一个 5GB 的数据库文件。
但我希望有人熟悉这种情况并知道如何编写正确的查询。
import sqlite3
import random
connection = sqlite3.connect("tmp.db")
cursor = connection.cursor()
cursor.execute(
"CREATE TABLE IF NOT EXISTS EVENT_LOG (user_id TEXT, action INTEGER, timestamp INTEGER)"
)
query = "INSERT INTO EVENT_LOG VALUES (?, ?, ?)"
parameters = []
for timestamp in range(200_000_000):
user_id = f"user{random.randint(0, 70000)}"
action = random.randint(0, 1_000_000)
parameters.append((user_id, action, timestamp))
cursor.executemany(query, parameters)
connection.commit()
cursor.close()
connection.close()
【问题讨论】:
-
@Strawberry 谢谢,我的误会。从问题中删除了该短语。
-
你需要use indexes。
-
如果我在 PHP(我知道的唯一应用程序代码)中执行此操作,我只需选择按用户排序的整个数据集,然后解析结果数组。这肯定比为每个用户执行单独的查询要快。
-
@Solarflare:谢谢,现在性能提高到每秒 30-50
user_ids。现在我将尝试@Strawberry 的解决方案,看看它是否有帮助。 -
我试着读了整张表
ORDER BY user_id ASEC,但是好像滞后太多了,可能是因为太大了,放不下我的物理内存,所以用了几十GB的虚拟内存.我想我应该使用索引,尽管我希望它可以更快......@Solarflare,如果你想发布一个,我会接受你的回答。