Redis 开发笔记2
Redis缓存穿透、缓存击穿、缓存雪崩解决方案与实现伪代码
缓存穿透
描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方案:
1.接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
2.从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
缓存击穿
描述:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
1.设置热点数据永远不过期。
2.加互斥锁
说明:
- 1.缓存中有数据,就直接返回数据
- 2.缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。
- 3.当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。
缓存雪崩
描述:
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。
和缓存击穿不同的是缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。设置热点数据永远不过期。
redis 分布式唯一id
问题:
MySQL的自增id在分库分表场景下,实现全局唯一id
解决:
1.基于雪花算法生成的id 过于依赖系统本地时间 (不适应场景放弃)
2.利用redis 单线程实现全局唯一id (使用)
实现思路:
yyyMMddHHmmss(14位) + 5位自增数字(前面不够5位的补0),组成19位不重复的数字。
yyyMMddHHmmss作为key存到redis,设置过期时间为2s,
在同一个key的时候,后5为从1自增,也就是说,同一秒内最大支持99999个请求,可以满足需求了。
redis缓存与数据库一致性问题
描述:
缓存和数据库之间数据一致性问题常用于缓存处理的机制有以下几种
Cache Aside 模式
这种模式处理缓存通常都是先从数据库缓存查询,如果缓存没有命中则从数据库中进行查找。
流程:
缓存命中:当查询的时候发现缓存存在,那么直接从缓存中提取。
缓存失效:当缓存没有数据的时候,则从 database 里面读取源数据,再同步到 cache 里面去
缓存更新:当有新的写操作去修改database 里面的数据时,需要在写操作完成之后,让cache 里面对应的数据失效,作缓存同步。
缺陷:
造成脏数据or导致分布式环境下出现 数据一致性问题:
一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,
让缓存失效,然后之前的那个读操作再把旧的数据放进去,所以会造成脏数据。
Write Through 模式
描述:
是指应用程序始终从缓存中请求数据。如果缓存没有数据,则它负责使用底层提供程序插件从数据库中检索数据。检索数据后,缓存会自行更新并将数据返回给调用应用程序
优点:
使用Read Through 有一个好处,我们总是使用key 从缓存中检索数据, 调用的应用程序不知道数据库,由存储方来负责自己的缓存处理,这使代码更具可读性,代码更清晰
缺陷:
开发人员需要编写相关的程序插件,增加了开发的难度性。
Write Behind Caching 模式
描述:
这种模式通常是先将数据写入到缓存里面,然后再异步的写入到database 中进行数据同步。
优点:
这样的设计既可以直接的减少我们对于数据的database 里面的直接访问,降低压力,同时对于database 的多次修改可以进行合并操作,极大的提升了系统的承载能力
缺点:
这种模式处理缓存数据具有一定的风险性,例如说当cache 机器出现宕机的时候,数据会有丢失的可能。