如何使用缓存模式避免数据库查询风暴

jam*_*con 5 caching appfabric

我们正在使用 PostgreSQL 数据库和 AppFabric 服务器,运行一个中等繁忙的 ASP.NET MVC 电子商务站点。

遵循缓存侧模式,我们从缓存中请求数据,如果不可用,则查询数据库。

这种方法会导致“查询风暴”,即数据库在短时间内收到对同一数据的多个查询,同时正在刷新缓存中的给定对象。更长时间运行的查询会加剧这个问题,显然对同一数据的多个请求会导致查询运行更长时间,形成令人不快的反馈循环。

此问题的一种解决方案是对缓存使用读锁定。然而,这本身可能会导致在 Web 场情况下(甚至在单个繁忙的 Web 服务器上)的性能问题,因为 Web 服务器在读取时被无故阻止,以防发生数据库查询。

另一种解决方案是放弃缓存侧模式并独立地为缓存设定种子。这是我们为缓解我们在此问题上看到的直接问题而采取的方法,但并非所有数据都可以这样做。

我在这里错过了什么吗?人们采取了哪些其他方法来避免这种行为?

STW*_*STW 1

根据您拥有的服务器数量和当前的缓存架构,可能值得评估添加服务器级(或进程内)缓存。实际上,您可以将其用作后备缓存,并且在访问主存储(数据库)资源非常密集或速度很慢的情况下特别有用。

当我使用它时,我对主缓存使用了缓存旁模式,对辅助缓存使用了读通设计——其中辅助缓存锁定并确保数据库不会因同一请求而过度饱和。使用这种架构,主缓存未命中会导致每个服务器(或进程)的每个实体最多对数据库进行一次查询。

所以基本的工作流程是:

1)尝试从主/共享缓存池中检索

* If successful, return
* If unsuccessul, continue
Run Code Online (Sandbox Code Playgroud)

2) 检查进程内缓存的值

* If successful, return (optionally seeding primary cache)
* If unsuccessul, continue
Run Code Online (Sandbox Code Playgroud)

3)通过缓存键获取锁(并仔细检查进程内缓存,以防它被另一个线程添加)

4)从主持久化(db)中检索对象

5) 进程内缓存种子并返回

我已经使用可注入包装器完成了此操作,我的缓存层都实现了相关IRepository接口,并且 StructureMap 注入了正确的缓存堆栈。尽管相当复杂,但这使实际的缓存行为保持灵活、集中且易于维护。