一.基于redis实现限流功能,有哪几种方式?
1.限流
一段时间内,某行为允许操作的最大次数
2.限流类型
截断限流,固定时间窗口限流,固定窗口内的行为统计,
实现方式:
set limits 0 nx ex 10
incr limits
滑动时间 窗口限流,滑动窗口行为统计,解决窗口间统计异常,容量固定的,速率也是固定的
实现方式:
zadd limits now now
zremrangebyscore limits 0 period
zcard limits
expire limits 10
-- KEYS[1]: 限流key
-- ARGV[1]: 当前时间戳
-- ARGV[2]: 窗口大小(秒)
-- ARGV[3]: 最大请求数redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, ARGV[1] - ARGV[2])
redis.call('ZADD', KEYS[1], ARGV[1], ARGV[1])
local count = redis.call('ZCARD', KEYS[1])
if count > tonumber(ARGV[3]) thenreturn 0 -- 被限流
elsereturn 1 -- 允许通过
end
漏斗限流 ,初始容量,操作数,时间,步伐 (更加细致的限流) redis-cell扩展实现
令牌桶限流 ,
二.redis为什么是单线程?这里的单线程是什么?为什么这么快?
1.redis是不是单线程
redis-server
所有命令处理
网络事件监听
bio-close-file
异步关闭文件
bio-aof-fsync
异步aof刷盘
bio-lazy-free
异步清理大内存
jemalloc-bg-threads
jemalloc后台线程
io-threads
io线程 read/write decode encode
2.这里的单线程是指什么?
命令处理流程在一个线程中处理的
3.redis为什么是单线程
redis不是CPU密集型,内存型数据库
为什么redis在redis-server中是单线程如果采用多线程有什么后果
加锁复杂,不会控制锁的粒度,多个数据类型(string list hash set zset),每个对象有多种数据结构实现,频繁的cpu上下文切换抵消了多线程的优点
单线程的局限:
不能有耗时操作 ,redis需要响应性能
4.单线程为什么这么快
内存数据库&&高效的数据结构
reactor网络模型 io多路复用 非阻塞io
做了哪些优化
耗时阻塞的操作,另起线程处理
异步关闭大文件
异步释放大内存
异步aof刷盘
io多线程等等。。。。。。。。。。。。
三.redis怎么实现分布式锁?有哪些缺陷?
1.分布式锁
高可用的问题,锁是一种资源
获取锁和释放锁必须为一个对象,需要记录持锁的对象
互斥:确保只有一个对象持有
扩展:怎么获知锁释放 ----怎么获知锁释放---非公平锁/公平锁
是否允许同一个对象多次获取锁 ----重入锁/非重入锁
2.redis怎么实现分布式锁
生成唯一标识value(通常用UUID),用于标识本次加锁的请求
使用redis命令尝试加锁
set lock_key value NX PX timeout
lock_key是锁的名字
value是上一步生成的唯一标识符
NX 保证只有key不存在的时候才能加锁
PX timeout 设置锁的过期时间,防止死锁
判断加锁结果
如果返回OK,说明加锁成功,用户获得锁
如果返回nil,说明锁已经被其他用户获取
执行业务逻辑
客户端获得锁后,执行需要保护的临界区代码
释放锁
释放锁时,需要判断当前锁是否是自己加的(即 value 是否一致),防止误删他人锁。通常用 Lua 脚本保证原子性:
if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1]) elsereturn 0 end
这样只有持有锁的客户端才能释放锁。
异常处理
如果客户端在持有锁期间崩溃,锁会因为超时自动释放,避免死锁。
3.redis怎么做到高可用的
主从复制(Replication)
Redis 支持一主多从架构,主节点负责写操作,从节点负责读操作。当主节点数据发生变化时,会自动同步到从节点。这样即使主节点宕机,从节点也能提供数据服务。哨兵模式(Sentinel)
Redis Sentinel 是官方提供的高可用解决方案。它可以自动监控主节点和从节点的状态,当主节点宕机时,Sentinel 会自动将某个从节点提升为新的主节点,并通知客户端更新主节点地址,实现自动故障转移。集群模式(Cluster)
Redis Cluster 支持数据分片和多主节点,每个节点保存部分数据和备份。即使部分节点宕机,集群仍能继续提供服务,提升了可用性和扩展性。总结:
Redis 通过主从复制、哨兵模式和集群模式,实现了高可用和自动故障转移,保证了服务的稳定性和可靠性。
四.缓存雪崩,缓存穿透,缓存击穿
1.缓存雪崩
指缓存中大量数据在同一时间失效(如大批量 key 同时过期或 Redis 宕机),导致大量请求直接打到数据库,数据库压力骤增,可能引发系统崩溃。
解决办法:
给不同 key 设置随机过期时间,避免同一时刻大量失效
做多级缓存或限流、降级处理
保证 Redis 高可用
2.缓存穿透
指请求的数据在缓存和数据库中都不存在,导致每次请求都要访问数据库,缓存形同虚设,数据库压力大。
解决办法:
对不存在的数据缓存空值(如设置一个短暂的空对象)
使用布隆过滤器等手段拦截非法请求
3. 缓存击穿
指某个热点 key 在高并发下突然失效,瞬间大量请求直接访问数据库,造成数据库压力骤增。
解决办法:
对热点 key 设置永不过期或互斥锁(如加分布式锁,只有一个线程去查库并回填缓存)
提前预热或延迟双删策略
五.Redis淘汰策略有哪些
noeviction(默认):
内存不足时,拒绝写入新数据,直接返回错误。
allkeys-lru:
所有key中,优先淘汰最近最少使用(LRU)的key。
volatile-lru:
只对设置了过期时间的key,淘汰最近最少使用的key。
allkeys-random:
所有key中,随机淘汰一个key。
volatile-random:
只对设置了过期时间的key,随机淘汰一个key。
volatile-ttl:
只对设置了过期时间的key,优先淘汰即将过期的key(TTL最小)。
六.redis中的事务
Redis中的事务是通过
MULTI
、EXEC
、DISCARD
、WATCH
等命令实现的一组操作的原子性执行机制。
命令打包:用
MULTI
开启事务,之后的命令会依次进入队列,直到执行EXEC
时,所有命令一次性、顺序执行。原子性:事务中的命令要么全部执行,要么全部不执行(如果执行前用
DISCARD
取消)。不支持回滚:Redis事务不支持部分命令失败时的回滚,某条命令出错不会影响其他命令的执行。
乐观锁:通过
WATCH
命令监控一个或多个key,在事务执行前如果这些key被修改,事务会被中断,不会执行。
WATCH key1
MULTI
SET key1 value1
INCR key2
EXEC
七.redis的持久化操作
1.RDB(快照)持久化
定期将内存中的数据生成快照(.rdb文件)保存到磁盘。
恢复时加载RDB文件即可还原数据。
优点:对性能影响小,文件紧凑。
缺点:可能丢失最近一次快照后的数据。
2.AOF(追加文件)持久化
以日志形式记录每次写操作(append only file)。
Redis重启时,重放AOF日志恢复数据。
优点:数据更安全,可配置每次写操作都落盘。
缺点:文件体积较大,恢复速度较慢。
两者可以结合使用,提高数据安全性和恢复效率。