🌈个人主页:一条泥憨鱼(欢迎各位大佬莅临)
🎬精选专栏:数据结构与算法,Java ,AI与Agent
前言
系统流量上来之后,最先扛不住的通常不是应用服务器,是数据库。接口一个接一个变慢,用户开始骂,你开始慌。
Redis就是干这个的。
它是个内存数据库,快。快到你基本不用等。除了缓存,消息队列、分布式锁、排行榜,你后面大概率都会用到。这篇文章把核心的东西讲一遍——概念、五种数据类型、常用命令。
一、Redis 是什么
Redis 全称Remote Dictionary Server,开源的,内存里的键值数据库。
不用记全称。你只需要知道一件事:数据在内存里,所以快。
跟 MySQL 比一下:MySQL 的数据在磁盘上,每次查都要读盘,几百毫秒起步;
Redis 的数据在内存里,一次读写通常低于 1 毫秒。差了不止一个数量级。
几个关键事实:
- QPS 轻松到 10 万以上(单机)
- 支持 String、Hash、List、Set、ZSet
- 可以把内存数据写到磁盘(持久化),重启不丢
- 单线程处理命令,原子操作,不用考虑并发问题
- 主从、哨兵、集群都支持
二、先搞清楚能干什么
学命令之前,知道它能解决什么问题,学的时候才有感觉。
缓存
这是最常见的。热点数据放 Redis,挡在数据库前面,流量一大你就知道好处了。
会话存储
用户登录后的 token、session,放 Redis 里,比放内存里安全(服务重启不丢),比放数据库快。
计数器
阅读量、点赞数、API 调用次数。INCR 一条命令搞定,原子性的。
排行榜
ZSet 天然就是排行榜结构,按分数排序,实时查排名。
消息队列
List 的阻塞弹出可以做简单的任务队列。真要用在生产上,还是 RabbitMQ 或 Kafka 更稳,但 Redis 的轻量队列在一些场景够用了。
三、五种数据类型
3.1 String
最基础的类型。一个 key 对应一个 value。
说是 String,实际上整数、浮点数、JSON 字符串都能存。底层二进制安全,你塞个序列化对象也行。
SET name "张三" GET name INCR article:view:1001 # 自增1,key不存在就从0开始 SET token "abc123" EX 3600 # 设值同时设过期时间 SET lock "1" NX EX 10 # 不存在才设置,常用于分布式锁▎ 用 INCR 做计数器,比"读出来→加一→写回去"安全一万倍。原子操作,不会有竞态问题。
3.2 Hash
存对象用的。一个 key 下面有多个 field-value 对。
user:1001 name → 张三 age → 25 email → zhangsan@qq.com跟 String 的区别:String 存对象得把整个对象序列化成 JSON,改一个字段就要全量读写。Hash 可以只动一个字段。
HSET user:1001 name "张三" age 25 email "zhangsan@qq.com" HGET user:1001 name # 只取名字 HINCRBY user:1001 score 50 # 积分+50,不影响其他字段 HGETALL user:1001 # 全取什么时候用: 用户信息、商品详情、配置项——需要部分更新的对象。
不过有个小坑:如果每次都取整个对象,Hash 反而比 String 慢(多一次命令解析)。只取部分字段时才划算。
3.3 List
双向链表,可以从左边塞也可以从右边塞。
LPUSH queue "a" "b" # 从左边塞,结果为 b, a RPUSH queue "c" # 从右边塞,结果为 b, a, c LPOP queue # 从左边弹出 "b" BLPOP queue 10 # 阻塞弹出,10秒超时,没数据就等着阻塞弹出是精髓。消费者没任务时就卡在那等,不用轮询,不浪费 CPU。
什么时候用: 简单消息队列、最新动态列表、操作历史。
3.4 Set
无序、不重复的集合。
SADD tags:article:1 "Java" "Redis" "Spring" SISMEMBER tags:article:1 "Java" # 判断是否存在 SINTER user:1:follow user:2:follow # 共同关注(交集) SUNION user:1:follow user:2:follow # 合并(并集) SDIFF user:1:follow user:2:follow # 我有他没有(差集)集合运算是最有用的部分。共同好友、可能认识的人,本质上都是交并差运算。
ZADD rank 2350 alice ZADD rank 3100 charlie ZADD rank 2750 diana ZREVRANGE rank 0 2 WITHSCORES # 取分数最高的前3名 ZINCRBY rank 500 alice # alice 加分 ZREVRANK rank alice # 查 alice 排名score 相同的时候按 member 的字典序排。这个细节容易忽略。
什么时候用: 排行榜、带优先级的任务队列、延迟队列(score = 执行时间戳)。
3.5 ZSet
Set 的升级版。每个元素绑一个 score,自动按 score 排序。
ZADD rank 2350 alice ZADD rank 3100 charlie ZADD rank 2750 diana ZREVRANGE rank 0 2 WITHSCORES # 取分数最高的前3名 ZINCRBY rank 500 alice # alice 加分 ZREVRANK rank alice # 查 alice 排名score 相同的时候按 member 的字典序排。
什么时候用: 排行榜、带优先级的任务队列、延迟队列(score = 执行时间戳)。
四、通用命令和命名习惯
这些是通用的,不挑数据类型:
EXISTS key # key 存在吗 DEL key # 删 TYPE key # 查类型关于命名:
用冒号分层,格式大概是 业务:对象:标识:字段:
user:info:1001 → 用户信息 article:view:2001 → 文章阅读量 game:rank:season:5 → 赛季排行榜 lock:flash_sale:1001 → 秒杀锁短一点好,别超过 100 字节。Redis Desktop Manager 之类的工具会按冒号自动建树,看起来清爽。
还有一条:生产环境别用 KEYS *。数据量大时会直接卡死。用 SCAN 代替。
五、选型速查
| 需求 | 用什么 | 原因 |
| 缓存一个值 | String | 简单 |
| 存对象,可能改部分字段 | Hash | 单独操作字段 |
| 队列,最新列表 | List | 有序,双端操作 |
| 去重,标签,好友 | Set | 自动去重+集合运算 |
| 排行榜 | ZSet | 按score自动排 |
六、亿些小细节
Redis 为什么快?
三个原因:
内存操作,不读磁盘;
单线程处理命令,没上下文切换和锁开销;
I/O 多路复用,一个线程扛住大量连接。
String 和 Hash 存用户信息怎么选?
如果你每次都取整个用户对象,String(序列化成 JSON)反而更快,因为少一次命令解析。如果你经常只改一个字段(比如积分),Hash 更合适。各有利弊,看你的具体业务。
过期键怎么清理的?
惰性删除 + 定期删除。惰性删除就是访问 key 的时候顺带检查是否过期;定期删除是后台每隔一段时间随机抽查一批 key。Redis 不会傻到去遍历所有 key 查过期,随机抽查就够了。
List、Set、ZSet 底层用的什么?
List:小数据用 ziplist(压缩列表),大了换成 quicklist。
Set:全是小整数用 intset,否则哈希表。
ZSet:小数据 ziplist,大了用 skiplist(跳表)+ 哈希表。
核心思路都一样——数据量小的时候用紧凑结构省内存,大了再切高效结构。
最后
Redis 命令语义直观,上手很快。真正难的是知道什么场景用什么类型、key 怎么设计、缓存怎么和数据库保持一致。这些都是做项目踩坑踩出来的。