前言
在高并发场景下,数据库往往会成为瓶颈:磁盘 IO 慢、SQL 计算成本高、连接数有限
那怎么办?没有什么是加个中间层不能解决的
**把“经常读、很少改”的数据,放到内存里。**这就是 缓存(Cache) 的基本思想
为什么是内存?因为内存和磁盘的访问速度,差了几个数量级。
就像:
- 磁盘 = 去仓库翻箱倒柜
- 内存 = 伸手从桌面拿东西
既然缓存放在内存里,那我们自然希望它:读得快,且能支持不同的业务场景
比如我要个排行榜,我要个好友集合… 所以它也需要各种数据类型
这时候,就轮到 Redis 出场了。Redis 本质上,就是一个基于内存的缓存系统
为什么redis快
- 基于内存
- 数据结构优化
- 单线程,无锁竞争
- 多路复用。使用
epoll技术,它允许一个线程同时监控多个网络 Socket。只有当 Socket 真正有“数据可读”或“可写”时,操作系统才会通知 Redis 线程去处理
Redis 6.0 及之后:网络请求的读写(IO 阶段)被改为了多线程,因为随着网卡性能提升,解析网络协议成了瓶颈。但执行命令本身依然保持单线程。
数据类型
假设我们正在做小蓝书,并且为了抗高并发,决定引入 Redis 作为缓存
String
一开始,需求很简单。存一个用户昵称、验证码、登陆状态,这些东西都是一个Key,对应一个Value。这时候用 String 就够了。
// 假设他叫张三,直接存
namesCache.add(id, “张三”)
这就是String,在redis里最基础的数据结构
Hash
你突然想到不对呀,你得存储用户的全部信息,是男是女,多高多重等等,如果继续nameCache、ageCache这样加下去,多累呀,我们直接套个壳,把他们放一起,一次存一次取,也就是
userZhangSan = {
name: "张三"
age: 15,
height: 16
}
usersCache.add(id, userZhangSan)
Hash。一个 key,对应一组 field-value,非常适合对象存储
List
接着,用户开始发动态,私信帅哥美女了,你想:得做一个动态列表、消息列表,新发的在最上面。 这时候,双向链表结构的 List 站了出来
postsCache.leftPush(id, "我的第一条动态")
List本身保证插入顺序,可以当队列 / 栈用的链表
Set
有了动态,自然有点赞收藏转发。但问题来了:有个狂热粉丝对着同一条动态点了一万次赞,你总不能在 List 里存一万个他的 ID 吧?你需要一个能自动拦住重复的东西
likesCache.add(id, userID)
还能算共同好友
followCache.inter(userID1, userID2)
Redis 的 Set,就是一个天然去重的集合
ZSet
有了点赞有了收藏,自然,就有了热度排行榜。排行榜、积分榜、热度榜,本质是什么? **一个值 + 一个可比较的分数。**点赞、评论都在变,热度实时刷新。如果用 List 手动排序,机器得累死。ZSet自告奋勇说:我力气多,给我一个分数(Score),我帮你排好
hotRankCache.add(postID1, 100)
hotRankCache.add(postID2, 100)
hotRankCache.add(postID1, 200)
Redis 的 ZSet,就是在 Set 的基础上,多了一个 score。
其它
| 类型 | 核心用途 | 常见场景 |
|---|---|---|
| HyperLogLog | 基数估算 | 独立访客数统计(去重计数) |
| Stream | 消息流 | 消息队列(持久化) |
| Bitmap | 位图 | 用户签到 |