首先感谢网友的参考资料:原文地址,这文章看了好多遍了,一直比较懒得整理一下自己的理解感受,今天有机会 整下呗。
redis常用考点
1、为什么使用redis
2、使用redis有什么缺点
3、单线程的redis为什么这么快
4、redis的数据类型,以及每种数据类型的使用场景
5、redis的过期策略以及内存淘汰机制
6、redis和数据库双写一致性问题
7、如何应对缓存穿透和缓存雪崩问题
8、如何解决redis的并发竞争问题
下面来一个个看这些问题(加入了博主个人的一些理解,如果有理解上的偏差,望同学们及时纠正)
为什么使用redis
原文章中讲到使用redis从并发和性能上考虑,从提高系统并发量及提高访问性能上考虑的,
1.性能: 原因一是 对于热点数据访问且数据不经常变动的,使用redis的缓存作用来提高系统的并发量,减少数据库的压力及提高并发量很有必要,毕竟数据库是磁盘数据,而redis是纯内存操作,那肯定是内存的效率比数据库的效率高喽。所以能够提高系统的性能.
2. 并发:redis每秒中可以写入8W左右数据,读取11W条数据.从性能上讲比数据库效率更高.所以如果同一时间大量的请求访问数据库,那数据库很可能会崩掉的(网络上很多网站就是这样的),所以采用缓存作为临时缓存区,避免直接访问数据库 这样系统的并发量无疑更好.
3.作用:谈到Redis无非2个作用,缓存及非关系型数据库.所以如果在需要缓存作为数据库缓冲的时候,或者某些请求耗时时间很长 而且结果不怎么变化的时候就可以用缓存(如系统中 水电煤查账,需要调用轮询查询耗时15秒,且用户的水电煤账单也不怎么变化,所以用缓存比较好 先缓存个半小时呗)。另外就是一些数据库结构简单的数据 不用关联查询的数据存储到Redis作比较好.相当于把数据作为持久化数据 放到Redis中(比如系统中用户购买的电池片数量及总额,而不是直接查询账单中的数据).
4.分布式锁:这篇文章暂不做讨论
Redis有哪些缺点
- 数据库与缓存双写一致性问题(就是数据库与缓存在写入的时候如果保证某个除了问题 另外一个能够回滚的问题?现在一般采用cache aside pattern解决方案)
- 缓存穿透/缓存击穿/缓存雪崩(缓存不存在,同一时间大量请求访问数据库,导致缓存穿透/很多个缓存设置同一失效时间,而此时恰巧大量请求访问,直接访问数据库崩了,一般采用缓存失效时间加个随机数的解决方案)
- 缓存并发竞争问题.(就是多个系统在设置同一个key的时候出现竞争的方案,可以采用分布式锁的方案,谁先抢到给谁,另外一个如果发现锁被用了,就等一会的方案.)
单线程的Redis为什么这么快
- 纯内存操作(相较于数据库磁盘存储方式)
- 单线程,避免了频繁的上下文切换.(说白了就是大量请求过来 也是在一个线程里面处理)
- I/O多路复用(说白了就是当I/O请求进来的时候,根据i/o流的状态 ,单个线程合理分配任务.类似于I/O输入输出流中的缓冲流,不是来一个I/O流就去处理任务,而是根据I/O流要到达的位置 而合理的堆积一定任务 去处理)
从文章中我们可以看到,实际上大量I/O流进来的时候,Redis有个I/O多路复用程序,将I/O流到达位置相近的放到一起然后 放到消息队列中,让文件事件分派器 去处理交给对应的事件处理器.
Redis基本数据类型
Redis基本数据类型有String/Hash/List/Set/Zset,之前有写过文章记录这些数据库类型的作用,这里补充下.
- String,简单set/get存储数据,可以是数字或者字符串。可以在计数的时候用这个.
- Hash结构:常用于一个KEY对应Hash结构数据.通常是用来放对象的。比如单点登录的时候 将登录客户的信息存到这里面.
- List:List集合之前 项目里面是拿来存储 一堆相同数据类型的 电池片编号.这篇文章里面有讲到 利用lrange实现简单的分页查询,还可以利用List结构实现简单的消息队列功能.
- Set:存入值无法重复的集合.类似Java中的Set集合数据结构.
- Zset:存入数据 无法重复的集合,加入了根据score排序的功能.项目中有用到排行榜的功能.
Redis的过期策略和淘汰策略
加入Redis只能存放5G的数据,而此时要放入10G的数据,那么多出来的5G数据是如何被淘汰的那?
Redis采用的是定期淘汰+惰性删除的策略.
-
原文中有讲到为什么使用定期淘汰而不是定时淘汰策略?首先要搞清楚定时淘汰指的是起一个线程 专门监控Redis中的key是否有过期,如果有那么删掉,但是这种实施监控对系统资源消耗太大,一般有时效且对系统新能要求较高的系统都不会采用这种方案。
-
什么是定期淘汰策略:说的就是redis默认每100ms定时从Redis查找100个key,如果key过期了,那么就删除掉.这一策略存在着不足就是假如key一直没有被遍历到,那么就需要通过惰性淘汰策略删除掉,就是在使用这个key的时候发现 key过期了就删除掉。
Redis与数据库双写一致性问题
这个问题我之前的文章也有提到过解决方案,就是通过cache aside pattern方式控制 数据库与缓存数据不一致性的问题,只能尽力避免出现不一致的情况.原理就是
读的时候先访问缓存,缓存不存在读取数据库更新缓存.写入的时候先删除缓存,然后更新数据库信息。但是可能会出现删除缓存 更新数据库的时候 读取数据 查询到旧的数据库数据 并更新到缓存中,虽然过了缓存有效期会读取到新的数据 但是会有问题.这里简单给出2中解决方案
- 写入的时候双删除 缓存方案,就是在写入的时候 删除缓存,更新完数据库,再删除一遍缓存。以保证在更新数据库的时候 即使有查询操作更新缓存 那也把他删除掉.
- 另外一种实现方案比较复杂:原理就是写入的时候把数据唯一标识放入jvm内存队列中,读取的时候判断内存队列中是否存在 更新数据库的操作,如果存在那么等待前面的操作完成再读取数据,如果有大量读取请求过来那么可以把读操作放入队列中,如果有读操作,那么就合并掉 用前面的数据,总之就是读数据 放在更新数据操作后面,但是可能会造成数据 吞吐性能下降的问题.
如何解决缓存穿透及缓存雪崩的方案
1.缓存穿透出现的原因是在缓存失效的时候,大量的请求过来访问数据库,造成缓存穿透的问题.解决这一问题的方法一般就是利用互斥锁,说白了就是悲观锁,可以在代码层面 在缓存失效,访问数据库这段代码加上互斥锁,可以使用Lock锁机制的方式或者synchronzied,防止同一时间 大量请求在前一个请求没处理完成之前,大量的并发到数据库中去。
2.缓存雪崩是指在统一时间大量的缓存同时都失效了,而此时大量的请求都到数据,那就崩了.比较简单的解决方案就是在原有失效时间后面加个随机值,这样避免大量缓存在同一时间失效的问题.
Redis竞争key的问题
指的是如果分布式 多个客户端同时要设置一个key值,那么如何处理key资源竞争问题。
- (不要求顺序)比较简单的解决方案就是通过通过一个分布式锁,谁先抢到谁先设置,另一个发现锁抢不到可以等待或者直接放弃(当然这是在不要求有先后顺序的情况下).
- (要求顺序)就是不同系统设置key的时候加个时间戳,如果A系统设置为1,时间为3分5秒,系统B 获得锁发现 key中的值是3分5秒,遭遇B系统设置的3分10秒,那么就不去设置key,否则更新key。