查询缓存的实现
Implementation of QueryCache
除了直接匹配查询的空白规范化散列之类的东西之外,还有什么可能是一种有用的(但不一定是完美的)方式来以部分方式处理查询缓存?例如,让我们来看下面的基本情况:
SELECT
Product, # VARCHAR
Revenue # DOUBLE
FROM
Sales
WHERE
Country='US'
这可能被用作 'base-cache',可以在其上执行进一步的查询以潜在地提高性能:
SELECT
Product, # VARCHAR
Revenue # DOUBLE
FROM
Sales
WHERE
Country='US' AND State='CA'
因此,假设 from
table(s) 中的数据不变,以下内容可作为确定缓存的起点:
- fields: [field:type, ...] // 可以少但不能多
- 来自:table(s)+joins
的散列
- filters: [filter1, filter2, ...] // 可以少但不能多
- 聚合:[agg1, agg2, ...] // 可以少但不能多
- having: [having1, having2, ...] // 可以少但不能多
- order+limit+offset if limited result-set // 可以少但不能多
但是,当我们考虑以下情况时,这就变得非常棘手了:
SELECT
ProductGroup AS Product, # Would produce a Product:VARCHAR hash
Revenue
FROM
Sales
WHERE
Country='US'
对于如何实现部分查询缓存,什么可能是一个现实的起点。
用例: 写入 SQL 以查询非 DBMS 管理的源中的数据,例如 CSV 文件,这将需要 ~20 秒左右的时间来发出任何查询,我们无法在文件上创建索引。 https://en.wikipedia.org/wiki/SQL/MED 或类似 Spark。
我认为以下内容可能是基本缓存实现的良好起点,它允许使用可以进一步查询以进行优化的缓存:
- 首先替换任何 udf 或 cte。查询本身需要是独立的。
- 标准化空格和大写。
- 散列整个查询。这将是我们的起点。
- 删除 select 字段并对查询的其余部分进行哈希处理。现在将所有单个项目的散列存储在 select 列表中。
- 对于部分缓存,生成一个散列减去select个字段,where,sort,limit+offset。 hash where's list(用AND分隔),确保缓存中不包含当前查询不包含的filter,orderby,看数据是否需要重新排序,limit+offset数,使得确保初始查询中的限制+偏移量为空或大于当前查询。
下面是数据保存方式的示例:
Hash
673c0185c6a580d51266e78608e8e9b2
HashMinusFields
41257d239fb19ec0ccf34c36eba1948e
HashOfFields
[dc99e4006c8a77025c0407c1fdebeed3, …]
HashMinusFieldsWhereOrderLimit
d50961b6ca0afe05120a0196a93726f5
HashOfWheres
[0519669bae709d2efdc4dc8db2d171aa, ...]
HashOfOrder
81961d1ff6063ed9d7515a3cefb0c2a5
LimitOffset
null
现在让我们尝试几个示例,我将使用人类可读的哈希值以便于阅读:
SELECT Name, Age FROM Sales WHERE id=2
-- fullHash: selectname,agefromsaleswhereid=2
-- selectless: fromsaleswhereid=2
-- hashoffields: [name, age]
-- minusfieldswhereorderlimit: null
-- hashofwheres: [id=2, ]
-- hashororder: null
-- limitoffset: null
-- query1
select age FROM sales where id=2
-- selectless: fromsaleswhereid=2
-- fields: [age] OK, all fields contained in initial fields
-- query2
select age FROM sales where id=2 and country='us' order by id limit 100
-- minusfieldswhereorderlimit: null
-- hashofwheres: [id=2, country=us] OK initial query does not contain any additional filters
-- limitoffset: 100 OK initial limitoffset is null (infinity)
-- hashorder: orderbyid
--> Can grab partial cache, need to apply one filter and re-sort/limit:
--> SELECT * FROM <cache> WHERE country='us' order by id limit 100
以上看起来是有效的初始实施吗?
除了直接匹配查询的空白规范化散列之类的东西之外,还有什么可能是一种有用的(但不一定是完美的)方式来以部分方式处理查询缓存?例如,让我们来看下面的基本情况:
SELECT
Product, # VARCHAR
Revenue # DOUBLE
FROM
Sales
WHERE
Country='US'
这可能被用作 'base-cache',可以在其上执行进一步的查询以潜在地提高性能:
SELECT
Product, # VARCHAR
Revenue # DOUBLE
FROM
Sales
WHERE
Country='US' AND State='CA'
因此,假设 from
table(s) 中的数据不变,以下内容可作为确定缓存的起点:
- fields: [field:type, ...] // 可以少但不能多
- 来自:table(s)+joins 的散列
- filters: [filter1, filter2, ...] // 可以少但不能多
- 聚合:[agg1, agg2, ...] // 可以少但不能多
- having: [having1, having2, ...] // 可以少但不能多
- order+limit+offset if limited result-set // 可以少但不能多
但是,当我们考虑以下情况时,这就变得非常棘手了:
SELECT
ProductGroup AS Product, # Would produce a Product:VARCHAR hash
Revenue
FROM
Sales
WHERE
Country='US'
对于如何实现部分查询缓存,什么可能是一个现实的起点。
用例: 写入 SQL 以查询非 DBMS 管理的源中的数据,例如 CSV 文件,这将需要 ~20 秒左右的时间来发出任何查询,我们无法在文件上创建索引。 https://en.wikipedia.org/wiki/SQL/MED 或类似 Spark。
我认为以下内容可能是基本缓存实现的良好起点,它允许使用可以进一步查询以进行优化的缓存:
- 首先替换任何 udf 或 cte。查询本身需要是独立的。
- 标准化空格和大写。
- 散列整个查询。这将是我们的起点。
- 删除 select 字段并对查询的其余部分进行哈希处理。现在将所有单个项目的散列存储在 select 列表中。
- 对于部分缓存,生成一个散列减去select个字段,where,sort,limit+offset。 hash where's list(用AND分隔),确保缓存中不包含当前查询不包含的filter,orderby,看数据是否需要重新排序,limit+offset数,使得确保初始查询中的限制+偏移量为空或大于当前查询。
下面是数据保存方式的示例:
Hash | 673c0185c6a580d51266e78608e8e9b2 |
---|---|
HashMinusFields | 41257d239fb19ec0ccf34c36eba1948e |
HashOfFields | [dc99e4006c8a77025c0407c1fdebeed3, …] |
HashMinusFieldsWhereOrderLimit | d50961b6ca0afe05120a0196a93726f5 |
HashOfWheres | [0519669bae709d2efdc4dc8db2d171aa, ...] |
HashOfOrder | 81961d1ff6063ed9d7515a3cefb0c2a5 |
LimitOffset | null |
现在让我们尝试几个示例,我将使用人类可读的哈希值以便于阅读:
SELECT Name, Age FROM Sales WHERE id=2
-- fullHash: selectname,agefromsaleswhereid=2
-- selectless: fromsaleswhereid=2
-- hashoffields: [name, age]
-- minusfieldswhereorderlimit: null
-- hashofwheres: [id=2, ]
-- hashororder: null
-- limitoffset: null
-- query1
select age FROM sales where id=2
-- selectless: fromsaleswhereid=2
-- fields: [age] OK, all fields contained in initial fields
-- query2
select age FROM sales where id=2 and country='us' order by id limit 100
-- minusfieldswhereorderlimit: null
-- hashofwheres: [id=2, country=us] OK initial query does not contain any additional filters
-- limitoffset: 100 OK initial limitoffset is null (infinity)
-- hashorder: orderbyid
--> Can grab partial cache, need to apply one filter and re-sort/limit:
--> SELECT * FROM <cache> WHERE country='us' order by id limit 100
以上看起来是有效的初始实施吗?