四种常用缓存技巧
Posted 码农小助手
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四种常用缓存技巧相关的知识,希望对你有一定的参考价值。
与你一起成长
背景
1.什么是缓存
在使用缓存前我们应该了解什么是缓存,下面是维基百科上面对于缓存的定义:
缓存是在计算机上的一个原始数据的复制集,以便于访问
缓存在计算机系统中被广泛应用,从缓存的定义来看,缓存是计算机上的原始数据的复制集,因此对于缓存的使用与应用场景密切相关,在不同的场景上会有不同的意义。
2.为什么使用缓存
首先对于互联网应用来说,用户体验是非常重要的,而使用缓存的目的是想通过提高服务的性能从而提高应用的用户体验。
而系统性能的指标一般包括:响应时间、延迟时间、吞吐量、并发用户数量和资源利用率等几个方面。
吞吐量:系统在单位时间内处理的请求的数量
3.缓存的分类
对于java程序开发者来说,根据缓存在软件系统中所处的位置的不同,缓存大体可以分为三类:
客户端缓存
服务端缓存
网络中缓存
下面主要针对客户端缓存和网络中缓存简单说明,并对服务端缓存做详细说明
3.1客户端缓存
对于BS架构的互联网应用来说客户端缓存主要分为页面缓存和浏览器缓存两种,对于APP而言主要是自身所使用的缓存。
3.2网络中缓存
网络中的缓存主要是指代理服务器对客户端请求数据的缓存,主要分为WEB代理缓存和边缘缓存(CDN边缘缓存)
3.3服务端缓存
对于服务端缓存而言,从系统的架构上面区分可以将缓存分为
服务器本读缓存(localCache)
分布式缓存(Redis、Memcached等nosql)
数据库缓存
3.3.1 服务器本地缓存
本地缓存是一级缓存,位于服务本机的内存中,在操作本地缓存的时候不需要网络IO不需要文件IO,直接从本机内存中读取数据,因此读写速度最快。
本地缓存存在的问题:
本读缓存数据直接保存在JVM中,需要考虑缓存数据的大小、JVM的垃圾回收性能消耗
单服务是集群部署的时候,应该考虑是否需要做集群中本地缓存的数据同步
在实际的开发中可以自己实现简单的本地缓存也可以使用开源的本地缓存框架,比如:ehcache、JBoss Cache等
3.3.2分布式缓存
当本地缓存被穿透的时候就会去查询分布式缓存,当在分布式缓存中查询到数据的时候,直接将查询结果放到本地缓存中。
对于分布式缓存主要是使用NoSQL数据库来实现,常用的NoSQL数据库有Redis、Memcached、MongoDB等。目前比较流行的Redis来说,支持Slava/Master模式和Cluster
3.3.3缓存中的几个常用术语
1.缓存命中:当客户端请求的数据在缓存中,这个缓存中的数据就会被使用,这一行为被称为缓存命中
2.没有命中:缓存中没有查询到数据,并且数据库中可以查到此数据,并将数据放到缓存中
3.缓存穿透:是指查询一个缓存中一定不存在的数据。即缓存中不存在,并且数据库中也不存在,并且在数据库中没有查询到数据的情况下,不会去写缓存,这样就导致每次对于此数据的查询都会去查询数据库,这样就导致缓存失去了意义。对于如何解决缓存穿透问题,后面会具体分析。
4.存储成本:缓存没有命中的时候,从其他数据源取出数据并放到缓存中的时间成本和空间成本就是存储成本。
5.缓存失效:当缓存中的数据已经更新时,则此数据已经失效
6.替代策略:当缓存没有命中的时,并且缓存容量已满,就需要在缓存中去除一条旧数据,然后加入一条新数据,而应该去除哪些数据,就由替代策略来决定。
常用的替代策略有:LRU、LFU等。在使用缓存算法的时候,通常会考虑使用频率、获取成本、缓存容量和时间等因素。
8.缓存雪崩
3.3.4使用云服务提供的缓存服务
国内的阿里云扽提供商提供Redis的云服务器,这些服务有以下特点:
1.动态扩容:通过后台配置升级Redis存储空间
2.数据多备,数据存储在一主一备中
3.自动容灾:主机宕机后系统自动检测,并切换到备机上,实现了服务的高可用
3.3.5数据库缓存
数据库在设计的时候也有缓存操作,更改相关参数开启查询缓存
简介
在项目中,经常会遇到处理高并发的情况,缓存是应对高并发的有效手段之一。使用缓存有一个前提:数据不是实时变更的。本文将介绍常用的四种方案。
一、CDN
CDN一般用来做静态资源的缓存,稍微修改,便可用来缓存动态接口的返回。当请求资源在CDN不存在时,请求会到回源机上,在回源机的nginx上,根据路由规则,将请求转发到不同服务,返回结果会存在到CDN上。
Nginx实现
upstream fs.com_backend {
server 127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=10s;
check interval=3000 rise=3 fall=3 timeout=1000 type=tcp default_down=false;
}
upstream hd.com_upstream {
server 127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=10s;
check interval=3000 rise=3 fall=3 timeout=1000 type=tcp default_down=false;
}
server {
listen 8080;
server_name aaa.d2s.com;
charset utf-8;
access_log /home/work/logs/nginx/d2s.com.log web;
error_log /home/work/logs/nginx/d2s.com.log.err;
error_page 403 404 500 502 503 504 http://hd.com/webfile/zt/hd/2014042802/tw.html;
expires -1;
location ~ /.git {
deny all;
access_log off;
log_not_found off;
}
location ~ /([^/]*)/openbuy/info {
expires 300s;
proxy_set_header Host "****";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://fs.com_backend;
}
location ~ /([^/]*)/eventapi/ {
rewrite /(.*)/eventapi/(.*) /$1/$2 break;
expires 300s;
proxy_set_header Host "****";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://hd.com_upstream;
}
}
注意
流量:正常情况下,回源的请求会比较少,但某些情况下,会导致请求激增,如市场在google上推了广告,请求后面会添加很多数据,如果CDN没有做处理,这些请求都会穿透CDN打到回源机上,导致请求暴增。如果服务性能有限,不但会导致该服务受影响,该Nginx上的其他功能可能也会受影响。
稳定性:因为请求会转发到服务上,如果服务不稳定,返回的数据有误或者服务500了,Nginx没做任何判断便返回,会导致一段时间内,用户请求的数据全是错误的。这种情况一旦发生,反馈到开发的时候,因为CDN缓存失效,重新获取了正确数据,此时症状已经消失,开发很难定位问题。建议在Nginx上添加相关正确性的校验。
二、Nginx缓存
Nginx也能缓存返回的结果,而且由于Nginx本身能够支持较高的并发,且易于配置,所以使用Nginx缓存是一个及其方便有效的手段。
Nginx部分实现
server {
listen 80;
#listen 445;
server_name abc.com;
access_log /home/work/logs/nginx/abc.com.log albproxy;
error_log /home/work/logs/nginx/abc.com.err;
#include vhosts/black.ini;
error_page 403 404 500 502 503 504 http://promo.com/webfile/globalweb/$local/404.html;
if ($app_uri ~* "^(hello/getstock.*)"){
set $cache_rewrite "nginxcache3s";
}
location ~* ^/[^/]*/nginxcache3s/.* {
proxy_pass_header X-Accel-Expires;
rewrite ^/([^/]*)/nginxcache3s/(.*)$ /$1/$2 break;
proxy_cache cache_one;
proxy_cache_valid 200 3s;
proxy_cache_key $host$uri;
proxy_ignore_headers Expires Cache-Control;
proxy_set_header Host $host;
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
proxy_set_header X-Real-IP $remote_addr;
real_ip_recursive on;
proxy_set_header X-Forwarded-For $http_x_forwarded_for;
#后端标识ssl请求
proxy_set_header Missl $sslparam;
proxy_set_header LOGID $logid;
proxy_pass http://web_backend;
}
}
注意
慎重使用:使用Nginx缓存确实很方便,也可以迅速提高接口性能,可以说是居家旅行必备良药。不过这些操作一般需要运维同学帮忙操作,如果频繁使用该方法,一是占用了运维很多时间做这种重复性工作,二是操作的过程中可能出现各种问题,三是会导致Nginx文件很繁杂,后期迁移和维护成本会较高。
三、 Redis
Redis是处理高并发情况的必备方案之一。一般的处理过程如下:
注意
缓存穿透
如果Redis没有数据,获取数据的时候也没有数据,这个时候,如果没有将结果写入Redis,则每次请求都会穿透Redis。建议
获取数据为空的情况下,也存数据
增加基础校验
缓存击穿
如果有个key为热点key,请求qps特别高,当该key失效的时候,大量的请求会到逻辑层,导致服务不稳定,建议
热点数据设置为永不过期。可以使用脚本等方案异步更新数据。
加互斥锁。如果key不存在,则只允许一个请求进入逻辑层。
缓存雪崩
如果大量key在同一时间过期,所有请求都会到逻辑层,导致服务不稳定,建议
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中
热点数据设置为永不过期
四、MemoryCache
MemoryCache是内存缓存,一般在同一台机器上安置服务和MemoryCache,将MemoryCache和Redis并用。虽然Redis性能极高,但终归还需要通过请求去获取数据,MemoryCache直接从本地内存获取数据,速度更加快速。
当Redis中没有数据,是否需要获取数据和再次写入Redis,需要根据具体业务情况来设计。例如如果Redis是异步写入的,则没必要去执行计算数据的逻辑,也没必要再次写入Redis。
注意
数据丢失风险
Memeory Cache是内存缓存,没有持久化机制,重启后数据就没有了。
OOM
Memeory Cache使用不规范,容易造成大量内存消耗,产生OOM问题
存储数据变更风险
在Golang中,如果存储的数据为指针结构,存储操作完成后,指针指向的数据更改,则Memeory Cache中的数据也会被更改
总结
本文介绍了常用的四种方案,CDN、Nginx缓存、Redis、MemoryCache,灵活组合使用这四个等级的方案,可以有效的处理高并发问题。
end
看完本文有收获?请转发分享给更多人
关注「码农小助手」,提升技能
每天记得对自己说:你是最棒的!
“转发和在看是对作者最大的支持”
以上是关于四种常用缓存技巧的主要内容,如果未能解决你的问题,请参考以下文章
FPGA技巧篇四FPGA设计的四种常用思想与技巧之四 :数据同步化