05-Redis

一、数据类型

1.1 String

新增、更新

redis 复制代码
set key value

删除

redis 复制代码
del key

查询

redis 复制代码
get key

自增:redis的string类型兼容number,如果存储的是number类型,可以实现INCR自增操作

redis 复制代码
set num 1
INCR num //2

新增+缓存时间

redis 复制代码
setex key 60 value //表示60s后过期

1.2 Hash

hash类型适用于存储整份对象的,例如商品属性,用户资料等。

新增

redis 复制代码
hset name key1 value1 key2 value2

读

读取对象的某个属性

redis 复制代码
hget name key

读取完整对象

redis 复制代码
HGETALL name 

删除

复制代码
hdeal name key1 key2

1.3 List

数组类型,按顺序排列的字符串数组,常用于消息队列和最新几条动态获取。它的左边是头,右边是尾巴。

增

list既支持头插,又支持尾插。

redis 复制代码
lpush name value //头插
rpush name value //尾插

查询

redis 复制代码
lrange name index1 index2 //访问index1-index2范围内的数据

删除

arduino 复制代码
lpop name //从左边弹出,头删
rpop name //从右边弹出,尾删

1.4 Set

无序的存储多个字符串数据,并且数据不能重复。

增

redis 复制代码
sadd name value

查询

redis 复制代码
SMEMBERS name // 获取所有数据
scard name //统计数据个数

删除

复制代码
srem name value

是否存在

redis 复制代码
SISMEMBER name value // 1存在,0不存在

1.5 ZSet

Zset在set基础上增加了分数,支持了自动排序

增

复制代码
zadd name  score value 

更新

arduino 复制代码
ZINCRBY name score value //在原来基础上增加15分

查询

arduino 复制代码
ZREVRANGE name 0 -1 WITHSCORES // 从高到低列出所有人,带分数
ZREVRANK name value //查询某个值的排名
ZSCORE name value //查询某个值的分数

删除

复制代码
zrem name value

二、跳表

跳表是ZSet获取排名范围的数据结构。

2.1 结构

跳表的示例如下所示,第0层存储所有数据的分数,按照从小到大顺序排列,数据结构是双向链表,支持从前到后,也支持从后到前查询。从上往下看,除了第0层其它都是是索引层,它们的结构是单向链表。

第 2 层: 1 -----------------> 7

第 1 层: 1 -------> 4 ------> 7

第 0 层: 1 -> 2 -> 4 -> 5 -> 7

2.2 查询

所有层中分数相同的节点地址是同一个,所以支持直接跳转查询。如果某层找到数据后,无需下一层查找。

案例1:查询5

从顶层第2层开始,1<5<7;来到第1层,继续从1开始遍历,发现4<5<7;来到第0层,直接从4开始往后遍历发现了分数5。

案例2:查询4

从顶层第2层开始,1<4<7;来到第1层,继续从1开始遍历,发现4成功返回。

2.3 新增

第 2 层: 1 -----------------> 7

第 1 层: 1 -------> 4 ------> 7

第 0 层: 1 -> 2 -> 4 -> 5 -> 7

案例1:插入数据6

  1. 首先从第2层一直找到第0层插入的位置,分别是1,4,5
  2. 抽签决定6出现在第几层,如果抽到第2层,则该层到第0层都需要插入6
rust 复制代码
第 2 层: 1 --------6---------> 7
第 1 层: 1 ----> 4 ----6----> 7
第 0 层: 1 -> 2 -> 4 -> 5 -> 6 -> 7
  1. 如果抽到第0层,按照规则,它已经是最底层,只需插入到当前层即可。
rust 复制代码
第 2 层: 1 -----------------> 7
第 1 层: 1 -------> 4 ------> 7
第 0 层: 1 -> 2 -> 4 -> 5 -> 6 -> 7

2.4 ZSet

zset有两个数据结构,1个存储数据成员到分数的映射表,另一个就是跳表,专门存储分数。需要获取某个数据的分数时,直接找映射表获取,如果找数据的排名,或者区间范围,则需要走跳表。

三、布隆过滤器

布隆过滤器用来判断是否存在某个事物,如果判断不存在,则一定不存在,如果存在,则事物是可能存在。常用于缓存穿透的解决。

3.1 原理介绍

它在redis中是一个个排列的格子,每个格子有自己的id,格子里面的值初始化全是0。一个商品在创建完成后通过哈希算法计算它所属的格子,然后给格子的赋值为1,所有的商品共用同一份key。

1. 初始化

格子 0 1 2 3 4 5 6 7
值 0 0 0 0 0 0 0 0

2. 商品1创建

商品1通过哈希计算命中格子1和4

格子 0 1 2 3 4 5 6 7
值 0 1 0 0 1 0 0 0

3. 商品2创建

商品2通过哈希计算命中格子4和6

格子 0 1 2 3 4 5 6 7
值 0 1 0 0 1 0 1 0

4. 查询

  1. 商品3在查询时,先走redis的布隆过滤器,命中2和3,发现1个0,直接判断它不存在,直接走mysql查询。

  2. 商品4在查询时,命中1和6,发现都是1,此时说明可能存在,先走redis查询,如果没有再走mysql查询。

