缓存的认识
# 缓存的认识
# 常见缓存分类
缓存分类可以分为客户端缓存、网络端缓存、服务端缓存。
# 客户端缓存
客户端缓存又可以细分为页面缓存、浏览器缓存、APP缓存。
页面缓存:页面自身对某些元素或全部元素进行存储,并保存成文件。
html5:Cookie、WebStorage(SessionStorage和LocalStorage)、WebSql、indexDB、Application Cache等等
开启步骤: 1、设置manifest描述文件
CACHE MANIFEST #comment js/index.js img/bg.png
2、html关联manifest属性
<html lang="en" manifest="demo.appcache">
使用LocalStorage进行本地的数据存储,示例代码:
localStorage.setItem("Name","zhangsan") localStorage.getItem("Name") localStorage.removeItem("Name") localStorage.clear()
浏览器缓存:当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。可分为强制缓存和协商缓存。
强制缓存:直接使用浏览器的缓存数据,条件:Cache-Control的max-age没有过期或者Expires的缓存时间没有过期
<meta http-equiv="Cache-Control" content="max-age=7200" /> <meta http-equiv="Expires" content="Mon, 20 Aug 2010 23:00:00 GMT" />
协商缓存:服务器资源未修改,使用浏览器的缓存(304);反之,使用服务器资源(200)。
<meta http-equiv="cache-control" content="no-cache">
APP缓存:原生APP中把数据缓存在内存、文件或本地数据库(SQLite)中,比如图片文件。
# 网络端缓存
网络端端缓存主要有web代理缓存以及边缘缓存。
- web代理缓存:可以缓存原生服务器的静态资源,比如样式、图片等。常见的反向代理服务器:Nginx。
- 边缘缓存:典型的商业化服务就是CDN了。CDN的全称是Content Delivery Network,即内容分发网络。CDN通过部署在各地的边缘服务器,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。现在一般的公有云服务商都提供CDN服务。
# 服务端缓存
服务器端缓存是整个缓存体系的核心。包括数据库级缓存、平台级缓存和应用级缓存。
- 数据库级缓存:数据库是用来存储和管理数据的。常见如MySQL:在Server层使用查询缓存机制。将查询后的数据缓存起来。InnoDB存储引擎中的buffer-pool用于缓存InnoDB索引及数据块。
- 平台级缓存:指的是带有缓存特性的应用框架。比如:GuavaCache 、EhCache、OSCache等。部署在应用服务器上,也称为服务器本地缓存。
- 应用级缓存:具有缓存功能的中间件:Redis、Memcached、EVCache、Tair等。采用K-V形式存储,利用集群支持高可用、高性能、高并发、高扩展。适用于分布式缓存。
# 优缺点
# 优点
- 提升用户体验:缓存的使用可以提升系统的响应能力,大大提升了用户体验。
- 减轻服务器压力:客户端缓存、网络端缓存减轻应用服务器压力。服务端缓存减轻数据库服务器的压力。
- 提升系统性能:对系统响应时间、延迟时间、吞吐量、并发用户数和资源利用率等有较大提升。
# 缺点
- 额外的硬件支出:缓存是一种软件系统中以空间换时间的技术,需要额外的磁盘空间和内存空间来存储数据。搭建缓存服务器集群需要额外的服务器(采用云服务器的缓存服务就不用额外的服务器了)
- 高并发缓存失效:在高并发场景下会出现缓存失效(缓存穿透、缓存雪崩、缓存击穿),造成瞬间数据库访问量增大,甚至崩溃
- 缓存与数据库数据同步:缓存与数据库无法做到数据的时时同步,Redis无法做到主从时时数据同步
- 缓存并发竞争:多个redis的客户端同时对一个key进行set值得时候由于执行顺序引起的并发问题
# 读写模式
缓存有三种读写模式:旁路缓存、直读/写模式、后写缓存模式。
# 旁路缓存
旁路缓存(Cache Aside Pattern),最经典的缓存+数据库读写模式。
即读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。更新的时候,先更新数据库,然后再删除缓存。
为什么是删除缓存,而不是更新缓存呢?
- 缓存的value,如果是hash、list结构,那么更新数据需要遍历
- Redis使用懒加载,使用的时候才更新缓存。(也可以采用异步的方式填充缓存)
- 高并发脏读的原因,以下是三种情况:
- 先更新数据库,再更新缓存。在这种情况下,有可能读取到的是在更新缓存前的脏数据。
- 先删除缓存,再更新数据库。这种情况,有可能由于缓存中没有数据,只能从DB中读取,但是DB中数据未更新。
- 先更新数据库,再删除缓存(推荐)。这种情况,能保证读取到事物提交前,Redis读取的是老数据;提交后,缓存删除,下次访问,可以建立新的缓存。
# 直读/写模式
直读/写模式(Read/Write Through Pattern),又称为穿透读模式/穿透写模式,指应用程序只操作缓存,缓存操作数据库。该种模式需要提供数据库的handler,开发较为复杂。
- 直读模式:应用程序读缓存,缓存没有,由缓存回源到数据库,并写入缓存。
- 直写模式:应用程序写缓存,缓存写数据库。
# 后写缓存模式
后写缓存模式(Write Behind Caching Pattern),指应用程序只更新缓存。
缓存通过异步的方式将数据批量或合并后更新到DB中,这种模式不能时时同步,甚至会丢数据。
# 缓存设计思路
缓存的整体设计思路有:
多层次:指在客户端、服务端这些层次搭建分布式缓存、本地缓存等等。
考虑数据类型:缓存大多数采用kay-value的模式,重点关注value的类型。如果是简单的类型,同时value值比较大,考虑用Memcached(纯内存,多线程);如果是复杂的数据类型,需要存储关系、聚合计算等等,考虑Redis(持久化,多类型,单线程NIO)
是否集群:Redis哨兵+主从、RedisCluster
缓存的数据结构设计:
缓存与数据库表一致:缓存的字段会比数据库表少一些,缓存的数据是经常访问的。例如用户表,商品表
与数据库表不一致:需要存储关系,聚合,计算等。比如某个用户的帖子、用户的评论。
以用户评论为例,DB结构如下:
ID UID PostTime Content 1 1000 1547342000 xxxxxxxxxx 2 1000 1547342000 xxxxxxxxxx 3 1001 1547342100 xxxxxxxxxx 如果要取出UID为1000的用户的评论,原始的表的数据结构显然是不行的。我们应做如下设计:
- key:UID+时间戳(精确到天),评论一般以天为计算单位
- value:Redis的Hash类型。field为 id和content
- expire:设置为一天