1、redis的几种数据类型?
五大基本数据类型:String、List、Set、ZSet、Hash
2、redis是单线程还是多线程?
无论是什么版本的redis工作线程只有一个,
在6.x高版本中出现了IO多线程(处理磁盘读写的是多线程,进行数据计算和操作的只有一个工作线程。
类似netty的主从Reactor模式
3、redis有线程安全性问题吗?为什么?
redis是线程安全的,redis可以保证内部串行,外界使用时只需要保证业务顺序即可
4、redis的缓存穿透、击穿、雪崩详述一下?
- 穿透:缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透(采用布隆过滤器)
- 击穿:热点key过期(或者从来没有缓存,数据库存在数据)类似穿透,解决方案(请求redis数据不存在是时开始抢锁,没抢到锁的等待或者降级,拿到锁的请求开始请求数据库。获取数据后,更新缓存)
- 雪崩:多个key同一时间过期(数据库存在数据)解决方案:分散设置key的过期时间(random),
5、redis的缓存如何回收?
- 后台轮询的时候分段分批删除过期的key
- 访问redis缓存数据的时候判断是否过期,过期则删除
- 尽量的把内存无用的空间回收回来。
6、redis的缓存如何淘汰?
- 内存空间不足的情况下。
- 淘汰机制里有不允许淘汰
- LRU算法:最近最少使用。优先淘汰最近未被使用的数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”,LRU底层结构是 hash 表 + 双向链表。hash 表用于保证查询操作的时间复杂度是O(1),双向链表用于保证节点插入、节点删除的时间复杂度是O(1)
- LFU算法:使用频率最少的(最不经常使用的)优先淘汰最近使用的少的数据,其核心思想是“如果一个数据在最近一段时间很少被访问到,那么将来被访问的可能性也很小”。LFU 使用 Morris counter 概率计数器,仅使用几比特就可以维护 访问频率,Morris算法利用随机算法来增加计数,在 Morris 算法中,计数不是真实的计数,它代表的是实际计数的量级。
- TTL算法:从设置了过期时间的数据中,挑选越早过期的数据进行删除
7、如何进行缓存预热?
- 提前将数据缓存至redis,哪些是热数据(刚开始并不知道,从测试切到生产,首先会出现穿透,雪崩。然后就会知道哪些数据需要预热了)
8、数据库和缓存不一致如何解决?
- 最直接的办法就是通过canal去延迟写数据库
- 延迟双删
1)先删除缓存;
2)再写数据库;
3)休眠500毫秒(根据具体的业务时间来定);
4)再次删除缓存 - 通过binlog
(1)更新数据库数据
(2)数据库会将操作信息写入binlog日志当中
(3)订阅程序提取出所需要的数据以及key
(4)另起一段非业务代码,获得该信息
(5)尝试删除缓存操作,发现删除失败
(6)将这些信息发送至消息队列
(7)重新从消息队列中获得该数据,重试操作。 - 如果数据库不采用事务,那么第一的方案是「先删除缓存,再更新数据库」;
- 如果数据库采用了事务,但是是读的场景为主,那么方案优先次序为:「先更新数据库,再更新缓存」、「先更新数据库,再删除缓存」、「先删除缓存,再更新数据库」
- 如果数据库采用了事务,但是是写的场景为主,那么方案优先次序为:「先更新数据库,再删除缓存」、「先更新数据库,再更新缓存」、「先删除缓存,再更新数据库」
- 之所以把「先更新数据库,再更新缓存」放在「先删除缓存,再更新数据库」前面是因为后者既对代码侵入性、和提高复杂,而且效率降低(在维护redis穿透和击穿时需要互斥),而前者只是效率降低)无论哪种情况都不应该采取的方案:「先更新缓存,再更新数据库」
- 总结
1、懒加载模式缓存可采取双删+TTL失效来实现;
双删失败情况下可采取重试措施,重试有业务通过mq重试以及组件消费mysql的binlog再写入mq重试两种方式;
2、主动加载由于操作本身不具有幂等性,所以需要考虑加载的有序性问题,采取mq的分区机制实现串行化处理,实现缓存和mysql数据的最终一致,此时读和写操作的缓存加载事件是走的同一个mq。
8、描述一下redis持久化原理?
- redis会fork一个子线程去执行持久化
- RDB:保存数据快照至一个RDB文件中,用于持久化。RDB操作和Mysql Dump相似。优点:速度快,但是一致性较差,恢复快(大指令集)
- AOF:采用AOF持久方式时,Redis会把每一个写请求都记录在一个日志文件里(追加写),AOF操作和Mysql Binlog相似。通过AOF重写机制减少AOF文件的体积,从而减少恢复时间。效率慢(如果开启了同步策略,不开启的情况下和RDB一致),一致性强,恢复慢
- 总结:二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。不过生产环境其实更多都是二者结合使用的。
9、为什么使用setnx?
- 使用SETNX实现分布式锁
如果 SETNX 返回1,说明该进程获得锁,SETNX将键 lock.foo 的值设置为锁的超时时间(当前时间 + 锁的有效时间)。
如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。 - 解决死锁
考虑一种情况,如果进程获得锁后,断开了与 Redis 的连接(可能是进程挂掉,或者网络中断),如果没有有效的释放锁的机制,那么其他进程都会处于一直等待的状态,即出现“死锁”。
上面在使用 SETNX 获得锁时,我们将键 lock.foo 的值设置为锁的有效时间,进程获得锁后,其他进程还会不断的检测锁是否已超时,如果超时,那么等待的进程也将有机会获得锁。
10、redis的分布式锁原理?
- 使用setnx、getset、expire、del这4个redis命令实现
- 底层使用了Lua脚本