高并发下的缓存风险点和使用
缓存是为了提高应用服务的响应时间。是一种空间换时间典型应用。
1.一个缓存只干一件事。
2.确保组装redis的key与将要缓存的数据的查询条件一致。
在网关使用reids的流程是先查缓存,缓存不存在,则RPC或者操作数据库,取回数据,再放入缓存。
缓存的key与查询数据条件不一致,缓存不仅一点意义没有,还会导致脏数据,产生一些莫名奇妙的bug。
1.缓存种类
1.Jdk自带
线程不安全HashMap,LinkedHashMap,LRUMap
线程安全ConcurrentHashMap,CopyOnWriteList,CopyOnWriteSet
2.第三方包
Gauva 线程安全,自带过期检查,自带更新。
Ehcache 可落地,重启数据可保留,hibernate中使用。
3.第三方服务
Redis 目前使用中。多种数据结构。可落地。
Memcache 数据结构单一,只有key string。不可落地。
2.目前使用情况
1.序列化为JSON。
简单易用,速度快,纯业务信息。
100W次
序列化 382 279
反序列化用时 282 292
2.将对象序列化为二进制字节数据。
转化万物,包括类信息占用内存多,耗时较多
100W次
序列化 2376 2544
反序列化用时 11352 11411
对明确第三方服务有缓存的情况下,网关可以不加缓存。
如果第三方有缓存,网关也加缓存,不仅增加工作量,还会增加代码逻辑,增大出错机率。比如。。。。
网关调用一次第三方有缓存的rpc,会经过以下几个步骤:
1.网关序列化。
2.网络传输。
3.第三方服务反序列化请求参数。
4.第三方读缓存,分二种情况
jvm,纯内存操作,耗时可以忽略不计。
redis,会有一次网络传输。
5.第三方序列化请求结果。
6.网络传输。
7.网关反序列化请求结果。
整个流程耗时在2-5ms,接口性能非常优秀。
3.高并发
1. JVM
HashMap LinkedHashMap纯读可以使用。
LRUMap 最近最少淘汰,可以自定义大小。
ConcurrentHashMap 多读多写多线程。
CopyOnWriteList,CopyOnWriteSet 读多写少多线程,写时会加锁复制原有数组,性 能较差。
Guava 同ConcurrentHashMap ,自带刷新,过期时间,自动加载。网关现在在用。
EhCache 功能强大,支持落地。
2. Redis
Redis是单线程,异步非阻塞 IO,多路复用模式,纯内存操作的外部缓存。
所谓的单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。
redis采用多路复用机制:即多个网络socket复用一个io线程,实际是单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流.
所以,我认为用redis作缓存与JVM缓存耗时上,主要是序列化与反序列化以及内部网络传输上的消耗。
缓存穿透:
可以防止对恶意攻击。
后端没有的数据,缓存中自然没有。supplierId=-1,suId=-1
1. 布隆过滤器,维护一个大map,空间换安全,从而避免了对底层存储系统的查询压力。(没用过)
2. 自定义规则,比如在网关对suId进行校验,spuId+XXXXXXXX,长度不够,自然 就是无效key。
2.如果某个key返回的数据为空,仍然缓存key,设置比较短的过期时间,比如10 秒,20秒。网关现在使用这种方式,但不是所有的缓存都有这个逻辑。
缓存击穿
主要保护下游戏系统,数据库,第三方服务。
缓存过期一瞬间,大量请求过来,都去请求后端服务。比如某个排行榜。
有并发,但不高,比如几十次。就是多几十次后端查询,多set几十次值。
如果并发很高,后端服务就完蛋了。
1.代码加锁,单服应用可以,多服依然可能会产生<N-1(应用实例数)次后端请求。
2.setnx加锁,获得锁的请求去后端请求数据,然后放入redis。切记这个锁一定要 加过期时间。
缓存雪崩
主要保护下游戏系统,数据库,第三方服务。
一组业务关联的缓存同一时间失效。比如suid缓存与suppliderId缓存。
预热缓存,定时刷新缓存。比如UserInfo,SupplierInfo,ProductInfo。
1.多级缓存,jvm缓存 + redis,jvm缓存+rpc(这是另一种实现和redis无关)。
2.过期时间+随机增量,比如5分钟失效,5*60 + random(0,60),主要思想还是把请 求分散,有点像请求限流+限流预热
网友评论