【问题标题】:Implementation of QueryCache查询缓存的实现
【发布时间】:2021-12-18 07:22:47
【问题描述】:

除了直接匹配查询的空白规范化哈希之类的东西之外,还有什么可能是一种有用(但不一定完美)的方式来以部分方式处理查询缓存?例如,让我们以以下基本情况为例:

SELECT
    Product,  # VARCHAR
    Revenue   # DOUBLE
FROM
    Sales
WHERE
    Country='US'

这可能被用作“基础缓存”,可以在其上执行进一步的查询以潜在地提高性能:

SELECT
    Product,  # VARCHAR
    Revenue   # DOUBLE
FROM
    Sales
WHERE
    Country='US' AND State='CA'

因此,假设from 表中的数据没有变化,以下可能作为确定缓存的起点:

  • fields: [field:type, ...] // 可以少但不能多
  • 来自:表的哈希+连接
  • filters: [filter1, filter2, ...] // 可以少但不能多
  • 聚合:[agg1, agg2, ...] // 可以少但不能多
  • have: [have1, 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。

【问题讨论】:

  • 您是否在使用任何现实世界的 DBMS(您忘记标记),这是否应该解决现实世界的性能问题?那么您可能只想在(country)(country, state) 上创建索引(或者甚至包括(country, product, revenue)(country, state, product, revenue) 之类的投影列(或者也包括productgroup 或者为此设置一组额外的索引))并完成。
  • @stickybit 不是真的,它更多的是用于查询非托管源,例如 CSV/Parquet/等。来自数据库的文件(类似于联合数据访问,其中查询需要很长时间并且没有索引选项)。
  • 好的。但是为什么不能创建索引而是缓存呢?
  • @stickybit 缓存是应用层。即,我们远程存储缓存数据,因此我们甚至根本不必点击文件。
  • 正如我想象的那样,您正在编程所说的应用层?那你也可以在那里实现(覆盖)索引?

标签: sql algorithm caching query-cache


【解决方案1】:

我认为以下可能是基本缓存实现的一个很好的起点,它允许使用可以进一步查询以进行改进的缓存:

  1. 首先替换任何 udf 或 cte。查询本身需要是独立的。
  2. 规范化空格和大写。
  3. 散列整个查询。这将是我们的起点。
  4. 删除选择字段并对查询的其余部分进行哈希处理。现在将所有单个项目的哈希存储在选择列表中。
  5. 对于部分缓存,生成一个哈希减去选择字段、where、sort 和 limit+offset。对where's list(用AND分隔)进行hash,确保缓存中不包含当前查询中不包含的filter,orderby,看数据是否需要重新排序,limit+offset个数,使得确保初始查询中的 limit+offset 为 null 或大于当前查询。

以下是数据保存方式的示例:

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

以上看起来像是一个有效的初始实现吗?

【讨论】:

    猜你喜欢
    • 2014-09-06
    • 2020-01-23
    • 1970-01-01
    • 2012-05-23
    • 2014-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多