【问题标题】:Rails app - populating the db cache and keeping it populatedRails 应用程序 - 填充数据库缓存并保持填充
【发布时间】:2021-03-31 15:52:10
【问题描述】:

我有一个在 Ubuntu 上运行的 Rails 5.2 应用程序,它与一个包含超过 5000 万条记录的大型 MySQL 数据库配合使用。

用户在数据库上执行搜索。在可接受的时间范围内执行特定搜索。

但是,默认是完整的通配符搜索,以返回第一个(例如 50 条)记录,按一个或多个字段排序(排序)。

默认通配符搜索可能需要相当长的时间,但是一旦执行,结果就会被缓存,重复通配符搜索非常快。

通常结果仍然缓存,但有时缓存会被刷新,并且必须再次执行搜索,这又需要很长时间。缓存总是在早上刷新,我将其归因于服务器/数据库在夜间备份,或者云服务器实例以某种方式刷新。

好的,我想,我将运行一个定期(每小时)运行的 cronjob 并重新填充缓存。所以这就是我所做的,并且我很小心地实际执行搜索(不仅仅是创建一个实际上没有执行的 Active Record 查询)。

cronjob 调用模型中的一个方法来执行数据库查询(并丢弃结果)。我可以从日志中看到执行了搜索。

不幸的是,这种填充缓存的尝试似乎并没有使提取相同数据的 HTTP RESTful API 受益。一段时间内的第一个通配符查询总是需要很长时间,即使最近运行了一个 cronjob 填充缓存任务。但是,同样,一旦 RESTful API 搜索完成,相同查询的后续重复会很快返回缓存的响应。

也许 RESTful 查询未被识别为与在 cronjob 中执行的相同搜索?

关于如何填充缓存并保持填充的建议表示赞赏。

更新

Rails 生成的用于查询的 SQL,

SELECT  `products`.*
    FROM  `products`
    WHERE  `products`.`category` = 0
      AND  `products`.`available` = TRUE
    ORDER BY  LENGTH(prefix) ASC,
              LENGTH(numbers) ASC,
              LENGTH(suffix) ASC,
              `products`.`prefix` ASC,
              `products`.`numbers` ASC,
              `products`.`suffix` ASC
    LIMIT  20 OFFSET 0 

架构的相关部分,

create_table "products", force: :cascade do |t|
    t.integer "category"
    t.string "prefix"
    t.string "numbers"
    t.string "suffix"
    t.boolean "available", default: false
    t.integer "user_id"
    t.integer "price"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "open_sale", default: false
    t.index ["category", "prefix", "numbers", "suffix"], name: "index_products_category_prefix_numbers_suffix"
  end

【问题讨论】:

  • 请向我们展示所涉及的 SQL 以及架构 (SHOW CREATE TABLE)
  • @RickJames 按要求添加。谢谢!

标签: mysql ruby-on-rails


【解决方案1】:

由于它是一个特定的查询,我只考虑一种特殊情况,并为此使用 rails 的缓存。

def index
  @records =
    if condition_to_detect_that_wildcard_search
      Rails.cache.fetch("some_cache_key", expires_in: 1.month) do
        do the query
      end
    else
      do the query
    end
end

如果您最终需要更改缓存值,可以使用一些全局可访问的变量。

【讨论】:

  • @arieljoud 非常有趣,我会调查并报告,谢谢。
  • @arieljoud 这真的很有帮助也很有趣,但我试过了,但没有用。不过感谢您的建议和见解。
  • 感谢您的贡献。我并没有完全解决一天中第一次加载需要很长时间的问题,但我确实学到了很多关于 Rails 缓存的知识。
【解决方案2】:

首先,您必须具体说明您所谈论的 cahe。您的 sql db 中有缓冲区缓存,这意味着当您执行查询时,需要扫描的数据页将从磁盘分页到您的内存中。那么在后面的查询中,假设缓存的页面还在内存中,那么查询时间会很短。

您提到您按小时进行搜索查询,这并不总是有帮助,因为缓存的数据页面可能会被驱逐以让位于后续查询所需的其他数据页面。如果发生这种情况,当下一个搜索查询发生时,您的 sql db 将为这些页面产生磁盘 IO,并且速度会很慢。

解决此问题的一种方法是您仍然运行 cron,但这次您需要将结果加载到您的缓存存储中,例如 Redis。

def reload_cached_search
  Rails.cache.write(“cache-key”, search_query_result)
end

这种方法与上面的答案类似,但您将有更好的控制,因为您正在主动刷新缓存,并且您的用户搜索不太可能调用数据库查询。

【讨论】:

  • 感谢您的贡献。我并没有完全解决一天中第一次加载需要很长时间的问题,但我确实学到了很多关于 Rails 缓存的知识。
【解决方案3】:

该查询需要一个索引 category, available 开头(以任意顺序)。但更重要的是,如果你可以去掉ORDER BYLENGTH(..) 部分,那么这将允许一个更好的索引,从而消除对缓存的任何需求:

INDEX(category, available, prefix, numbers, suffix)

可能当前设置必须每次都扫描整个表。根据我的建议,

WHERE  `products`.`category` = 0
  AND  `products`.`available` = TRUE
ORDER BY  `products`.`prefix` ASC,
          `products`.`numbers` ASC,
          `products`.`suffix` ASC
LIMIT  20 OFFSET 0 

只会触及 20 行。

我需要查看“通配符”问题的示例,在 SQL 中,而不是在 Rails 中。有了这个,我也许可以提出改进建议。

【讨论】:

  • 感谢您的贡献。我并没有完全解决一天中第一次加载需要很长时间的问题,但我确实学到了很多关于 Rails 缓存的知识。
猜你喜欢
  • 1970-01-01
  • 2013-01-17
  • 1970-01-01
  • 1970-01-01
  • 2014-12-25
  • 2015-05-07
  • 2013-02-28
  • 1970-01-01
  • 2020-05-09
相关资源
最近更新 更多