对于一个研究项目,我创建了一个存储新闻文章的 sqlite 数据库。目前该数据库大小为 272GB,存储在 2TB 的云中。我的云计算机具有 32 核和 128GB RAM,并附加到此卷。
我正在运行以下查询:
"select * from articles where year={} and source in {}"
其中我将“{}”替换为年份和大约 6 个来源。
运行此查询大约需要 1 小时,数据库会生成大约 45 万行(总共 9000 万行)。执行此操作时,CPU 使用率实际上为 0%。
表是这样创建的:
"create table if not exists articles(source_id TEXT, source TEXT, day INTEGER, month INTEGER, year INTEGER, program_name TEXT, transcript TEXT, parliament INTEGER, top1_topic INTEGER, top1_acc REAL, top2_topic INTEGER, top2_acc REAL, top3_topic INTEGER, top3_acc REAL, emotionality_nrc REAL, emotionality_liwc REAL, subject_codes TEXT, PRIMARY KEY (source_id, day, month, year, program_name));"
我已经分别索引了来源和年份。
查询说明为:
QUERY PLAN`--SEARCH articles USING INDEX idx_articles_on_year_source (year=? AND source=?)
我在数据库存储的目录下进行了ioping测试,得到:
--- . (ext4 /dev/vdb) ioping statistics ---
99 requests completed in 31.1 ms, 396 KiB read, 3.18 k iops, 12.4 MiB/s
generated 100 requests in 1.65 min, 400 KiB, 1 iops, 4.04 KiB/s
min/avg/max/mdev = 157.4 us / 314.5 us / 477.6 us / 76.8 us
以及以下 fio 测试 fio --randrepeat=1 --ioengine=libaio --direct=1 --gtod_reduce=1 --name=fiotest --filename=testfio --bs=4k --iodepth=64 --size=8G --readwrite=randrw --rwmixread=75
给出了这个结果:
read: IOPS=10.8k, BW=42.3MiB/s (44.4MB/s)
write: IOPS=3619, BW=14.1MiB/s (14.8MB/s)
我还尝试了诸如``PRAGMA synchronous=OFF``之类的东西以及不同的日志,例如内存和WAL。
我有点不明白为什么数据库这么慢以及我应该做什么来提高速度。我在设置中犯了一个愚蠢的错误还是基础设施不好?我应该切换到数据仓库解决方案,例如 amazon redshift?
PS:我通过 python sqlite3 库连接到数据库并使用以下代码
def select_articles_by_year_and_sources(self, year, sources=None):
cur = self.conn.cursor()
rows = cur.execute(select_articles_by_year_and_sources_query.format(year, sources))
return iter(ResultIterator(rows))
conn = db.NewsDb(path_db) # connect to database
articles = list(conn.select_articles_by_year_and_sources(year, sources))
conn.close()
我刚刚尝试将 8GB 文件从附加卷复制到我的虚拟机。使用 bash cp 命令花了 2 分钟 30 秒。我猜这意味着附加卷的带宽非常慢?
最佳答案
您的查询计划显示您的 WHERE
子句中的两列 - year
和 source
- 正在使用索引,因此您可能无法加快速度。不过,根据数据的分布,有可能不是在 articles(year, source)
上建立索引,而是在 articles(source,year)
上建立索引通过更快地修剪更多行来获得更好的效果。
您可以尝试添加该新索引,然后运行 ANALYZE
在数据库上生成有关索引的统计信息,SQLite 使用这些统计信息来选择它认为效果更好的几个可能索引中的哪一个。之后检查 EXPLAIN QUERY PLAN
输出,看看它是否正在使用新索引或仍在旧索引上,然后删除未使用的索引(或者在实践中使用新索引是否较慢) ,删除那个)。
另一个选项是使用 sqlite3 command line program's .expert
command ,它会为查询生成索引建议,看看在这种情况下会产生什么结果。
关于python - SQLite 数据库在非常简单的查询下运行速度非常慢。我怎样才能提高性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72826139/