Redis
一.为什么用redis
使用redis主要考虑两个问题,性能和并发。
1.性能
在并发高的情况下,所有请求直接访问数据库,数据库会出现连接异常。用Redis做一个缓冲操作,让请求先访问Redis而不是数据库。
2.并发
二.redis优缺点
1.优点
①.性能优异
②.支持多种数据类型,操作方便。
③.支持数据持久化,支持AOF(append-only-file)和RDB快照(snapshotting)两种方式。
RDB:把当前内存中的数据集快照写入磁盘,恢复时将快照文件读到内存中。RDB分为自动触发和手动触发。
④.支持主从复制
2.缺点
①.不具备自动容错和恢复功能
②.存储的数据量和机器本身内存大小有关,需要提前预估和节约内存
③.单线程,单台服务器无法充分利用cpu
三.单线程redis为什么快
1.完全基于内存
redis的绝大部分请求都是基于内存的操作。
2.数据结构简单
redis的数据结构简单,对数据的操作也简单。
3.单线程
redis采用单线程,避免不必要的上下文切换和竞争条件,也不存在多进程或多线程导致的切换而消耗cpu,不需要去考虑各种锁的问题, 没有加锁、释放锁的操作,没有因为死锁而导致的性能消耗。
4.多路I/O复用模型,非阻塞IO
单个线程通过跟踪每个I/O流的状态,来管理多个I/O流。redis-client在操作的时候,会产生不同事件类型的socket。服务端有一段I/O多路复用程序,将socket放入队列中。 文件事件分派器,去队列中取然后转发到不同的事件处理器中。
5.底层模型不同
redis底层实现方式以及与客户端之间通信的应用协议不一样,redis自己构建了vm机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
四.redis数据类型和使用场景
1.string
value可以是string也可以是数字
2.hash
是key-value的键值对集合,适合用于存储对象。
3.list
简单的字符串列表,按照插入顺序排序,可以添加元素到头或者尾
4.set
string类型的无序集合,通过哈希表实现,添加、删除、查找的时间复杂度都是O(1)
5.sorted set
比set多了一个权重参数score,集合中的元素能够按照score进行排列。
五.redis过期策略和内存淘汰机制
redis采用的是定期删除+惰性删除策略。
定期删除:redis默认每100ms将随机抽取key检查是否过期。如果存在过期的key则删除,因此只采用定期删除的策略会 导致一些key到了过期时间而没有被删除。
惰性删除:在获取key的时候,redis会检查是否过期,如果设置了过期时间并且过期了,就会删除。
定期删除+惰性删除存在的问题
如果定期删除没有删除key,也没有即使请求去获取key,惰性删除也没有生效,redis的内存会越来越高。那么就应该采用内存淘汰机制。
redis中有一行配置:
/# maxmemory-policy volatile-lru
这个就是配置内存淘汰策略的,当内存不足以容纳写入新数据时:
1.noeviction:新写入操作会报错。(不推荐)
2.allkeys-lru:移除最近最少使用的key。(推荐)
3.allkeys-random:随机移除key。(不推荐)
4.volatile-lru:在设置了过期时间的key空间中,移除最近最少使用的key。(这种一般是既当缓存,又做持久化存储的时候才使用)
5.volatile-random:在设置了过期时间的key空间中,随机移除key。(不推荐)
6.volatile-ttl:在设置了过期时间的key空间中,有更早过期时间的key优先移除。(不推荐)
没有设置过期时间的key,由于不满足先决条件,volatile-lru、volatile-random、volatile-ttl的策略行为和noeviction基本上一致。
六.redis和数据库双写一致性问题
一致性是分布式的常见问题,又可以分为强一致性和最终一致性。数据库和缓存双写,必然会存在不一致的问题。如果对数据有强一致性的要求, 就不能放入缓存中。
七.如何应对缓存击穿和缓存雪崩
缓存穿透
黑客故意去请求缓存中不存在的数据,导致所有请求都到数据上,使得数据库连接异常。
解决方案:
1.利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁则休眠一段时间再重试。
2.采用异步更新策略,无论key是否取到值,都直接返回。value中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。 需要做缓存预热的操作。
3.提供一个能迅速判断请求是否有效的拦截机制,比如利用布隆过滤器, 内部维护一系列合法有效的key。迅速判断出请求的key是否合法有效。
缓存雪崩
缓存同一时间大面积失效,这时候又来了一波请求,结果请求都到了数据上,使得数据库异常。
解决方案:
1.给缓存的失效时间加上一个随机值,避免集中失效。
2.双缓存:缓存A和缓存B,A设置失效时间20min,B不设失效时间。
(1)从缓存A读数据,有则直接返回
(2)A没有数据,直接从B读数据,直接返回,异步启动一个更新线程。
(3)更新线程同时更新缓存A和缓存B
参考文章
https://blog.csdn.net/bjweimengshu/article/details/100911399 https://www.cnblogs.com/haha12/p/10470786.html