我被要求改进现有代码来查询 SQLite 数据库。原始代码对数据库进行了大量单独的调用,并在 Python 中过滤了结果。相反,我选择重写数据库创建并将过滤逻辑放入 SQL 查询中。
在不同大小的数据库上运行基准测试后。与原始实现相比,我发现n=3
新实现中查询的平均查询时间要快得多(3 秒 vs 46 秒)分钟)。我怀疑这是一个缓存问题,但我不确定其根源。在每次查询之间,我关闭了数据库连接并删除了所有残留的 Python 变量并运行,gc
但这个世界之外的情况仍然存在。然后我发现很可能是系统缓存了一些东西。事实上,当我在每次迭代后清除系统缓存时,echo 3 > /proc/sys/vm/drop_caches
性能更符合我的预期(2-5 倍的速度提升与 80.000 倍的速度提升相比)。
我现在遇到的近乎哲学的问题是我应该报告为改进:缓存性能(按原样)或非缓存性能(在查询之前显式删除缓存)。 (我可能会报告两者,但我仍然对缓存的内容感到好奇。)我认为这归结为实际缓存的内容的问题。换句话说:缓存是否代表现实世界的场景,或者根本不代表。
我认为,如果数据库或其索引被缓存,那么快速的默认性能可以很好地代表现实世界,因为它将适用于新的、看不见的查询。但是,如果缓存特定查询,则缓存的性能不会反映在未见过的查询上。
注意:这可能是一个不重要的细节,但我发现在使用 fts5 虚拟表时,这种缓存的影响尤其明显!
Tl;dr:当系统将查询缓存到 SQLite 时,它到底缓存了什么,这会对新的、未见过的查询产生积极影响吗?
如果重要的话:带有 sqlite3 的 Ubuntu 20.04。
答案1
不,它不缓存查询。它缓存页面。
数据库将表(以及索引)保存在页中。每页包含表格的一行到多行。一旦页面进入缓存,任何需要该页面中的行的查询都可以使用它。
索引也是如此:如果新查询有限制somefield between 20 and 40
- 数据库引擎首先查看其缓存 - 缓存是否包含该索引的页面来描述该值范围?
页面大小是在创建新数据库时定义的。查看 pragma 的文档页面大小关于限制以及如何使用它。
这种方法不仅允许在不相关的查询之间共享页面,甚至还允许在不同的连接之间共享页面。以下是有关其工作原理的文档:https://www.sqlite.org/sharedcache.html