四、过期淘汰

4.1 过期

redis中存储的数据几乎都会设置有效时间,时间到期后,redis不会全量的扫描所有的数据去清理数据,通常是如下两种

  1. 下次有人来 GET 这个 key,发现过期了,当场删掉,返回空。一直没人读,它就先留在内存里。
  2. 隔一小段时间,从设了过期的 key 里抽一批来查,到期的删掉。

所以,如果一大批key在同一个时间点过期时,会导致请求一起打到数据库上,这种行为称为雪崩。因此过期时间要加一点随机数,让它们的过期时间错开。

4.2 内存淘汰

redis把数据存储到内存,但如果超过了内存上限,会触发淘汰机制。

1.noeviction

默认策略,内存超出后,不会删除已有的数据,后续写入的直接报错

2.allkeys-lru

所有 key 里,扔掉最久没被访问的,最常见的策略。它并不是把所有的数据key列一个清单,标记它们多久没被使用了,而是随机的抽查一部分key,丢掉最近没有被使用的key。

4.3 redis内存溢出排查

  1. 判断内存是否满+溢出策略是什么
  2. 查找大key并分析内存占用
sql 复制代码
redis-cli --bigkeys
redis-cli MEMORY USAGE user:3

3.TTL 是 -1,说明没设过期,会一直占着。大 key 不要了就 UNLINK,不要用 DEL。如果是很多小key没有设置过期,则补充过期时间,策略调整成allkeys-lru。

五、缓存三大问题

5.1 穿透

用户使用随机的id去查询不存在的数据,导致请求一直查询数据库。

解决方案

  1. 空值缓存:请求的id如果redis没有,数据库也不存在时,添加redis的空值缓存30s,后续相同的id在短时间请求时,直接输出空缓存即可

  2. 布隆过滤器:适用于高并发场景,如果过滤器查询是0,直接返回空,如果是1,继续查询数据库,如果数据库没有,则添加redis的30s空值缓存。

5.2 击穿

热点key在过期的瞬间,大批量的请求打到数据库,然后又一起重建缓存。

解决方案

加锁重建:所有的请求到redis时,发现没有缓存,只有1个线程能抢到锁,然后去mysql中读取到数据,写回缓存。其它线程在java中设置休眠时间等待,后面再去读取redis的缓存即可

csharp 复制代码
SET lock:product:1 1 NX EX 10

5.3 雪崩

大批量的key在同一时间瞬间失效,导致大量的请求打到数据库上。

解决方案

所有的key过期时间添加一段随机数,例如原始是60s过期,可以加1-10s的随机数。如果是redis服务挂了,则需要备份主从redis机器,主机器挂了,从机器作为备用的缓存。

六、大key与热点key

6.1 大key

大key是指数据在redis中值太大,例如字符串太长,list数据存储数据量过大,一次读取或者删除都会阻塞redis(redis是单线程)。

解决方案

  1. 大key可以进行拆分,例如大字符串可以拆成约定的多个字符串,大list拆分成多个list,当然也可以拆分成hash数据,比较灵活。
  2. 删除大key用unlink,而不是del直接删除,unlink适用于所有数据的删除,它是让key失效,等一会后台线程再去删除。

6.2 热点key

热点key问题指,这个key访问量过大,所有的请求都访问这个key,导致redis服务器很容易崩。

解决方案

  1. 在本地短期缓存热点key,这样大部分请求都不会走redis
  2. 设置集群,把热点key拆分成多个key1,key2,然后每个机器承担一部分压力即可

七、缓存一致性

  1. 数据读取时,先走缓存,缓存没有,再走数据库,然后再添加缓存。
  2. 数据变更时,先删除缓存,删除失败,就把这个任务放到消息队列,不能阻塞用户操作,等待消息队列删除。
相关推荐
OnlineProxy1 小时前
亚马逊多账号运营:如何在规避“关联封号”风险的同时实现电商规模化扩张
服务器·数据库·redis
databook2 小时前
从手动检查到自动监控:一个数据质量工作流的实现
后端·python·数据分析
【JAVA】玩家2 小时前
Spring核心原理全解析:从零到生产实战
java·后端·spring
东风破_2 小时前
从 Neo4j 到 GraphRAG:用 Text-to-Cypher 构建图检索 RAG
人工智能·后端
llqbzllll4 小时前
Spring AI 工具调用不是反射一下就结束:用 2.0.1 跑通失败恢复与调用上限
人工智能·后端
狼爷4 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言
mantou1325 小时前
我给 AI Agent 做了个「油猴」:让 Claude Code / Codex 直接用你已登录的浏览器
前端·javascript·后端
程序员老赵5 小时前
Docker 部署 DeepSeek Harness:轻松搭建局域网里的 AI Agent 平台
后端·ai编程·deepseek
YIAN5 小时前
实战|用 DeepSeek + SQLite 从零搭建轻量 Text2SQL 查询助手
后端·sqlite·deepseek