文档章节

缓存级别与缓存更新问题

Float_Luuu
 Float_Luuu
发布于 2017/03/19 18:06
字数 1931
阅读 1769
收藏 5

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton

缓存失效问题被认为是计算机科学中最难的两件事之一,这篇文章来自翻译,内容主要包括缓存级别与缓存更新常见的几种模式。

缓存应用模式

常见缓存应用模式

缓存常用来加快页面的加载速度,减少服务器或数据库服务的负载。缓存应用的常见模式如上图所示:

  1. 检索缓存,尝试查找之前相同请求的执行结果,如果找到了则返回,省去了重新执行的步骤;
  2. 如果缓存未命中,则重新执行计算逻辑并将结果保存至缓存;

数据库常常得益于对均匀分布的数据的读写,但是热点数据使得这种均匀被打破,从而出现了系统瓶颈。通过数据库服务前置缓存服务,可以有效吸收不均匀的负载和抵挡流量高峰。

缓存级别

缓存级别关注的问题是什么时候做缓存(When)以及什么地方做缓存(Where),下面介绍几种常见的缓存级别。

客户端缓存

缓存可以存储在客户端(操作系统或浏览器、服务端、或者是独立的缓存系统中。

CDN缓存

CDN也可以被认为是一种缓存。

Web服务器缓存

反向代理或者像Varnish这样的缓存服务可以直接保存静态的或动态的缓存内容。Web服务器也可以缓存请求直接响应客户端从而避免请求再次触达应用。

数据库缓存

我们的数据库服务在默认的配置或者稍微针对通用场景进行优化的情况下通常包含不同级别的缓存,针对特定的使用场景进行适当的调整可以进一步提高性能。

应用缓存

像Memcached和Redis这种内存key-value缓存服务,通常是置于应用和数据库服务之间,因为数据存储在内存中,因此这要比将数据存储在磁盘的数据库要快的多。但是内存与磁盘相比往往受限于空间,因此类似LRU(Least Recently Used)这种缓存淘汰算法应运而生,他们将相对较少访问的"冷"数据从内存置换出来将访问频率较高的“热”数据放入内存(将内存的使用价值最大化,译者注)。

Redis还有很多其他的功能,包括:

  • 持久化选项;
  • 内建数据结构(如sets、lists);

下面是针对数据库查询级别对象级别的一般缓存:

  • 行级缓存;
  • 查询级别缓存;
  • 完全序列化的缓存;
  • 渲染后的HTML缓存;

值得一提的是,我们通常要避免文件级别的缓存,因为基于文件的缓存常常难于扩展和维护。

查询级别缓存:

每当我们查询数据库的时候,将查询(比如SQL)进行hash并作为key和查询结果关联存储,这种方法会遇到缓存过期的问题:

  • 对于复杂的查询很难删除缓存的结果;
  • 缓存粒度较大,如果查询结果中只有丁点数据被更新,则整个查询都要过期;

对象级别缓存:

对象级别缓存是将数据看做对象:

  • 如果数据被修改则将数据从缓存中移除;
  • 使用异步的任务来更新缓存;

对象级别的缓存建议的使用场景:

  • 用户会话;
  • 渲染后的页面;
  • 活动流;
  • 用户图形数据;

缓存更新问题

因为内存受限于空间缓存只能存储有限的数据,因此我们需要决定在我们的应用场景中,使用何种缓存更新策略,下面介绍几种常见的模式。

Cache-Aside

Cache-Aside模式

应用负责基于存储读写数据,缓存不直接和存储打交道,应用的行为如下:

  1. 检索缓存,缓存没有命中;
  2. 从数据库加载数据;
  3. 将数据更新至缓存;
  4. 返回结果;

代码示例如下:

def get_user(self, user_id):

    user = cache.get("user.{0}", user_id)

    if user is None:

        user = db.query("SELECT * FROM users WHERE user_id = {0}", user_id)

        if user is not None:

            cache.set(key, json.dumps(user))

    return user

 

Memcached通常被应用于这种方式,这种模式对于接下来的数据读取将非常快,Cache-Aside也叫做延迟加载,只有需要的数据被缓存,避免不需要的数据占用缓存空间。

这种模式的缺点如下:

  • 每次缓存没命中都增加系统之间的交互,这将会增加响应延迟;
  • 当对应数据库中的数据被更新之后将出现脏数据问题,这个问题可以通过设置过期时间(TTL)来缓解,当时间过期将发生强制更新缓存;
  • 当一个节点坏了之后,新的节点代替旧的节点,这个时候将出现大量的缓存穿透问题;

Write-Though

Write-Though模式

应用将缓存作为主要存储,读写都直接和缓存打交道,缓存负责基于存储进行读写:

  1. 应用基于缓存添加或删除记录;
  2. 缓存同步地将记录写入存储;
  3. 返回;

应用代码示例:

set_user(12345, {"foo":"bar"})

缓存代码如下:

def set_user(user_id, values):

    user = db.query("UPDATE Users WHERE id = {0}", user_id, values)

    cache.set(user_id, user)

Write-Though对于所有的写操作都是比较慢的,但是对于读来说很快,用户通常需要容忍写延迟,但是不会出现脏数据。

这种模式的缺点如下:

  • 由于failure或者scaling带来的新增节点的时候,新增节点在下次更新数据之前将没有数据,这个问题可以结合Cache-Aside模式来缓解;
  • 对于很多写入的数据将永远不会读取到,这个问题可以通过设置过期时间解决;

Write-Behind(Write-Back)

 

Write-Behind模式

在这种模式下,应用的行为如:

  1. 直接读写缓存;
  2. 写操作通知任务来异步进行更新;

这种模式的缺点如下:

  • 如果在数据被更新到存储之前缓存挂了,则数据将会丢失;
  • 实现起来比Write-Though和Cache-Aside模式更为复杂;

Refresh-Ahead

Refresh-Ahead模式

我们可以配置缓存自动在最近访问的数据过期之前更新它们,如果可以准确预测将要访问的数据,Refresh-Ahead模式可以有效地减少读写的延迟。

这种模式的缺点如下:

  • 如果预测数据不准确,则比不做什么更有损性能;

缓存的缺点

一种解决方案通常会带来一些问题,我们来看看引入缓存带来的问题:

  • 缓存的引入带来了一致性问题,我们需要处理缓存中的数据与原数据不一致的问题;
  • 缓存的引入增加了软件架构的复杂性;;
  • 缓存过期是个难题,这个问题主要体现在何时更新缓存上;

扩展阅读

  • From cache to in-memory data grid
  • Scalable system design patterns
  • Introduction to architecting systems for scale
  • Scalability, availability, stability, patterns
  • Scalability
  • AWS ElastiCache strategies
  • Wikipedia

注:文中翻译如有错误,请多多包涵。

另外我开公众号啦,欢迎订阅:

© 著作权归作者所有

下一篇: MySQL统计信息
Float_Luuu
粉丝 220
博文 47
码字总数 104674
作品 0
长宁
高级程序员
私信 提问
hibernate二级缓存理解

缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写...

祥子哥哥
2014/07/08
982
0
hibernate中数据库的优化

3、list和iterate不同 a)list取所有 b)literate先取ID,等到用的时候根据ID取对象 c)session中list第二次发出,仍会到数据库查询。 d)iterate第二次,先找session级缓存。 4、一级缓存和...

