Redis知识总结
Redis
- Redis核心特性
- 丰富的数据类型
- 持久化机制
- 过期键与内存管理
- 高可用与集群
- 常见应用场景
- 注意事项
- 常见问题
- 缓存和数据库 一致性
- 产生原因
- 常见更新策略
Redis核心特性
- 基于内存存储:数据主要存储在内存中,读写速度快(单机OPS可达10万+)。
- 支持多中数据结构:还支持字符串、哈希、列表等复杂结构。
- 持久化:可将内存中数据持久化到磁盘中,防止重启丢失。
- 高并发:单线程模型(避免线程切换开销)+IO多路复用,高效处理并发请求。
- 跨平台:支持Linux、Window等系统,可通过客户端(如Java的jedis、python的redis-py)访问。
注:IO多路复用:一种高效的IO模型,允许单个进程(或线程)同时监控多个IO流(如网络连接、文件描述),当某个IO流就绪时,进行相应的处理。
优势:减少等待时间,提高CPU利用率。
传统IO模型中 单个进程处理一个IO流,IO未就绪则阻塞等待。IO多路复用通过:集中监控、就绪通知、非阻塞等待。
I/O 多路复用让单个进程能高效处理多个并发连接,避免了多线程 / 多进程模型的上下文切换开销。
丰富的数据类型
redis支持多种数据结构,每种结构有特定的命令和应用场景。
数据结构 | 特点 | 常用命令 |
---|---|---|
字符串 | 存储字符串,整数、浮点数;最大512B | SET(设置值)、GET(取值)、INCR(自增)、DECR(自减)、APPEND(追加) |
哈希Hash | 键值对集合(类似JSON对象),适合存储结构化数据 | HSET(设字段)、HGET(取字段)、HGETALL(去所有字段)、HDEL(删字段) |
列表List | 有序字符串集合(可重复),类似双向链表,支持双端操作 | LPUSH(左加)、RPUSH(右加)、LPOP(左删)、RPOP(右删)、LRANGE(取范围) |
集合SET | 无序字符串集合(不可重复),支持交集、并集、差集运算 | SADD(加元素)、SMEMBERS(取所有)、SINTER(交集)、SUNION(并集) |
有序集合Sorted Set | 有序集合(不可重复),通过**“分数score”**,排序,支持范围查询。 | ZADD(加元素+分数)、ZRANGE(按分数升序取)、ZREVERANGE(降序取)、ZINCRBY(分数自增)。 |
扩展:Bitmap(位图)、HyperLogLog(基数统计)、Geospatial(地理信息)。
持久化机制
Redis是内存数据库,需要通过持久化将数据写入磁盘,防止重启丢失。核心方式有两种:
- RDB(Redis Database)
- 原理:定时将内存中所有数据生成数据快照(二进制文件,.rdb),写入磁盘。
- 触发方式:
- 手动:SAVE命令(阻塞主进程),BGSAVE(后台异步执行,不阻塞)
- 自动配置文件中设置触发条件(如save 60 1000;60秒内右1000次写操作)
- 优点:快照文件所占空间小,恢复速度快(直接加载到内存)。
- 缺点:若没触发快照 就宕机了,会导致数据丢失(如69内1000次,在59秒时宕机,则丢失59秒内数据)。
- AOP(Append Only File)
- 原理:记录所有写命令(如SET、HSET)到日志文件(.aof)。重启时重新执行命令恢复数据。
- 触发方式:设置appendonly yes开启,默认每执行一次写命令同步到磁盘(可通过appendfsync调整频率;always实时同步/everysec每秒同步/no操作系统控制)
- 优点:数据安全性高(最多丢失1秒数据)。
- 缺点:日志文件大(命令重复记录),恢复速度慢(需要重新执行所有命令)。
- 混和持久化(Redis 4.0+)
- 结合RDB和AOF的特点:AOF文件头部存储RDB快照,尾部存储增量命令。
- 恢复时先加载RDB快照,在执行尾部AOF命令,兼顾速度和安全性。
过期键与内存管理
1、过期键 删除策略
Redis允许为键设置过期时间(EXPIRE key seconds),过期后需要删除,策略如下:
- 定时删除:过期时立刻删除(消耗CPU不推荐)。
- 惰性删除:访问键时检查是否过期,过期则直接删除(节省CPU,但可能出现浪费内存)。
- 定期删除:每隔一段时间扫描部分过期键,并删除(折中方案,平衡CPU和内存)。
- 实际使用:Redis 采用 惰性删除+定时删除 组合。
2、内存淘汰策略:
当内存到达maxmemory 限制时,Redis会淘汰部分键,常见策略:
- volatile-lru:从过期键中淘汰最近最少使用的键。
- allkeys-lru:从所有键中淘汰最近最少使用的键(适合全缓存)。
- volatile-ttl:从过期键中,淘汰剩余时间最短的键。
- volatile-random:从过期键中,随机淘汰。
- allkey-random:从所有键中,随机淘汰。
- 配置策略:maxmemery-policy:volatile-lur(默认)。
高可用与集群
1、主从复制
- 作用:实现数据备份(从节点复制 主节点数据)和读写分离(主节点写,从节点读)。
- 原理:
- 全量复制:从节点首次连接主节点时,主节点生成RDB文件 并发送。从节点加载快照后同步增量数据。
- 增量数据:主节点后续 写操作通过复制偏移量同步到从节点。
- 配置:从节点配置 slaveof 主节点IP 主节点端口。
从节点连接主节点,主节点生成 RDB 快照并发送(全量复制),之后通过偏移量同步增量数据(增量复制)。
作用:读写分离(主写从读)、数据备份。
2、哨兵模式(Sentiel)
- 作用:监控主从节点状态,当主节点故障时 自动将从节点升级为主节点(故障转移)。
- 组成:多个哨兵节点(避免单点故障),通过投票机制决定故障转移。
- 配置:指定监控的主节点,哨兵数量(如:sentinel monitor mymaster 127.0.0.1 6379 2)需要2个哨兵同意才能 触发转移。
作用:监控主从节点,主节点故障时自动选从节点升级为主(故障转移)。
流程:监控(心跳检测)→ 主观下线(单哨兵认为主节点故障)→ 客观下线(多数哨兵同意)→ 选举新主节点(从节点优先级、偏移量等)→ 切换(其他从节点复制新主)。
3、集群(Cluster)
- 作用:分片存储数据(突破单机内存限制),支持高并发和自动故障转移。
- 分片方式:将数据分到16384个哈希槽中,每个节点负责部分槽(如3个槽分别负责0-5460,5461-10922、10923-16383)
- 通信:节点间通过Gossip协议交换状态(如健康,哈希槽分配)。
- 配置:通过redis-cli --cluster create搭建,需要至少3个主节点(推荐3主3从)。
共 16384 个哈希槽,每个主节点负责一部分槽(如 3 主节点各分 5461 个)。
键通过 CRC16(key) % 16384 计算槽位,映射到对应节点。
支持动态扩缩容(迁移槽位),每个主节点可带从节点实现高可用。
小结:
主从复制是基础,解决数据备份和读写分离;哨兵基于主从复制,解决故障自动转移;集群则是更高阶的方案,在主从和哨兵的基础上实现数据分片和水平扩展。
常见应用场景
1、缓存:存储热点数据(如商品信息),减少数据库压力。需要注意:
- 雪崩:大量key同时过期,可设置随机过期时间。
- 击穿:热点key过期瞬间,大量请求打向数据库,可设为永久key。或互斥锁保证单请求。
- 穿透:查询不存在的数据(如id=-1),可通过布隆过滤器拦截。或缓存null值。
2、计数器:如文章阅读量(INCR)、点赞数(HINCRBY)
3、消息队列:用List的LPUSH(生产)和RPOP(消费)实现简单消息队列(需要注意消息丢失问题,可结合AOF持久化)
4、排行榜:用Sorted Set的ZADD和ZRANGE实现实时排名(如游戏得分)。
5、分布式锁 通过SET key value NX EX 10(不存在则设值,过期时间10秒)实现,避免并发冲突。
注意事项
- 避免大key:大key(如百万级元素的List)会导致网络传输慢,删除阻塞,建议拆分。
- 合理设置过期时间:避免内存溢出,优先使用EXPIRE而非手动删除。
- 使用管道(Pipeline):批量发送命令(如批量SET),减少网络往返次数。
- 监控与运维:通过INFO命令查看状态,用Redis-cli --bigkeys检测大key,结合Prometheus+Grafana监控。
常见问题
缓存和数据库 一致性
缓存与数据库一致性问题:当数据 同时存在 缓存和数据库中时,两者数据出现不匹配现象。
核心原因:缓存和数据库是两个独立存储组件,数据更新无法保证原子性,加上并发环境。容易导致数据不一致。
产生原因
缓存主要是 提高读取速度,读走缓存,写更新数据库的逻辑,但在写操作时,需要同时处理数据库和缓存。而两者的操作顺序、网络延迟、并发冲突等,都会引起不一致
- 操作顺序不合理:可能导致旧数据残留
- 并发冲突:导致数据覆盖
- 操作失败:更新数据库成功但是 更新/删除缓存失败,导致缓存中残留旧数据。
- 缓存过期/淘汰:缓存因过期或内存淘汰被删除,重新加载时可能读取到中间状态的数据库数据。
常见更新策略
解决一致性的核心:**明确”写操作“时如何处理缓存。**常见策略及其问题:
1、先更新数据库,再更新缓存(不推荐,高并发下易覆盖,后线程更新缓存)
2、先删除缓存,再更新数据库(有风险,出现脏读,同时构建缓存)
3、先更新数据库,在删除缓存。(推荐基础方案)
进阶方案:
1、延时双删策略(解决先删缓存在更新数据库的脏读问题)
流程:先删除缓存;更新数据库;延时一段时间后再删一次缓存。
2、异步删除缓存(解决删除失败的问题)
流程:更新数据库;发送”删除缓存“消息到消息队列中;消费消息,执行删除缓存操作(失败则重试)。
优势:借助消息队列确保消息不丢失,避免网络波动导致缓存删除失败。
3、读写锁/分布式锁(解决并发冲突)
高并发场景中(如秒杀)
写操作加锁 保证更新数据库+删除缓存的原子性,防止并发写导致的数据覆盖。
读操作加锁:当缓存失效时,只允许一个线程进行缓存重建,其他请求等待,避免缓存击穿。同时保证数据一致性。
4、兜底策略:设置有效时间保证最终一致性
无论采用那种策略,都给缓存设置有效时间,即使出现不一致 过期后缓存会被自动删除。下次请求或重建最新数据。实现最终一致性。
5、Canal监听binlog(强一致性场景)
对一致性要求极高
通过Canal监听数据库的binlog日志,实时感知数据变更;
当数据库更新后,右Canal触发缓存更新/删除缓存,确保缓存与数据库一致性。
优势:避免业务代码侵入,可靠性高,但实现复杂度高,适合核心场景。
小结:
- 优先选择 “先更数据库,再删缓存”:简单且适配多数场景(如电商商品详情、用户信息)。
- 结合过期时间兜底:即使出现不一致,也能在过期后自动修复。
- 高并发场景加锁:用分布式锁(如 Redis 分布式锁)保证写操作原子性。
- 核心数据用 binlog 同步:如交易金额、库存等强一致性需求,通过 Canal 监听 binlog 同步缓存。