介绍
我正在构建一个缓存系统,其中缓存的每个节点都可以从具有 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');
此操作将影响带有参数的查询#1 ['foo']
,并且所有依赖于具有这些参数的查询的缓存节点都应该失效。
UPDATE users
SET birth_date = '1990-01-01'
WHERE id = 'foo';
此操作不会影响查询 #1,因为它不依赖列birth_date
来构建其结果。
DELETE FROM users
WHERE id = 'bar';
['bar']
即使在操作后没有行与查询 #1 匹配,这也会影响带有参数的查询 #1。
第一个解决方案
我想出的解决方案有效,但肯定需要改进。
- 对于数据库上的每个操作,跟踪一组受影响的行和列:
INSERT
:考虑插入的行及其所有列
UPDATE
:考虑更新之前和之后的行,仅包含更新的列。您最终得到 2 行
DELETE
:考虑删除之前的已删除行及其所有列 - 对于在步骤 1 中找到的每一行,找出所有可能受到影响的查询。这是我今天做大量体力工作的地方。我目前正在手动列出每个查询的所有依赖项。示例
Q1
:
const dependencies = [
{
table: 'users',
columns: ['id', 'name'],
getParams: (row) => [[row.id]],
}
]
需要注意的一些有趣的事情:
- 一个查询在使用join时可能会依赖多个表,所以dependencies是一个数组
- 我列出了查询所依赖的列,因此可以跳过其他列的更新
- 通过查看表和列,我们知道行会影响查询
- 我们需要根据行找到参数集。
结果是一个数组,因为一行可能会影响具有多个参数集的同一查询。在这个基本示例中,数组的长度仅为 1,因为该行使用 1 个参数集影响查询。
现在考虑以下查询:
UPDATE users
SET id = 'bar'
WHERE id = 'foo';
基于步骤 1,我们构建两行:
{ id: 'foo' }
: 更新前行的值{ id: 'bar' }
: 更新后行的值
请注意,两行都只有id
列,因为我们只更新了这一列。现在查看我们在上面构建的依赖项数组,我们知道两行都会影响查询Q1
,因为表匹配,并且列重叠(它们都有id
列)。
要找到参数集,我需要getParams
为每一行调用并将结果展平:
[['foo'], ['bar']]
.
就是这样。我们现在使所有依赖于Q1
参数集['foo']
或的缓存节点无效['bar']
。
开放式问题
我正在寻找我可能忽略的任何其他路线。最重要的是,我正在寻找一种自动构建每个查询的依赖关系的方法,手动完成是缓慢、困难且容易出错的。