简介
在项目中,大家经常会遇到处理高并发的情况,缓存是应对高并发的有效手段之一。这篇文章简单介绍一下常用的缓存手段。当然,使用缓存有一个前提:数据不是实时变更的。
方法
一、CDN
CDN一般用来做静态资源的缓存,但是稍微进行修改,便可以用来缓存动态接口的返回。当请求资源在CDN不存在时,请求会到回源机上,在回源机的Nginx上,根据路由规则,将请求转发到不同服务,返回结果会存在到CDN上。如下图所示:
Nginx实现
upstreamfs.com_backend {
server127.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;
}
upstreamhd.com_upstream {
server127.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{
listen8080;
server_nameaaa.d2s.com;
charsetutf-8;
access_log/home/work/logs/nginx/d2s.com.log web;
error_log/home/work/logs/nginx/d2s.com.log.err;
error_page 403 404500 502503 504http://hd.com/webfile/zt/hd/2014042802/tw.html;
expires-1;
location~/.git {
denyall;
access_logoff;
log_not_foundoff;
}
location~/([^/]*)/openbuy/info {
expires300s;
proxy_set_headerHost "****";
proxy_set_headerX-Forwarded-For$remote_addr;
proxy_passhttp://fs.com_backend;
}
location~/([^/]*)/eventapi/ {
rewrite/(.*)/eventapi/(.*)/$1/$2 break;
expires300s;
proxy_set_headerHost "****";
proxy_set_headerX-Forwarded-For$remote_addr;
proxy_passhttp://hd.com_upstream;
}
}
注意事项
注意流量
正常情况下,回源的请求会比较少,但某些情况下,会导致请求激增,如市场在google上推了广告,请求后面会添加很多数据,如果CDN没有做处理,这些请求都会穿透CDN打到回源机上,导致请求暴增。如果服务性能有限,不但会导致该服务受影响,该Nginx上的其他功能可能也会受影响。
注意稳定性
因为请求会转发到服务上,如果服务不稳定,返回的数据有误或者服务500了,Nginx没做任何判断便返回,会导致一段时间内,用户请求的数据全是错误的。这种情况一旦发生,反馈到开发的时候,因为CDN缓存失效,重新获取了正确数据,此时症状已经消失,开发很难定位问题。建议在Nginx上添加相关正确性的校验。
二、Nginx缓存
Nginx也能缓存返回的结果,而且由于Nginx本身能够支持较高的并发,且易于配置,所以使用Nginx缓存是一个及其方便有效的手段。
Nginx部分实现
server{
listen80;
#listen 445;
server_nameabc.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 404500 502503 504http://promo.com/webfile/globalweb/$local/404.html;
if($app_uri ~*"^(hello/getstock.*)"){
set$cache_rewrite "nginxcache3s";
}
location~*^/[^/]*/nginxcache3s/.* {
proxy_pass_headerX-Accel-Expires;
rewrite^/([^/]*)/nginxcache3s/(.*)$ /$1/$2 break;
proxy_cachecache_one;
proxy_cache_valid200 3s;
proxy_cache_key$host$uri;
proxy_ignore_headersExpires Cache-Control;
proxy_set_headerHost $host;
set_real_ip_from10.0.0.0/8;
real_ip_headerX-Forwarded-For;
proxy_set_headerX-Real-IP$remote_addr;
real_ip_recursive on;
proxy_set_headerX-Forwarded-For$http_x_forwarded_for;
#后端标识ssl请求
proxy_set_headerMissl $sslparam;
proxy_set_headerLOGID $logid;
proxy_passhttp://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,灵活组合使用这四个等级的方案,可以有效的处理高并发问题。大家如果有其他方案,欢迎一起讨论。
本章的图片,大家可以通过https://www.processon.com/view/link/5ec7e780f346fb69070fe20a查看
最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
https://mp.weixin.qq.com/s/xElsNUjxiT0MYZuVJct6Gw
有疑问加站长微信联系(非本文作者)