redis
2025-10-20 19:23:18 0 举报
AI智能生成
redis总结,包括实战问题,数据结构
作者其他创作
大纲/内容
数据结构
基本数据类型
string
1.存储普通字符串
2.存储数字类型字符串,让数字做自增(可以用作全局唯一ID,计数器)
实现方式
long类型整数
long范围内的数据就做此类型存储
embstr,编码简单动态字符串
长度小于44个字节的字符串
raw 简单动态字符串
长达大于44个字节的字符串
长度限制
最大512M
使用场景
incr
自增命令
使用场景
全局唯一ID
计数器
sentx
分布式锁
赋值命令
hash
特性:一个key,可以对应多个field字段,每个字段都可以存储不同的数据,可以缓存数据库行数据
实现方式
压缩列表
hashtable表
使用压缩列表条件满足
列表保存元素个数小于512个
每个元素长度小于64字节
使用场景
导入mysql数据库的表
表的字段如下: id,name ,age,sex
使用hash数据结构,key就设置成表的唯一字段;value可以存放多个字段和对应的值
如下:key 设计成user:1 value 设计成 name fankuai age 28 sex name 这样就ok了
list
使用场景
利用list双向链表的性质,可以实现栈,队列,阻塞队列数据结构
子主题
实现方式
双向链表
压缩列表
使用压缩列表条件满足
1、列表保存元素个数小于512个
2、每个元素长度小于64字节
使用场景
消息队列
利用对应的api从头部添加,从尾部获取数据的api
可以模仿一切队列使用到的场景
set
使用场景
用于抽奖
集合的并集,交集,差集(社交网站的关注模型)
常见api
SADD KEY {ID}
所有的id 都放在一个集合中
SRANDMEMBER key [数量]
这个随机选择几个,并且不从集合里面删除;
SRANDMEMBER SPOP key [数量]
随机选择几个,从集合里面删除掉;
实现方式
intset
存储整数集合
hashTable
使用intset实现方式条件满足
1、集合中所有的元素都必须是整数
2、集合中所有的元素不超过512个
使用场景
抽奖
添加抽奖者
sadd sku01 1001 1002 1003 1004
查看抽奖者用户
smembers sku01
查询参与抽奖的人数
scard sku01
抽取2人获奖
srandmember sku01 2
spop sku01 2
点赞,收藏,标签
用户添加商品收藏
sadd user1 sku1001 sku1002 sku1003 sku1004
用户取消收藏
sadd user1 sku1001
检查是否收藏
sismember user1 sku1001
查询用户的收藏列表
smembers user1
获取用户收藏数量
scard user1
关注模型
关注的人
sadd user1 元素1 元素2 元素3
共同关注的人
Sinter user1 user2
两个集合取交集
zset
使用场景
一些需要做排序的数据;
实现方式
压缩列表
HashTable和SkipList
HashMap存储的分数
skipList存储的是所有成员
使用压缩列表的需要满足条件
保存元素数量小于128个
保存的所有元素长度小于64个字节
使用场景
各种排行榜
主要是用的score 分数的排序功能
zadd key score1 member1 score2 member2
key对应的value中有两个元素,并且这两个元素对应的分数是score1, score2
数据结构实际应用场景
hash
zset
数据排序
数据如下: 88分 ,小明; 99分,小红
使用set命令存储的时候排序:key 为 chengjipaixu ; value为 99,小红 88 小明
实战
限流
用户消息时间线timeline
消息队列
商品标签
商品筛选
高级数据类型
bitMap
hyperLogLog
Geo
工作机制
缓存淘汰机制
缓存淘汰类型
不设置任何淘汰机制
noeviction
不做任何处理,写入超过限制后,会返回操作错误;读操作还是可以正常进行
默认淘汰策略
LRU最近最少使用淘汰
volatile-lru
设置了过期时间,清除最近最少使用的键值对
allkeys-lru
未设置过期时间,清除最近最少的键值对
存在问题
1、需要存储缓存数据之外额外的时间数据
2、可能会删除热key
解决方案
设置每一次要被淘汰的key的个数,个数如果=10比较,对热键数据影响比较小
实现
缓存对象中会存储这个缓存最近被访问的时间戳
淘汰的时候会根据当前的时间戳-缓存对象中的时间戳,差值最大的就被淘汰
LFU最小使用频率淘汰
volatile-lfu
设置了过期时间,清除某段时间内使用次数最少的键值对
allkeys-lfu
未设置过期时间,清除某段时间使用次数最少的键值对
TTL生命周期结束淘汰
volatile-ttl
设置了过期时间,清除过期时间最早的键值对
random随机淘汰
allkeys-random
未设置过期时间,随机清除键值对
volatile-random
设置了过期时间,随机清除键值对
缓存淘汰机制选择
1.如果缓存有明显的热点分部,那么就选择lru算法
2.如果缓存没有明显热点分部,那么就选择随机
触发内存淘汰时间点
1.Redis的每一次命令处理的时候,都会去判断当前redis是否已经达到最大缓存极限,如果达到极限,就会启用相应算法去处理需要清除的键值对;
2.过期key的回收
定期删除
Redis启动时候的定时时间,默认是每100毫秒的检测过期的key,过期就清理;
惰性删除
访问key的时候,key是否过期,过期就删除;
实际执行策略
Redis 惰性删除 + 定期删除同时启用
访问时删除 → 确保访问的 key 立即生效
后台扫描删除 → 确保不访问的 key 也能最终被回收
事件通知机制
事件通知概述
redis数据集改动事件之后对客户端的一种通知行为
事件通知类别
键空间通知
键事件通知
通知类型
删除,设置过期时间,重命名等一些和数据类型无关的操作的通知
字符串命令通知
列表命令通知
集合命令通知
哈希命令通知
有序集合命令通知
过期事件通知
缓存驱逐事件通知
不管发生什么事件都通知
事件通知使用
该功能默认是关闭;需要在config配置文件中开启该功能
配置形式:notify-keyspace-events +事件通知类别和通知类型;notify-keyspace-events "Ex"表示对过期事件进行通知发送
事件订阅缺陷
事件通知是不可靠的,服务器采用的是发送即忘,如果当订阅事件发生的时候;客户端掉线了,那么这个事件就不会通知到客户端,所有事件订阅是不可靠的
持久化机制
AOF
机制说明
1.记录每一个redis的写命令以日志的形式进行存储
2.AOF刷盘时间间隔
1.有命令就刷盘一次
2.一秒刷盘一次(推荐,也是默认的)
3.由系统决定刷盘时间间隔
3.为啥需要设置刷盘时间:持久化的目的是把数据记录在磁盘上,所以当数据在内存中的时候,就需要把内存中的数据放到磁盘上,放到磁盘上的时间间隔就是刷盘时间;
优缺点
优点
1.持久化实时性比较高(可以设置间隔多少秒追加一次日志,也就是间隔时间越短,丢失的数据就是越少)
缺点
1.AOF文件的体积通常大于RDB
2.数据恢复比rdb慢
AOF机制
1.当AOF文件过大时,后台会去优化AOF文件;
当AOF文件出错(以下两者方式都是可以解决AOF文件出错了,数据该怎么恢复的问题,最终还是需要重启redis服务器去载入AOF文件)
1.可以使用修复程序修改AOF文件;
2.为AOF文件创建一个备份文件
RDB(默认方式)
机制说明:就是以内存快照的形式缓存内存中的数据
缺点:1.实时性比较低,单独使用该持久化机制,服务器宕机导致数据丢失较多;
优缺点
缺点
1.实时性比较低,单独使用该持久化机制,容易导致数据丢失;
2.从主进程fork子进程的时候会被阻塞,
优点
1.rdb文件大小紧凑;可以设置间隔时间备份,还原到不同历史时期的数据状态
2.持久化的时候可以由子进程去完成所有的数据保存工作;父进程无需任何的io操作;
3.数据恢复比AOF快
数据存储:存储在dump.rdb文件中
RDB 的触发机制主要有两类:自动触发和手动触发
Redis 可以根据配置自动触发 RDB 快照,配置在 redis.conf 中以 save 指令定义
save 900 1 # 900秒内如果至少1个key发生变化,则触发RDB
save 300 10 # 300秒内如果至少10个key发生变化,则触发RDB
save 60 10000 # 60秒内如果至少10000个key发生变化,则触发RDB
save 300 10 # 300秒内如果至少10个key发生变化,则触发RDB
save 60 10000 # 60秒内如果至少10000个key发生变化,则触发RDB
Redis 也支持管理员手动触发 RDB 快照:
优点:确保 RDB 完成后返回
持久化机制
1.可以在不重启的情况下切换RDB到AOF模式
2.当RDB,AOF都打开的时候,程序默认使用AOF方式持久化
容灾措施
1.定期的把RDB文件备份到其他位置
Redis 持久化如何避免数据丢失?
Redis 的 持久化机制本身是为了解决内存数据丢失问题,但不同持久化方式的可靠性不同
1. 高性能型场景(对数据丢失可容忍)
用途:缓存型场景,例如热点数据、会话数据、排行榜
方案:
RDB 快照即可,默认配置 save 900 1 等
不启用 AOF 或 AOF everysec
特点:
快速启动、性能高
宕机时可能丢失最近几分钟的数据,但业务可以容忍
2. 数据安全型场景(对数据丢失敏感)
用途:需要较高可靠性的业务,如消息队列、计数器、交易系统缓存
方案:
AOF + RDB 混合持久化
AOF 推荐 appendfsync everysec(性能和安全折中)
定期 RDB 快照作为备份
可结合 主从复制,保证高可用
特点:
数据丢失窗口最小(一般 ≤ 1 秒)
启动稍慢,因为需要加载 AOF 文件
3. 超高可靠场景(金融、电商核心数据)
用途:绝对不能丢失的数据
方案:
AOF always + RDB + 主从复制 + Sentinel/Cluster 高可用
定期异地备份
特点:
性能稍差,但几乎保证不丢数据
启动时间最长(需要重放 AOF)
多路复用机制
epoll模式的多路复用
多线程
单线程性能瓶颈
网络IO
多线程只是用来处理网络数据读写和协议的解析,执行Redis命令依旧是单线程去执行
事物机制
事物本质
一组命令的集合,要么所有的命令都执行成功,要么都执行失败
特点
命令队列化:事务内命令先入队,不立即执行
一次性执行:执行 EXEC 时按顺序执行队列中的命令
错误处理:
语法错误:命令不会入队
运行错误(如操作类型错误):事务仍继续执行,不回滚
只能保证原子性
原子性:事务中的命令要么全部执行,要么都不执行(在执行期间不会被其他客户端命令打断)
隔离性弱:事务执行期间,其他客户端可以看到数据库状态,但不会插入事务内的命令
一致性和持久性:依赖 Redis 本身的数据模型和持久化机制
事物执行过程
一个事物所有的命令都会放在队列中缓存,执行的时候会去串行执行队列中的命令
事物相关命令
MULTI
开启一个事物
EXEC
执行这个事物的所有命令
discard
取消事物
watch
监视某些key
unwatch
放弃监视某些key
watch命令特别说明
配置事物一起使用,只有被监视的key没有发生任务数据变化的时候,事物才会被执行,否则是不会被执行
使用方式:在事物开始之前监听某些key
事物中的错误类型
入队时候的语法错误
2.6.5之前版本,忽略入队失败的命令,可以继续执行事物
2.6.5开始版本,入队失败,执行事物的时候会自动放弃执行该事物
执行事物调用之后错误;比如说错误的用string数据结构的命令操作list数据结构的数据
exec事物开始执行的命令开始了,事物队列中某条或者某些命令执行失败了,Redis依旧会接着执行命令,不会放弃执行命令
redis事物与数据库事物最大差别
不支持回滚,即使事物队列开始执行后,有命令执行失败了也不会回滚
主从复制机制
复制分类
全量复制
作用:把从服务器数据的状态更新到和主服务器状态一致;=
使用场景:一般都刚刚搭建服从服务的时候
缺点
1.数据量较大时候,主从节点的网络开销很大
增量复制
作用
1.当主服务器收到写命令的时候,为了保持从服务器与主服务器的数据一致;就会让从服务器也去执行主服务器的命令;这个过程就是增量赋值的过程
2.对全量复制方式的工作方式弥补,当主从断开了连接,就不需要做全量复制,只需要执行断开期间主服务器的写命令
概述:复制分为全量复制,增量复制,也就是对应着同步操作,命令行操作;
心跳检测
1.各自彼此都模拟成对方的客户端发送心跳信息
2.主节点默认间隔10秒给从节点 发送链接信息
3.从节点默认间隔1秒给主节点发送偏移量
主从复制过程
1. Slave 重连时发送 PSYNC 请求,告诉 Master 上次同步的 偏移量(offset)。
2. Master 检查偏移量是否在缓冲区内:
如果在 → 直接发送缓冲区中未同步的命令(增量复制)。
Slave 执行 Master 发送的命令,补齐数据
如果不在 → 需要进行全量复制。
Master 生成 RDB 快照,将快照传递给从机;从机获取快照数据加载进内存
主机缓存这段时间之内的命令,在从机降快照加载完成之后,传递缓存之中的命令,从机再执行换从之中的命令;之后都是增量复制了。
复制原理
1.主节点处理完命令之后,会把命令字节长度累加记录起来,一个记录在命令表,一个记录在偏移量表
2.从节点收到主节点的命令,也会累计自身节点的复制的偏移量;
3.从节点每秒钟把自己的偏移量发送给主节点,主节点对比偏移量,
4.主节点就知道从节点的数据是否和主节点数据一致;
复制注意事项
1.从服务器在同步时,会清空所有数据
2.Redis不支持主主复制
3.主从复制不会阻塞master
4.主节点的处理完写命令就会直接给客户端返回,然后异步将命令传递给从服务器
Pipeline
客户端批量发送多条命令 → Redis 按顺序执行 → 客户端一次性读取返回
核心优化:减少每条命令的网络往返时间
特点:
不保证原子性
批量命令提高吞吐量
常用于批量写入或获取大量数据
redis集群
主从模式
特点
1.一主一备,主机写,从机读;主机挂了不影响从机读
2.主机挂了,系统还能提供读服务,并不能提供写服务,从机不会变成主机
缺点
Master 挂掉后,无法自动切换,需人工干预。
哨兵模式
特点
1.建立在主从模式之上,哨兵节点本身不做数据存储;
2.主节点挂了,哨兵节点就会从所有从节点中选取一个节点做为主节点;
3.挂掉的主节点重启之后,就作为从节点;
工作机制
1.客户端连接的是哨兵节点,由哨兵节点来提供Redis的各种服务
特点:
实现了 高可用(HA)。
运维简单,适合中小型项目。
缺点:写请求仍然集中在 Master,容量受单机限制。
分片集群
概述
哨兵模式就能保证高可用了,但是如果数据量过大,一台服务器存不下所有数据,就需要搭建高可以用集群
分片集群 自带高可用机制
特点
分片集群(Cluster) → 最少 6 节点(3 主 + 3 从)。
可在线添加,删除节点
既能保证高可用,又能实现水平扩展。
节点间自动分片,使用 **16384 个槽位(slot)**分布数据。
支持节点间的自动发现、自动故障转移。
数据存储特点:
数据按照 槽位(slot)哈希分布存储在不同节点。
每个节点有自己的 Master,通常也有 Slave 做备份。
客户端可直接根据 key 找到对应的 slot -> 节点,进行访问。
如果集群中一个master挂了,会怎们样?
正常情况(有 Slave 备份)
其他 Master 不受影响,因为它们负责的槽位独立。
会从这个master从节点从选一个节点作为主节点
客户端会被告知新的 Master 地址(MOVED 或 ASK 重定向)
最终结果:集群整体可用,数据不会丢失(可能丢失少量主从未同步的数据)
异常情况 1(Master 没有 Slave)
这个 Master 负责的槽位(hash slots)不可用。
整个 Redis Cluster 会认为 集群处于 FAIL 状态(不可用)。
客户端访问任何 key 时,可能都会报错(即使 key 在别的 Master 上)。
原因:Redis Cluster 必须保证 槽位全集完整,否则视为集群故障。
异常情况 2(网络分区 / 少数节点故障)
如果 Master 节点挂了,但由于网络原因,集群无法达成多数派投票(quorum),那么故障转移不会发生。
这时,挂掉的 Master 负责的槽位依旧不可用,客户端请求会超时或失败。
影响范围
影响范围:只有挂掉的那个 Master 的槽位数据暂时不可用,几秒钟内自动恢复
投票过程说明
只有 Master 节点 有投票权;Slave 节点没有投票权,它们只负责竞选成为新的 Master。
故障检测流程(两阶段)
主节点之间相互发送心跳,如果超过半数节点,发现有一个节点没有发送心跳信息给自己,就会被标记下线了
挂掉的主节点对应的从节点会发起选举,其他正常的主节点会根据从节点的(发起请求、数据同步最新、延迟最小),来选择谁作为新的主节点当超过半数的时候会成为新的主节点。
为什么可以在线加节点?
所有的master节点的数据都是在分部在16384 个哈希槽(hash slots)
新增 Master 节点时,可以把已有 Master 上的一部分槽位迁移到新节点
槽位迁移时,客户端访问会被 ASK 重定向到新节点,保证在线过程不中断。
Redis配置文件说明
内存相关
maxmemory
Redis最大存储大小
为0的时候表示可以无限制使用redis内存
maxmemory-policy
配置内存清理策略
maxmemory-samples
作为LRU,LFU,TTL内存回收策略,检查数量的key
redis命令
数据类型操作
String
在来的字符串后面追加拼接
APPEND myphone "nokia"
返回指定字符串的值中间几位对应的字符串
GETRANGE greeting 0 4
重新设置key的值返回老的key的值
GETSET db mongodb
把key为db 的数据的值设置成 mongodb,返回老的值
自增自减
DECR failure_times
failure_times对应的数值自减1
DECRBY count 20
count对应的数值指定自减的数量
INCR page_view
page_view的值自增1;
INCRBY rank 20
rank对应的数据自增20
INCRBYFLOAT mykey 0.1
mykey对应的值自鞥指定的浮点数值
位图操作
Hash表操作
设置值
HSET website google "www.g.cn"
key为website
value中field为Google,value为"www.g.cn"
获取指定value指定field对应的值
HGET site redis
获取key的所有field的值
HGETALL people
获取people所有field和值
删除指定key的指定的field的值
HDEL abbr a
指定field是否存在
HEXISTS phone myphone
自增自减
HINCRBY counter page_view 200
对key 为counter 中field字段难为page_view的自增200
HINCRBYFLOAT mykey field 0.1
返回所有的field
HKEYS website
HMSET website google www.google.com yahoo www.yahoo.com OK
操作结果
1) "google"2) "yahoo"
返回所有的域的值
HVALS website
1) "www.google.com"2) "www.yahoo.com"
List操作
往列表中添加
LPUSH languages python
可以重复添加
LPUSHX greet "hello"
往列表的表头添加
RPUSH languages c
往列表的表尾添加
LINSERT mylist BEFORE "World" "There"
指定key中在指定元素的前面或者后面添加元素
从列表中获取
LPOP course
获取表头元素并删除
RPOP mylist
获取表尾元素并删除
blpop key timeout
获取表头元素,如果没有元素就会阻塞,阻塞的时间为指定时间
brpop key timeout
获取表尾元素,如果没有元素就会阻塞,阻塞的时间为指定时间
LRANGE fp-language 0 1
返回list中指定某个索引位置的数据
LINDEX mylist 3
返回对应索引位置的值
删除
lrem key count value
根据key中value的值删除指定个数
ltrim key start stop
删除指定区间的值
Set
存储
添加单个
SADD bbs "discuz.net"
不能被重复添加
hmset
批量存储hash
移除
单个移除
SREM languages ruby
移除 languages 中的ruby元素
SMEMBERS not_exists_key
移除key的所有元素
删除集合并随机返回一个元素
SPOP db
获取
获取集合长度
SCARD tool
获取tool集合长度
获取集合中所有元素
SMEMBERS db
返回两个集合的交集
SINTER group_1 group_2
返回两个集合的并集
SUNION songs my_songs
返回两个集合的差集
SDIFF peter's_movies joe's_movies
判断
判断某个元素是不是当前set集合的元素
SISMEMBER joe's_movies "bet man"
判断key为 joe's_movies 中是否含有"bet man"
Zset
添加
添加单个元素
ZADD page_rank 10 google.com
往key为page_rank集合中添加数值为10 的google.com 元素
添加多个元素
ZADD page_rank 9 baidu.com 8 bing.com
给指定的元素添加分数
ZINCRBY salary 2000 tom
移除
移除一个或者对个元素
ZREM page_rank google.com
ZREM page_rank baidu.com bing.com
移除按照排名指定区间的数据
ZREMRANGEBYRANK salary 0 1
移除指定索引区间的值
ZREMRANGEBYSCORE salary 1500 3500
移除指定分数区间的值
获取
获取集合长度
ZCARD salary
获取salary集合长度
返回指定区间的元素
ZRANGE salary 200000 3000000 WITHSCORES
正序从小到大
ZRANGE salary 0 -1 WITHSCORES
整个集合从小到大排序
ZREVRANGE salary 0 -1 WITHSCORES
递减排列
获取指定分数区间的元素个数
ZCOUNT salary 2000 5000
获取salary集合中分数在2000到5000之间的分数
获取指定元素的分数
ZSCORE salary peter
获取salary集合 peter对应的分数
指定分数区间分页查询
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]
排序获取元素排名
zrank key member
排序按照分数从小到大
zrevrank key member
排序按照分数从大到小
相同操作
设置
字符串
setnx key value
setex key seconds value
hash
HSETNX nosql key-value-store redis
重新设置 nosql 中指定field对应的数据
移动元素到另外集合
set集合
SMOVE songs my_songs "Believe Me"
将songs 集合中的"Believe Me"元素移动到my_songs
list
rpoplpush source destination
把 source的list集合尾部元素添加到 目标元素的头部;并把值返回给客户端
brpoplpush source destination timeout
上一个命令的阻塞版本
批量操作
批量存储
字符串
MSET date "2012.3.30" time "11:00 a.m." weather "sunny" OK
Hash
HMSET website google www.google.com yahoo www.yahoo.com OK
批量获取
字符串
MGET date time weather
Hash
HMGET pet dog cat fake_pet
获取key 为pet中对应的field对应的数据
批量设置
字符串
MSETNX rmdbs "MySQL" nosql "MongoDB" key-value-store "redis"
返回长度
字符串
STRLEN mykey
返回mykey对应的value的长度
hash
hlen key
返回key对应的field的个数
list
LLEN job
list集合对应的长度
key操作
存活时间
获取key存活还有多少存活时间
TTL key
-1
没有设置存活时间
10084
还存活 10084秒
PTTL key
返回值是key存活的毫秒值
设置生存时间
EXPIRE cache_page 30000
设置的时间为毫秒值
PEXPIRE mykey 1500
生存时间为1500毫秒值
PERSIST mykey
移除key的生存时间
删除key
DEL name
指定key删除
FLUSHDB
清除整个redis的数据
判断key是否存在
EXISTS phone
模糊匹配获取key
先批量设置key,value MSETone1two2three3four4
KEYS *o*
返回值 four,two,one
KEYS t??
"two"
KEYS t[w]*
KEYS t[w]*
随机返回一个key
RANDOMKEY
返回值为随机的一个key
移动key到其他数据库
MOVE song 1
把key为song 的值移动到数据库1里面;Redis默认的存放在第一个数据库
重命名key
RENAME message greeting
0,key不存在
1,成功
renamenx key newkey
新的key不存在的时候才会成功
根据key获取value的数据类型
TYPE weather
排序
SORT
返回指定list,有序集合,无需集合拍过排序之后的结果
排序方式按照 数字大小,字母的自然排序
序列,反序列key
DUMP
RESTORE
特殊命令
分页查询操作
Zset
List
说明,只有Zset和List支持分页查询;
计算地理位置
获取经纬度的geoHash值
实际问题
缓存相关问题
缓存穿透
概述
大量的请求在redis缓存中没有拿到数据,直接请求数据库,数据库也没有拿到数据;大量的请求直接怼到了数据库,给数据库造成了巨大压力
分支主题
解决方式
1.Redis缓存从数据库也没有查询到的空数据
分支主题
2.在业务系统和缓存之间使用布隆过滤器,直接过滤掉一些缓存以及数据库都没有的请求,直接返回null
分支主题
布隆过滤器,guav提供的。
缓存击穿
概述
Redis缓存的是失效时间到了或者是Redis服务挂了,同一时间大量的请求过来,就会访问数据库,从数据库里面获取数据,这样对数据库的压力太大了
分支主题
解决方式
在Redis和数据库之间再做一层中间缓存;
通过定时job,给快要失效的缓存数据做一个再次续期的操作;
redis和数据库之前的代码中自己手动做限流措施
缓存雪崩
概述
大量的缓存数据在同一时间过期,引起大量的数据直接访问数据库,引起数据压力过大甚至宕机
解决方式
错开缓存的过期时间
搭建高可用redis集群
做好熔断操作
缓存击穿缓存穿透区别
缓存穿透,就是访问了根本就不存在的数据;缓存击穿,缓存中不存在,但是数据库存在;
缓存数据一致性
缓存更新时间节点
1、更新完数据库, 再更新缓存
缓存更新失败,导致缓存未被更新,从缓存中获取的数据还是更新之前的老数据
2、更新完缓存,再更新数据库
缓存更新成功,数据库更新失败,数据库中的数据是老数据;缓存失效之后,再去读取数据库老数据,覆盖到缓存,造成数据还是老数据
存在问题
1、针对缓存或者数据库更新失败的解决方式
子主题
1、更新失败导致缓存不一致
解决方案
1、采用先更新数据库,后更新缓存的做法
2、更新缓存失败后,捕捉失败的异常,然后发送消息队列,消息队列的消费方做缓存数据的再次更新,并且更新后,将缓存数据和需要被更新的数据做对比,是否保持一致;
使用场景
并发量非常低
2、多线程并发修改,造成数据的不一致
不一致原因示意图
示意图1
示意图2
解决方案
分布式锁
将数据库数据的更新还有缓存数据封装在一起,使用分布式锁来操作;
以被更新的目标数据的id为key;
数据倾斜问题
数据倾斜解释
在redis集群模式下,数据会按照一定的分布规则分散到不同的实例上;如果由于业务数据特殊性,按照指定的分布规则,可能导致不同的实例上数据分布不均匀
数据倾斜分类
数据量倾斜
在集群中的各个实例上数据类大小不一致
示意图
子主题
数据访问倾斜
对集群中某些实例的访问频率大大超过了其他实例
示意图
子主题
数据量倾斜
原因
存在大key
大key导致占据的缓存空间过大,实际上可能这些大key并没有多少;
解决方式
将大key拆分成其他缓存
业务层面避免大key创建
solt分配不均
某些实例分配到的槽位到,相应的得到数据量也会多
解决方式
初始分配的时候,将槽位分配均匀
如果已经造成,则可以进行实例之间的数据迁移
hashTag使用不当
在hash设置key的时候不要带上画括号
数据访问倾斜
主要原因
存在热key
并发问题
redis本身执行读写操作命令式单线程执行的,但是从调用redis的客户端来说,使用的时候会存在线程安全问题
比如说:扣库存操作,读取库存和扣减库存,不是一个原子操作
redis也有自增自减的api,将两个不是原子操作的操作合并成一个原子操作;
解决此类由客户端导致的并发问题方式
1、客户端加锁
2、客户端使用单线程执行
3、将客户端的逻辑放在lua脚本中执行
好处
ua脚本的开销非常低
缺点
Lua脚本只能保证原子性,不保证事务性,当Lua脚本遇到异常时,已经执行过的逻辑是不会回滚的
redis为什么快
基于内存存储
采用单线程避免锁竞争,结合 I/O 多路复用模型
高效的数据结构和内存分配器优化,使其在处理高并发、小数据量请求时能达到极高的性能
分布式锁
Redis 分布式锁依赖以下特点:
1. 原子操作
使用 SET key value NX PX ttl 命令保证 锁的原子获取:
NX → 只有 key 不存在时才设置,保证互斥
PX ttl → 设置锁过期时间,防止死锁
2. 自动过期
避免客户端崩溃或忘记释放锁导致死锁
TTL 时间应该略大于业务执行时间
3. 唯一标识
每个客户端在加锁时生成一个随机 value
解锁时检查 value 是否匹配,防止误删其他客户端的锁
集群脑裂
集群脑裂概述
集群中由于网络分区或节点故障,集群的不同部分相互失联,触发了从节点升为主节点机制,从而产生多个“主节点”的现象
Redis 中,这主要发生在:
Redis Sentinel + 主从架构
Redis Cluster 模式
Redis 脑裂的表现
多个节点同时是主节点(Master)
客户端写入不同主节点,数据冲突
从节点可能无法及时同步数据
系统可能出现不可预测的异常
集群脑裂问题
新的master节点就不会同步原来老master节点的数据;当网络原因解决之后,原来的主节点就会变成从节点,新的master节点由于没有同步数据,就会造成缓存数据的丢失;
解决方式
网络可靠性
避免网络分区,减少主从节点之间的延迟和断连。
哨兵模式
严格配置哨兵选举规则
保证哨兵数量奇数,超过半数才能提升主节点。
分片集群
Cluster 模式下注意节点分布
保证每个主节点至少有部分从节点和投票节点在线。
使用多数派(quorum)机制
只有大多数节点确认主节点挂掉,才允许从节点提升。
hash分片
Redis Cluster 如何处理数据倾斜?
数据倾斜的原因
slot 分配不均
每一个master节点分配到的槽位数量不同,有的多,有的少
解决方式
将每一个master节点槽位分配均匀
增加节点,迁移槽位
存在热点数据
某些 key 被频繁访问(例如 user:123)
解决方式
对key中的{}中的内容计算 slot,将热点数据分散
将热点数据拆分成多个key,分散开
业务设计问题
key 命名模式不均匀,到时在hash算法计算槽位的时候偏向某些范围的槽位
缓存分片hash问题
一致性hash
解决的问题
1.普通hash算法的伸缩性差,解决了分布式系统中机器的加入和退出
2.解决了数据倾斜问题
增设了很多虚拟节点
hash槽
jedis已经支持了hash槽位的计算
redis默认的缓存分片是hash槽位,槽位的最大个数是16384个。每一个主节点都会分配到相应的槽位;根据计算key的对16384取模计算,当前的key应该属于哪个槽位。就知道落在了哪个节点
每一master节点对应的槽位是连续的吗?
可以连续,可以不连续,取决于实际操作
热key
如何监控热点数据?
热点数据的定义
Hot Key:访问频率远高于其他 key 的数据
Hot Slot:某个 slot 被频繁访问的情况(适用于 Redis Cluster)
表现为:
单节点 QPS 占总流量的很大比例
CPU/内存消耗异常高
单 key TTL 短或数据量大
原理
MONITOR 命令,可以统计每个 key 的访问次数,可用脚本收集频率,发现热点 key
看每一个master节点的QPS数据量,如果存在异常,极有可能存在热点key
实现
Redis Exporter + Prometheus 做到可视化
Redis Exporter 可以收集:
每个命令调用次数(keyspace_hits, keyspace_misses)
每个 DB 的 key 数量
配合 Prometheus/Grafana:
绘制 key 或 slot 热点图
配置告警(单 key 访问量过高)
热点数据有什么危害?
单节点压力过大
延迟和响应不稳定
数据不均衡(Cluster 场景)
内存压力
持久化与复制压力
高可用风险
如何保证缓存中的数据都是热键数据
使用LFU算法
最小使用频率做缓存数据的淘汰
实现
记录了最近访问的时间
记录了请求访问的次数
相对其他算法
不设置任何淘汰机制
这种肯定不行
随机淘汰
这种也不行
生命周期淘汰
这种也不行,如果是一个高频使用的缓存,缓存周期到了也被淘汰了
最近最少使用算法
不能够完全说明这个问题;短期之内使用的比较少,但是整个周期内使用的次数比较多,这种也会被淘汰
最终选择LFU
热key优化?
将热点 key 拆分成多个 key
和对大key的处理方式一样
子key分散到多个槽位上
使用过期 / TTL 策略
对热点 key 设置 短期 TTL
避免长期占用内存和单节点负载
主从读写分离
不适用分片集群的时候可以优化
限流 / 队列削峰
大key
如何监控大key
MEMORY USAGE命令
查询单个 key 占用内存:
可结合脚本扫描所有 key,统计大于阈值的 key
实现
编写脚本使用MEMORY USAGE命令扫描各个 Master 节点
获取到每一个节点的key排行榜,最后汇总
可视化监控
Redis Exporter + Prometheus
收集 key 的统计信息(按前缀或类型)
可以设置告警:
单 key 内存占用超过阈值
集合长度超过阈值
大 key 告警
告警实现
在Prometheus 配置规则
key_memory_bytes > 阈值 → 告警
内存超过阈值 → 警告
单 key 访问耗时超过阈值 → 警告
热点大 key → CPU/延迟告警
优化策略
拆分 key
List / Set / ZSet → 按分页或哈希拆分
限制写入
避免一次性 push/insert 大量数据
设置 TTL
避免长期占用大量内存
本地缓存或外部存储
超大对象存储在对象存储或数据库,Redis 缓存部分数据
Redis 的一致性保证?为什么不是强一致性?
Redis 的一致性模型
单节点
操作是原子性的,客户端每次写操作立即生效,读取也能马上看到最新值 → 强一致性
哨兵或集群模式
主从复制延迟:主节点写入后,从节点异步复制 → 从节点可能暂时读不到最新数据
为什么不是强一致性
Redis 选择了 优先可用(AP)或最终一致性
主节点挂掉 → 自动切换新主节点
某些从节点可能落后 → 数据不完全同步
确保系统可用性,而非严格同步所有节点
一致性保证的机制
单节点模式
强一致性,写操作立即生效
主从复制(异步)
主节点写 → 异步复制到从节点
从节点可能滞后 → 弱一致性/最终一致性
哨兵模式
自动故障切换 → 数据可能丢失或落后
避免脑裂,需要多数哨兵确认 ODOWN
一致性是最终一致性
Cluster 模式
主节点宕机 → 从节点提升为新主节点
异步复制导致短期内部分节点数据不同步
通过投票机制 + quorum 保证多数节点数据可靠
最终一致性得到保证
Redis 如何做延时队列?
如何保证 Redis 高并发下的性能?
数据分片
分片(Sharding)
把数据按 Key 哈希到多个 Redis 实例,避免单节点瓶颈。
集群化
集群模式
Redis Cluster 可以水平扩展,多个 Master+Slave 节点共享数据
主从架构
Master 写入,Slave 提供读服务,提高并发读能力
缓存策略优化
添加本地缓存
过期策略
LRU/LFU 等策略淘汰冷数据,保证热点数据命中率
异步与批量处理
Pipeline:一次发送多个命令,减少往返时间
Lua 脚本:在 Redis 内部执行复杂逻辑,减少客户端多次请求
基础原理类
redis为啥快
1.数据存储在内存
2.数据结构简单
3.单线程不存在锁
4.io多路复用
Redis 为什么是单线程的?
- 避免上下文切换、内存操作快,多核可通过分片/多实例利用。
Redis 常见的使用场景?
缓存、分布式锁、计数器
数据结构类
1.Redis 常见的数据结构?底层实现是什么?
String(SDS)
Redis 自己实现的动态字符串
特点:
存储长度、已用空间、预分配空间 → 减少频繁 realloc
支持二进制安全(可存储 \0)
高性能拼接和扩容
List(双端链表/压缩列表)
有序集合
底层实现:
1. ziplist(压缩列表)
小量数据和短字符串优化
内存紧凑,但随机访问慢
2. 双向链表(linkedlist)
数据量大或元素较长时使用
支持快速头尾插入/删除
Hash(哈希表/ziplist)
无序集合,不允许重复,常用去重
底层实现
intset(整数集合)
全是整数且数量少时使用
内存紧凑,支持快速查找
哈希表(dict)
元素多或者包含非整数时
提供 O(1) 查找、添加、删除
Redis 会自动在 intset 和哈希表之间转换
Set(哈希表/intset)
键值对集合,用于对象存储
底层实现:
ziplist(压缩列表)
小量字段,短字符串
哈希表(dict)
字段多或者字符串长时
支持 O(1) 查找、添加、删除
Sorted Set(跳表+哈希表)
带 score 的集合
子主题
底层实现:
ziplist(压缩列表)
元素少且短字符串时使用
跳表(skiplist) + 哈希表
大量元素或字符串长时使用
哈希表存成员到 score 的映射 → 快速查找
跳表保证按 score 排序 → 支持范围查询
Bitmap / Bitfield
位操作,如签到、布隆过滤器、计数器
底层实现:
使用字符串(String)存储二进制位
操作通过 位运算,非常高效
HyperLogLog
用途:基数统计(去重计数)
底层实现:
压缩算法 + 估算算法
内存固定(~12KB)
精确度允许一定误差(~0.81%)
3.Redis 为什么用跳表而不用红黑树?
4.Redis 的 HyperLogLog、Bitmap、GEO 是做什么的?
Redis与MemCache的区别
线程操作
redis数据处理是单线程,memcache是多线程处理
数据结构
Redis支持更多更复杂的数据结构,memcache只支持keyvalue的字符串数据;
数据安全性
Redis支持数据的持久化,会把数据同步到磁盘上;memcache不支持数据的持久化
数据备份
Redis支持数据备份,需要开启主从模式;memcache不支持数据备份
过期策略
REDIS支持更多的过期策略;memcache支持的过期策略少
开发模式
jedis
redisson
springBoot+整合redis
本质还是springBoot整合了jedis
0 条评论
下一页