背景
今天去xx云面试,回答的不是很好,不过感觉问的很好,这里我分为几次记录下来,每天进步亿点点
实战面试题(redis篇)
- redis如何保证所有数据都是热点数据?
redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略
- voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-enviction(驱逐):禁止驱逐数据
- redis常见的性能问题有哪些?
- Master写内存快照(RDB),save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能的影响非常大,会间断性暂停服务,所以Master最好不要写内存快照
- Master AOF 持久化,如果不重写AOF文件,那么这个方式对性能影响是很小的,但是AOF不断增大,AOF文件过大会影响Master的重启恢复速度。Master最好不要做任何持久化的工作,包括RDB和AOF。如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次
- Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内
- redis中有1000W数据,其中有20W是以 XX_ 开头的,如何获取到它们?
可以通过
keys pattern
来获取,如:keys* xx_
不过这种方式是以
停止insert为代价
的,所以在生产环境中不能使用通过
scan cursor [MATCH pattern] [COUNT count] [TYPE type]
来获取,如:scan 0 match xx_* count 5
其中:
- scan是基于游标的迭代器,执行scan后会返回下一次的游标
- 0表示开始一次新的迭代
- 3表示返回的数量,但是查询出的结果却不可控,只是大概率符合count参数
- 返回结果不保证与之前的数据不重复,需要在客户端自行去重
- 分布式锁如何用redis实现?有什么问题吗?
实现思路:redis分布式锁实现是基于命令
setnx key value
,意思是:若该键不存在则创建键,这就保证了redis中该键的唯一性,所以在多个线程同时执行该命令时,谁执行成功了,谁就获取了锁;然后业务逻辑执行完毕则需要使用del key
删除键,表示释放锁;
问题1:如果一个线程逻辑执行完毕,程序出现异常,则锁会一直存在,没有得到释放,其它应用就会无法获得锁,此时就会造成死锁
;
改进方式:拿到锁之后,给锁加上一个过期时间,也就是expire key seconds
指令;此时避免了死锁问题,但是由于业务逻辑执行的时间不同,过期的时间设置也是一个问题,故通常分布式锁不能应用于业务逻辑执行较长的程序
;
问题2:由于redis 每条指令都是原子性操作,但由于setnx 和 expire 是2两条指令,如果在执行setnx后程序出现问题expire指令未得到执行就会造成死锁问题;
改进方式:redis2.8版本之后引入了指令set key value [EX seconds] [PX milliseconds] [NX|XX]
,该指令可以同时执行 setnx 和 expire ,于是解决了死锁问题;
问题3
①线程1获取锁,直到锁超时都未能执行完代码并释放锁。② 线程2此时获取到锁。 ③ 线程1执行完毕,释放了线程2持有的锁,出现问题
处理思路:
setnx的时候,同一把锁key必须为同名,但是value可以设置为uuid,这样可以保证谁去获取的锁,只能由自己释放;
接上一个问题:
4、此时线程3来了,轻松地获取到了锁,那么这时出现了多个线程并发操作,锁失效
处理思路:
究其出现的根本原因,是业务执行时间超过了redis中key的失效时间,我们可以通过给获得锁的线程开启一个守护线程,用来给快要过期的锁“续航”
。
其实这个锁最大的缺点就是它加锁时只作用在一个Redis节点上,即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况;所以这个时候就要考虑用RedLock
——一个作用与多个节点的锁。