Redis面试问题及其详解
- 分布式redis锁
- redis过期策略
- redis的缓存穿透,击穿,雪崩问题
- redis高可用策略
- redis的位图操作
分布式redis锁
这个主要是有4个方面要解决。
1.互斥
2.不能死锁
3.容错性
4.重试机制
常见案例是这样的。
案例一:线程中master中活得锁后,master宕机了, 而集群使用主从异步的方式,在宕机的同时,另一个线程去获得锁,会产生两个线程获得锁。
案例二:有两个功能块都是需要上锁执行。 A模块需要调用B模块功能。此时会发生死锁问题。
from threading import Thread,Lock
import time
mutex=Lock()
class MyThread(Thread):
def run(self):
self.func1()
def func1(self):
mutex.acquire()
print('\033[41m%s 拿到锁\033[0m' %self.name)
self.func2()
mutex.release()
def func2(self):
mutex.acquire()
print('\033[43m%s 拿到锁\033[0m' %self.name)
mutex.release()
if __name__ == '__main__':
t=MyThread()
t.start()
为解决该问题,可以尝试以下方法入手。
redlock算法: 其核心思想就是给 n/2 +1 个机器加锁。这样即使有某少许机器宕机了,不影响锁的判断机制。 加锁是否超时来判断加锁是否成功,超时,则把该操作之前加的锁全解锁。
解决死锁问题,是引入一个新概念——可重入锁。
意思是让同一线程或者说同一资源块下可以重复访问这个锁。
实现原理是,先获取key对应的value,value默认一般会用uuid+threadid来取值。判断有value后,判断value是否与当前线程资源值匹配,若匹配,加锁时,则把锁的引用计数+1,没有key的时候则初始化为1;解锁时则-1,判断引用计数是否不大于0,来判断是否释放锁。
#修改上面代码,更换锁
from threading import Thread,Lock,RLock
import time
# mutex=Lock()
mutex = RLock() ##可重入锁
class MyThread(Thread):
def run(self):
self.func1()
重试机制的实现,原理是在value中增加一个锁过期的时间戳,每次设置锁失败的时候,用get获取该锁的过期时间,与当前时间进行比较,再进行判断即可。
redis过期策略
这个比较简单,就三种。
定时删除:指应用中给key设置过期时间时,同时设置删除时间。
惰性删除:指在获得key的时候判断是否过期,进行是否删除策略。
定时删除:指规定某个时间定时删除。
redis的缓存穿透,击穿,雪崩问题
这里先介绍下名词的意思。
缓存穿透:指大量请求不存在的key时,例如不存在的订单号时,不断访问数据库,数据库负荷。
缓存击穿:指在高并发时,key刚好过期,都需要连接数据库查询,数据库负荷。
缓存雪崩:指服务器重启或者大量缓存同一时间失效。
常见解决穿透方案就是:布隆过滤器了。大致原理是把可能出现的key都哈希到一个bitmap上。进行过滤
解决击穿的方案是在过期后,先给这个key设置个固定值,设置成功后,然后查询数据库,然后再把值覆盖上去。
解决缓存雪崩的问题,服务器重启,这个用集群去解决。大量缓存同一时间失效,则在设置过期时间的时候加个随机值。
redis高可用策略
redis高可用主要指的是4个点。
持久化
复制
哨兵机制
集群使用
复制和集群使用,这个就简单,就是多个redis服务,然后,同一份数据多备份一份。
哨兵机制,这个东西比较大,建议详查。哨兵机制是起自动化故障恢复作用。原理大致是:每个服务都有哨兵实例。
解决主节点挂了时,更换主节点。 原理每个哨兵都定时ping主节点,当失败是则主观判断主节点挂了,当有n/2 +1 个哨兵认为是挂的,则在剩下的节点中随机选举主节点。
持久化:指把数据放到磁盘中,一般有两个方式
RDB:复制当前数据库的数据到磁盘中
AOF:复制当前所有的数据库操作命令到磁盘中。
现在常用AOF。
毕竟AOF的优势显示在更实时。
RDB的优势则在于存储在磁盘的文件大小很小,恢复起来很快。
redis的位图操作
这个作用大都是处理大量需要存在redis中的key可以转化为序号,值为布尔值时,可以采用位图。
原理是利用二进制的0和1。序号表示位数。
假设 位图存的值是十进制是6,二进制则为110
序号为0的值则为0
序号为1的值为1
序号为2的值为1.
这样可以大大节约redis的使用资源。