我正在将一个应用程序从 MySQL 5.6 迁移到 MariaDB 10。MariaDB 的一个主要新特性是子查询缓存。在启用和测试它之前,我试图确定这个缓存对我们的应用程序的预期影响。在“实施”部分下,知识库文章是这样说的:
每个子查询缓存都会创建一个临时表,用于存储结果和所有参数。它对所有参数都有一个唯一的索引。首先在 MEMORY 表中创建缓存(如果这样做是不可能的,则该表达式的缓存将被禁用)。当表增长到 tmp_table_size 和 max_heap_table_size 中的最小值时,将检查命中率:
- 如果命中率真的很小(<0.2)缓存将被禁用。
- 如果命中率适中 (<0.7) 将清理表(删除所有记录)以将表保留在内存中
- 如果命中率高,表将转换为磁盘表(5.3.0 只能转换为磁盘表)。
我要确定的第一件事是“每个子查询缓存”的确切含义。由于它声明缓存是在临时表中创建的,这意味着它们必须限定在当前连接范围内。所以我的猜测是每个连接创建一个 subquery_cache 表。(我怀疑每个查询都有自己的缓存,因为这样关于缓存“增长”的行就没有意义了。)但这只是一个猜测;我找不到更多细节。也许这些临时表确实以某种方式在连接之间共享,这与常规临时表不同。或者每个连接可能有多个缓存。
任何人都可以确认创建的子查询缓存的数量和范围吗?
我的下一个问题是是否有调整缓存的选项。由于我们正在从一个优化的 MySQL 应用程序转移,我们目前没有任何查询广泛使用可缓存子查询(因为这样的查询在 MySQL 下会非常慢)。虽然我们确实使用了大量子查询,但不是以可能导致大量缓存命中的方式。鉴于此,我希望启用缓存只会在每个子查询的临时表创建和查找以及用于存储缓存表的内存中产生开销(显然不能与主要的 tmp_table_size 和 max_heap_table_size 变量分开调整). 鉴于我希望发现我们当前存在的应用程序在不启用缓存的情况下效率更高。理想的情况是它支持类似 '
由于在任何地方都没有记录此类功能,我希望它们不存在,但如果有人知道为什么不存在,或者我们可能期望它们存在的时间,那就太好了。
缓存需要返回与运行子查询相同的结果,因此范围也必须受到活动事务的限制。
但是看到缓存只能作为优化器功能启用或禁用,而不能以其他方式配置,我的猜测是它实际上为优化器认为值得的查询的每个特定可缓存子查询实例化一次。
这似乎可以通过以下方式得到证实:“它对所有参数都有一个唯一的索引。” - 如果您在同一张表中存储多个子查询的结果,这是不可行的。
所以在我看来,缓存为每个可缓存的子查询实例化一次,并且它的生命周期将受到外部查询执行的限制。
编辑:我检查了一些源代码(免责声明:我绝对不完全理解这些代码,因为 MariaDB 是一个相当大且复杂的软件,而我对 C++ 的了解远非专家水平)。缓存由
Expression_cache_tmptable
. 用于存储缓存结果的表是在从某些方法调用的init()
方法Item_cache_wrapper
中创建的,并且各种Item的 AFAIK 生命周期是单个(外部)查询执行,因为不同类型的Item保留和管理查询的不同部分(如果我理解正确的话,有点像表达式树)。由此我推断,在“准备”执行查询时,为每个可缓存的子查询创建了一个子查询缓存实例。然后在查询执行期间首次访问它时对其进行初始化(并创建临时表)(因此仅当子查询实际至少执行一次时)。然后在 200 次未命中(可能是在处理了大约 200 多行外部查询之后)之后检查“<0.2”的条件(独立地针对每个子查询),以查看缓存是否有任何积极影响,并提前禁用它如果它没有帮助。然后,如果缓存表增长过多,则根据手册中列出的条件再次检查。
所以子查询缓存是不可调整的,但似乎尽可能不被侵入。优化器将永远为至少在理论上可以使用它的子查询实例化它,如果它没有被证明有用,它将在查询执行的早期被禁用。
如果您仍然担心它会减慢您的查询速度,您可以通过配置全局禁用它
optimizer_switch
,然后通过调用仅为选定的查询启用它在查询执行之前,然后可以选择将其关闭。