缓存简单介绍

  缓存 - 是一种典型的以牺牲数据时效性换取访问性能的技术,是中大型架构中应用最为广泛的技术。主要有以下两类:

  • 静态缓存: 主要应用在Web应用中,提供网站访问性能。
  • 动态缓存:数据库热点数据、session缓存以及动态页面缓存中。

缓存使用场景

静态缓存

  一般是指Web类应用中,将HTML,JS,CSS等静态资源通过磁盘/内存等缓存方式进行存储,从而提高资源响应方式,减少服务器压力/字眼开销。(经典代表产品-CDN)

方法一:浏览器缓存

   浏览器缓存,也称为客户端缓存,是静态缓存中最常见、最直接的表现形式。

我们会经常在Nginx配置文件中看到如下的缓存配置:

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
	expires 1d;
}
location ~ .*\.(js|css)?$
{
	expires 15d;
}复制代码

以往JSP文件中也会看到如下配置:

<meta http-equiv="expires" content="60">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">复制代码

  上面介绍的两种配置客户端缓存的方式都是无需去服务端验证(nginx的优先级大于代码中设置的),直接通过浏览器自身是否过期即可,所以不会产生额外的流量。
  此种方法非常适合不经常变动的资源。

方法二:磁盘缓存

   磁盘缓存是指将静态资源文件通过磁盘进行缓存的技术。

# levels 设置目录层次
# keys_zone 设置缓存名字和共享内存
# inactive 在指定时间内无人访问则被删除,在这里是1天
# max_size 最大缓存空间
proxy_cache_path /montos/www/default/cache_dir/ levels=1:2 keys_zone=cache_one:200m inactive=1d max_size30g

server {
listen       80;
server_name  _;
location /{
proxy_pass http://***.***.***.***:8080;
proxy_set_header   Host             $host;
proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
}

location ~ .*\.jsp$
{
proxy_cache cache_one;                    # keys_zone后的内容对应
proxy_cache_valid  200 304 301 302 10d;   #哪些状态缓存多长时间
proxy_cache_valid  any 1d;                #其他的缓存多长时间
proxy_cache_key $host$uri$is_args$args;   #通过key来hash,定义KEY的值

proxy_pass http://***.***.***.***:8080;
proxy_set_header   Host             $host;
proxy_set_header   X-Real-IP        $remote_addr;
proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
}
access_log  /montos/nginx/logs/default-cache.log;
}复制代码

   可以从上面的配置中看出,Nginx主要通过proxy_cache来实现的,而且不仅仅能实现静态文件的缓存,还可以实现动态文件的缓存。

方法三:磁盘缓存

   内存缓存就是把静态文件缓存在服务器端的内存中。

varnish -f default.vcl -s malloc,2G -a 0.0.0.0:80 -w 1024,51200,10 -t 3600 -T ***.***.***.***:3500复制代码

上述是一段Varnish的启动命令。配置信息介绍如下:

  • -a address:port 监听端口
  • -f 指定配置文件
  • -s 指定缓存类型malloc为内存,file为文件
  • -t 默认ttl
  • -T address:port 管理端口
  • -w 最小线程,最大线程,超时时间

default.vcl的核心配置如下:

sub vcl_fetch{
    if(req.request == "GET" && req.url ~ "\.(gif|jpg|jpeg|png|bmp|swf|html)$"){
          set obj.ttl = 3600s;
    }
}复制代码

  通过上面的配置,我们从请求头中X-Cache中可以查看是否命中缓存。

方法四:CDN

   CDN已经包含了上述三种所代表的的静态缓存。

  对于CDN的介绍,相信小伙伴们对此都是或多或少的了解。目前大多数公司都是结合OSS来实现静态资源的存放以及缓存。
   通过OSS的资源存储以及CDN的回源,我们可以很快的将前端发布的静态资源进行一个迭代,让用户从最近距离的CDN获取到最新的资源。

动态缓存

  动态缓存用于缓存业务中动态数据。

场景一:数据库缓存

   数据库缓存即将磁盘中部分数据放置缓存中,便于下次访问直接从缓存中取出。

数据库缓存拥有以下技术特点:

  • 性能优越
      缓存主要为了提高性能,相对于数据放在磁盘中读取,从缓存中读取是更具有优势。
  • 应用场景
      对于日常业务中,对于数据库而言绝大部分流量压力都是来源于查询,增、删、改这三个操作占据了一点点,所以此时上了缓存,更更有效的减少数据库的压力。
  • 数据一致性
      用牺牲空间来换取服务的高访问,此时就会出现数据不一致的问题,而出现这种问题我们就需要想好解决方案去维护这两者的数据一致性。一定要求是实时性的数据我们就可以考虑不应该从缓存中读取。
  • 高可用
      同时也要满足缓存的高可用,避免缓存血崩的情况发生,在一定时间内对数据库造成庞大的压力。

场景二:集中Session管理

  分布式架构中,文件存储可由OSS、FastDfs等完成,而会话技术则更偏向于负载均衡方面

主要由以下几种方案完成:

  1. 基于源IP的会话保持
      负载均衡识别客户端请求的源IP地址,将同一地址转发到同一台后端服务器处理。
  2. 基于浏览器Cookie的会话保持
      将会话植入客户端的Cookie中,然后负载均衡根据根据这个来选择后端对应服务器。
  3. 基于数据库存放Session
      统一将用户登录之后的会话保存在数据库中,能够解决分布式架构下会话不统一的问题,然而随之带来数据库方面压力。
  4. 基于动态缓存存放Session
      通过缓存统一存放会话,能够很好的解决会话不一致的问题,也能解决上述数据库的压力。
  5. 基于Tomcat集群Session共享
      通过配置Tomcat集群也能够达到这样的效果,但是只是针对于当前的集群中。
  6. 基于NAS等文件共享
      基于文件共享系统也是能够完成会话统一的情况,不过也会增加磁盘I/O方面的问题。

场景三:动态页面缓存

  动态页面一半都会涉及动态计算、数据库缓存、数据库操作等,所以对于数据及时性要求比较高的则不太适合使用。

  • 通过Nginx配置
      通过内置的Proxy模块的proxy_cache实现。
      通过内置的MEMcache模块实现。
      通过第三方模块memc-nginx和srcache-nginx构建高透明的缓存机制。

上述的三种方法都是通过Nginx的插件形式完成,作为动态页面缓存目前项目中可以说很少见,大多数都是基于动态数据的缓存。

场景四:分布式锁

  在大型的业务场景中,为了解决单一数据被操作的问题,引入了一个分布式锁来控制解决。(这里仅谈Redis实现)

通过Redis来实现分布式锁主要有以下几类:

  • 基于Redis自实现
  • RedLock
  • Redission

缓存总结

  缓存是一种以牺牲数据及时性换取访问性能的技术,同时也增加了空间成本、数据维护成本等,如何更好的使用好缓存还是根据自己的业务特性进行一个度量。