dailei1021
2014/05/25
70
0
Hibernate缓存策略详解

Hibernate缓存策略 1. 数据缓存 1.1. 概述 缓存是数据库数据临时容器,它包含了库表数据的临时拷贝,位于数据库与数据访问层之间。 ORM 在进行数据读取时,会根据其缓存管理策略,首先在内存...

提广乾
2012/09/11
182
0
Mybatis一二级缓存实现原理与使用指南

Mybatis 与 Hibernate 一样,支持一二级缓存。一级缓存指的是 Session 级别的缓存,即在一个会话中多次执行同一条 SQL 语句并且参数相同,则后面的查询将不会发送到数据库,直接从 Session ...

丁威
09/23
0
0
全面讲解Hibernate缓存

在向大家详细介绍Hibernate二级缓存之前,首先让大家了解下一级缓存,然后全面介绍Hibernate二级缓存。 Hibernate中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的...

dong.li
2012/04/24
452
0

没有更多内容

加载失败,请刷新页面

加载更多

教你玩转Linux—添加批量用户

添加和删除用户对每位Linux系统管理员都是轻而易举的事,比较棘手的是如果要添加几十个、上百个甚至上千个用户时,我们不太可能还使用useradd一个一个地添加,必然要找一种简便的创建大量用户...

xiangyunyan
32分钟前
6
0
返回提示信息,如:xxx创建成功!

【服务端】在输出的方法块中,加入要输出的字段(qcm_batch_id) QCMUserType.cs: public struct QCM_Custom_Create_Batch_Out_Tag { public BASCoreType.Cmn_Out_T......

_Somuns
32分钟前
6
0
Aliyun Serverless VSCode Extension v1.12.0 发布

Aliyun Serverless VSCode Extension 是阿里云 Serverless 产品 函数计算 Function Compute 的 VSCode 插件,该插件结合了函数计算 Fun 工具以及函数计算 SDK ,是一款 VSCode 图形化开发调试...

阿里云官方博客
33分钟前
6
0
程序员如何培养解决复杂问题的能力?

今天在上网时候,突然看到了这篇文章,感觉非常的适合现在的自己去思考下,可能也适用在座的读者。程序员不仅仅是敲代码,更是一个复合能力的结合体,也不仅仅停留在技术和代码阶段。你想要成...

哥本哈根的小哥
37分钟前
8
0
市场变化驱动产品思维升级

宜信科技中心财富管理产品部负责人Bob,与大家一起聊聊个性化推荐产品功能的设计和B端产品的功能策划方式。 拓展阅读:回归架构本质,重新理解微服务 智慧金融时代,大数据和AI如何为业务赋能...

宜信技术学院
37分钟前
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部