从预定义查询列表中查找受INSERT、UPDATE或DELETE影响的SQL查询



简介

我正在构建一个缓存系统,其中缓存的每个节点可以从一组预定义的、有限的、具有0-n个参数的SQL查询中调用任意数量的SQL查询。

根据这些查询的结果,节点执行相当慢的计算并返回缓存的值。

查询可能看起来像:

查询#1:

SELECT name 
FROM users 
WHERE id = ?;

查询#2:

SELECT email 
FROM emails 
WHERE deleted_at IS NULL AND user_id = ?;

其他查询可能使用联接,没有或有多个参数,但查询的数量是有限的。

我跟踪每个节点调用的查询和参数集,并构建一个依赖关系列表。然后,当查询结果发生变化时,我知道我需要使所有依赖它的缓存节点无效,并重新计算它们的值。

问题的核心

现在最困难的部分是知道当我执行INSERT、UPDATE或DELETE时,哪些查询和参数集会受到影响。

示例

INSERT INTO users ("id", "name") 
VALUES ('foo', 'John');

此操作将影响具有参数['foo']的查询#1,并且所有依赖于具有这些参数的查询的缓存节点都应该无效。

UPDATE users 
SET birth_date = '1990-01-01' 
WHERE id = 'foo';

此操作不会影响查询#1,因为它不依赖列birth_date来生成结果。

DELETE FROM users 
WHERE id = 'bar';

这将影响具有参数['bar']的查询#1,即使在操作之后没有行与查询#1匹配。

第一个解决方案

我提出的解决方案是可行的,但肯定需要改进。

  1. 对于数据库上的每个操作,请跟踪受影响的一组行和列:
    INSERT:考虑插入的行及其所有列
    UPDATE:考虑更新前和更新后的行,仅考虑更新后的列。最终得到2行
    DELETE:在删除行之前先考虑已删除的行及其所有列
  2. 对于步骤1中找到的每一行,查找可能受到影响的所有查询。这就是我今天做很多体力活的地方。我目前正在手工列出每个查询的所有依赖项。Q1示例:
const dependencies = [
{
table: 'users',
columns: ['id', 'name'],
getParams: (row) => [[row.id]], 
}
]

需要注意的一些有趣的事情:

  • 使用联接时,查询可能依赖于多个表,因此依赖关系是一个数组
  • 我列出了查询所依赖的列,因此可以跳过对其他列的更新
  • 通过查看表和列,我们知道行会影响查询
  • 我们需要根据行找到一组参数
    结果是一个数组,因为一行可能会影响设置了多个参数的同一查询。在这个基本示例中,数组的长度仅为1,因为该行使用1个参数集影响查询

现在考虑以下查询:

UPDATE users 
SET id = 'bar' 
WHERE id = 'foo';

在步骤1的基础上,我们构建了两行:

  • { id: 'foo' }:更新前的行的值
  • { id: 'bar' }:更新后的行的值

请注意,这两行都只有id列,因为我们只更新了这一列。现在看看我们在上面构建的依赖项数组,我们知道这两行都会影响查询Q1,因为表匹配,列重叠(它们都有id列)。

为了找到参数集,我需要为每一行调用getParams并将结果压平:[['foo'], ['bar']]

就是这样。我们现在使所有依赖于Q1的缓存节点失效,参数设置为['foo']['bar']

未决问题

我正在寻找我可能忽略的其他路线。最重要的是,我正在寻找一种自动构建每个查询的依赖关系的方法,手动操作速度慢、困难且容易出错。

在另一种可能的方法中,我建议您检查是否可以直接使用RDBMS,您的RDBMS是否具有结果缓存能力。一些RDBMS可以被询问SQL查询的结果缓存状态,从而直接为您提供一种方法来了解缓存条目是否仍然有效,而无需解析DML语句。还为至少一个RDBMS提供了查询对象依赖关系,这可能对自动构建依赖关系很有用。

Pro:

  1. 它可以一个查询一个查询地完成,或者对于一堆查询,RDBMS为您完成任务
  2. RDBMS可以处理更复杂的情况
  3. 可扩展
  4. 可靠。RDBMS很少出错
  5. 您只需要在使用结果缓存状态选项执行查询时获得查询的结果缓存id

缺点:

  1. 一个大的。您至少需要向RDBMS提交一个缓存询问查询以进行查询。这意味着网络I/O和延迟
  2. 您需要配置/调整RDBMS以使用结果缓存(通常,当RDBMS具有此功能时,默认情况下会启用它)
  3. 在负载较重的RDBMS上,一些查询结果不会不时缓存,这意味着相关的缓存条目将无效,从而增加RDBMS上的负载
  4. 注意结果缓存的限制,带有时间戳或序列引用的查询通常会从结果缓存中排除
  5. 结果缓存无效策略可能无法满足您的需求(缓存过期、非细粒度无效等)

对于第一个缺点,处理它的一个好方法是在检查一堆查询的缓存结果之前,检查RDBMS上最后一次DML执行(例如通过审计表)。仍然会有损失(I/O延迟),但至少它可以最大限度地减少RDBMS和缓存层的负载(不可靠,这可能会引发竞争条件。如果您在T有一个查询,则从T(delta2)的审计中获得T(delta1)缓存项,而DML可能发生在T(delta 1)

T(delta 2)为了说明这一点,您可以在OracleRDBMS 11+上提交一个带有/*+ RESULT_CACHE */提示的EXPLAIN PLAN语句,以获得查询的结果缓存id。然后,您可以在v$result_cache_objects中查询该缓存id,TYPEResult,并检查STATUS列。如果它与Published(InvalidateSync等)不同,或者缓存id已更改,则可以使缓存条目无效。这也意味着,当你填充或刷新查询时,你需要在缓存项中获取并存储查询的查询缓存id。在用/*+ RESULT_CACHE */

执行查询后,你应该立即获取查询的结果缓存id,并从查询DBMS块中获取,所以你必须使用能够返回这些数据的SQL客户端/接口
此处的文档。。

使用SQLServer,AFAIK,直到今天,还没有结果缓存功能,SQLServer将缓存应用于其输出缓冲区,因此它不允许这种使用。

相关内容

  • 没有找到相关文章

最新更新