老弟第一次学 Redis,被坑惨了!小白可懂的保姆级 Redis 教程

你是小阿巴,刚入职的程序员。

这天,产品经理找到你:阿巴阿巴,用户吐槽咱们网站首页加载太慢,快优化!

你打开监控一看,好家伙!每秒有上万个用户在访问首页,每次都要查询 MySQL 数据库来获取热门文章。

虽然你运用毕生所学优化了数据库查询,但它还是扛不住这么高的并发。

你急得满头大汗:数据库快撑不住了,怎么办啊?

这时,你的导师 ------ 号称 "后端之狗" 的鱼皮路过,淡定地说了 3 个字:Redis(瑞迪斯)。

你一脸懵:Redis?那是啥?

⭐️ 推荐观看本文对应视频:bilibili.com/video/BV1a1...

第一阶段:认识 Redis

鱼皮:Redis 的全称是 Remote Dictionary Server(远程字典服务器),是一个 基于内存的 K/V 存储系统

你:远程?字典?K/V?这些都是什么啊?

鱼皮:你可以把 Redis 当成一个数据库,服务器可以通过网络远程操作它写入和读取数据。K/V 就是 Key-Value 键值对,数据就像字典一样保存。

你可以通过 Key 快速查询到对应的 Value。而且因为数据存在内存中,Redis 的读写速度有时比 MySQL 快 100 倍!

你眼前一亮:那我赶紧装一个试试!

机智如你,直接打开 官网 下载了 Redis 服务器,并且成功安装运行。

但是怎么操作 Redis 呢?

鱼皮:可以使用官方提供的命令行工具 Redis CLI 连接并操作 Redis。

打开终端输入下列命令:

css 复制代码
redis-cli -h 127.0.0.1 -p 6379

连上之后,咱们试试最基础的操作,利用 SET 命令保存一个键值对:

sql 复制代码
SET name "xiaoaba"

然后通过键读取到值:

sql 复制代码
GET name

你刚敲完命令,屏幕立刻返回 "小阿巴"。

你大为震惊:秒回!这也太快了!

鱼皮:你刚刚保存的值是 Redis 的 String 字符串 类型,只是最简单的一种。

你:还有其他类型?

鱼皮:当然,Redis 能存的东西可多着呢~ 它有 5 种基本数据结构,适用于不同场景:

1)String 字符串:存简单的值,比如用户名、计数器

sql 复制代码
SET username:1 "鱼皮"
GET username:1

2)Hash 哈希表:存对象,比如用户信息 {name: "小阿巴", age: 18}

sql 复制代码
HSET user:1001 name "小阿巴" age 18
HGET user:1001 name

3)List 列表:存有序数据,比如最新的 10 条评论

arduino 复制代码
LPUSH comments "太棒了!" "学到了"
LRANGE comments 0 -1

4)Set 集合:存不重复的数据,比如点赞用户列表

ruby 复制代码
SADD post:1:likes user1 user2 user3
SMEMBERS post:1:likes

5)SortedSet 有序集合:存需要排序且不重复的数据,比如游戏排行榜

arduino 复制代码
ZADD leaderboard 100 "玩家A" 95 "玩家B" 90 "玩家C"
ZRANGE leaderboard 0 -1 WITHSCORES

你感叹道:这用法也太多了,我要背这些命令吗?

鱼皮:千万别背!用的时候去查 Redis 官方的命令手册 就行。

而且想偷懒的话,推荐你装个 Redis 官方的可视化工具 Redis Insight,能像操作 Excel 一样操作 Redis,数据一目了然。

你:哇,确实方便多了!但是我怎么用 Java 代码操作 Redis 呢?

鱼皮:主流编程语言都有操作 Redis 的客户端 SDK。

对于 Java 开发者,可以选择 Jedis、Lettuce、Spring Data Redis 和 Redisson,它们封装了很多操作 Redis 的方法,几行代码就能搞定。

时间充足的话,建议你看看某马的 Redis 教程来系统学习,我当年也看过,讲的很好,先把基础篇和实战篇看完就够了。

你:得咧,我这就看!

第二阶段:实战应用

半个月后,你已经看了不少教程,准备解决热门文章查询太慢的问题。

你实现了经典的缓存逻辑:第一次查询时,先从 Redis 中查找,如果没有找到,再从 MySQL 数据库中查询,然后将结果存入 Redis 中,并设置过期时间。后续相同的查询就能直接从 Redis 返回结果。

示例代码:

typescript 复制代码
public List<Article> getHotArticles() {
   // 1. 先从 Redis 查询
   String cacheKey = "hot_articles";
   String cachedData = redisTemplate.opsForValue().get(cacheKey);
   if (cachedData != null) {
       // 缓存命中,直接返回
       return JSON.parseArray(cachedData, Article.class);
  }
   // 2. 缓存未命中,查询数据库
   List<Article> articles = articleMapper.selectHotArticles();
   // 3. 将结果存入 Redis,设置 10 分钟过期
   redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(articles), 10, TimeUnit.MINUTES);
   return articles;
}

使用 Redis 后,查询速度从 3 秒缩短到了 0.3 秒,产品经理看你的眼神都变了,你很是得意。

鱼皮:不错,不过 Redis 的作用可不仅仅是缓存,你还能用它继续优化项目么?比如之前用户反馈的登录态失效问题。

你想了想:Redis 的本质是 存储系统,也就是说,可以利用 Redis 来存储多个服务器间需要共享的数据。公司有 2 台服务器,用户第一次登录时,Session 存在服务器 A 本地;刷新页面后,请求被分配到服务器 B,就找不到 Session 了,导致丢失登录态。那我就把需要共享的 Session 数据存到 Redis 中,这样无论请求分配到哪台服务器,都能从 Redis 里拿到 Session。

鱼皮欣慰地点了点头:不错,这就是典型的 分布式 Session 问题。

类似的,分布式锁也可以利用 Redis 实现,让多个服务器都从 Redis 去获取锁资源,谁先拿到锁,谁就能操作,其他人排队等待。

你:确实,那 Redis 还有其他应用场景么?

鱼皮:那可太多了,Redis 除了 5 种基本数据结构外,还提供了很多 "高级" 数据结构,专门解决特定场景的问题。

  • GEO:存地理位置坐标,实现附近的人、附近的餐厅功能
  • Bitmap:用 1 个 bit 表示一个状态,节约内存,实现用户签到、在线状态统计
  • HyperLogLog:适用于大规模统计 UV(独立访客数),虽然存在误差,但内存占用只有 12 KB
  • 布隆过滤器:快速判断数据是否存在,可用来防止无效查询打到数据库
  • Stream:消息队列功能,可以实现异步任务,比如用户下单后异步发送短信通知(但更推荐用专业的消息队列)

鱼皮:对了,还有更高级的玩法,Redis 支持编写 Lua 脚本 保证多个操作的原子性,确保要么全部成功,要么全部失败,避免数据不一致的问题。

你:这些听起来好高级,感觉学不完了......

鱼皮:想什么呢,肯定学不完啊!

Redis 是在持续更新的,去看看 官方文档,你会发现更多数据结构,比如适用于 AI 应用开发的 向量存储。不过别担心,可以等实际开发中遇到对应场景再学。

Redis 是实战型技术,光看不练等于白学,一定要多动手实践。

你:好,那我就先给 公司网站 的更多查询也加上缓存吧~

第三阶段:常见问题和解决方案

一个月后的某天,凌晨 3 点,你被电话吵醒了。

运维小哥急切地说:阿巴阿巴,网站挂了!数据库查询一直超时,你快看看!

你很是疑惑:都用 Redis 缓存了,还能超时?

你赶紧去公司看了下日志,发现原来有恶意用户在疯狂查询一个不存在的文章 ID,每次 Redis 缓存中都查不到,请求就直接打到了 MySQL 数据库上!

鱼皮这时也赶到了公司:这就是经典的 缓存穿透 问题,恶意请求故意查询不存在的数据,绕过缓存,直接攻击数据库。

你汗流浃背了:那怎么办?

鱼皮:最简单的方法是,即使数据库查询结果为空,仍将这个空结果缓存到 Redis 中,并设置一个较短的过期时间。后续相同请求会直接命中缓存的空值,避免访问数据库。

你恍然大悟,赶紧写代码补充了缓存空值的逻辑。

kotlin 复制代码
public Article getArticleById(Long id) {
   String cacheKey = "article:" + id;
   String cachedData = redisTemplate.opsForValue().get(cacheKey);
   if (cachedData != null) {
       // 如果是空值标识,直接返回 null
       if ("__NULL__".equals(cachedData)) {
           return null;
      }
       return JSON.parseObject(cachedData, Article.class);
  }  
   // 查询数据库
   Article article = articleMapper.selectById(id);
   if (article != null) {
       // 存储正常数据,10 分钟过期
       redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(article), 10, TimeUnit.MINUTES);
  } else {
       // 存储空值标识,2 分钟过期
       redisTemplate.opsForValue().set(cacheKey, "NULL", 2, TimeUnit.MINUTES);
  }  
   return article;
}

鱼皮提醒道:缓存穿透只是其中一个问题,实际项目中还会遇到 缓存击穿缓存雪崩

1)缓存击穿:热门数据突然过期,大量请求同时涌向数据库。可以用互斥锁,让请求排队一个一个来,第一个请求负责查数据库并重建缓存,其他请求等缓存重建完成后再查询。

2)缓存雪崩:大量缓存同时过期,数据库瞬间压力巨大。解决方法是给过期时间加上随机值,避免同时失效。

你感慨道:Redis 的坑还真不少!

鱼皮:这就是为什么要学习 Redis 的最佳实践,比如合理设计缓存键名、设置合适的过期时间、选择正确的数据结构等等。

鱼皮:不过这才刚开始呢,你以后还会遇到缓存和数据库的数据不一致、查询 Redis 阻塞、内存爆满等问题,都是需要学习的,以后我再给你讲吧。

你:那如果 Redis 本身也扛不住高并发怎么办?

鱼皮:那就搭建 多级缓存,比如服务器本地内存缓存 => Redis 分布式缓存 => 数据库,层层过滤。用户请求先查本地缓存,没有再查 Redis,还没有才查数据库。每一层都能拦截一部分请求,大大减少 Redis 的压力。

还有 缓存预热,比如大促活动前,提前把热门商品数据加载到 Redis 中,避免活动开始时大量请求同时打到数据库。

第四阶段:高级特性和生产部署

用了一段时间 Redis 后,你开始有点飘了。

你对着新来的实习生阿坤炫耀:Redis 我闭着眼睛都能写!什么缓存、分布式锁、多级缓存,我都搞定过!

结果第二天,老板黑着脸找到你:昨晚机房断电重启后,很多用户反馈自己的学习进度丢失了,怎么回事?!

你这才意识到问题的严重性:Redis 数据存储在内存,断电不就没了吗?

鱼皮及时出现:看来你对 Redis 的理解还停留在玩具阶段啊,生产环境的 Redis 可不是这么简单!我来问你几个问题。

第一问:怎么防止数据丢失?

你支支吾吾:做...... 做备份?

没想到,旁边的阿坤突然鸡叫起来:可以利用 Redis 提供的 2 种持久化方案!

1)RDB 快照:定期把内存数据完整保存到硬盘,像拍照一样,恢复快但可能丢失最新数据。

2)AOF 日志:把每个写操作都记录下来,像记日记,数据更安全但恢复较慢(需要重新执行所有写操作)。

第二问:Redis 服务器挂了怎么办?

你挠头:重启?

鱼皮摇头:用户等得起吗?

阿坤又鸡叫起来:要部署 主从集群,主节点负责写数据,从节点实时同步主节点的数据。这样即使主节点挂了,从节点立刻顶上,用户毫无感知。

第三问:主节点挂了,谁来决定哪个从节点升级?

你无言以对,但是那阿坤竟从容不迫:用 哨兵机制,哨兵就像监工,可以实时监控 Redis 集群健康状态。发现主节点挂了,自动选择一个从节点升级为新主节点,实现自动故障转移。

第四问:数据量太大,单台 Redis 存不下怎么办?

你眼前一亮,终于等到自己会的问题了,抢答道:删除数据!

阿坤用看流浪狗的眼神看了你一眼,回答道:首先 Redis 自身有多种淘汰策略,会自动删除不常用的数据。

还可以搭建 分片集群 ,把数据按照某种规则分散到多台 Redis 上,每台只存一部分。Redis 使用 哈希槽 机制来分配数据,既能存储更多数据,又能承受更高并发。

鱼皮拍了拍阿坤的肩膀:小伙子年轻有为啊!

你羞愧地抬不起头:我以为自己已经掌握了 Redis,原来只是学了个皮毛......

鱼皮:这些都是 Redis 在生产环境必须考虑的问题,大厂的 Redis 集群动辄几十上百个节点,就是为了保证 高可用、高性能、高可扩展性。小阿巴,你还要好好跟阿坤学习啊。

第五阶段:深入底层原理

被鱼皮连环拷问后,你主动找到阿坤:我想深入学习 Redis,不能只停留在会用的层面,请问你是怎么学习底层原理的呀?

阿坤有些惊讶:咦?你不背八股文的么?去 面试刷题网站 刷刷题就好了呀!

你震惊了:现在的校招生,竟然恐怖如斯!

鱼皮:阿坤你别逗他了,其实我们可以带着问题学习。比如你知道 Redis 为什么这么快 吗?

你想了想:因为数据存在内存里?

鱼皮:这只是表面原因。深层次的原因有很多:

  1. 高效的数据结构:Redis 底层使用了动态字符串、跳表、压缩列表等经过优化的数据结构
  2. 单线程模型:避免了多线程的上下文切换开销
  3. IO 多路复用:一个线程就能同时处理成千上万个连接
  4. 内存管理:有完善的内存淘汰策略,比如 LRU 算法

你惊讶:单线程还能这么快?

鱼皮:对!这就是 Redis 设计的巧妙之处。从这些问题出发,去阅读相关的文章,或者直接像阿坤说的刷一刷 Redis 面试题,就能快速学会很多核心知识点。

如果想系统学习,可以看看《Redis 设计与实现》这本书,讲得很透彻;甚至可以看看 Redis 的开源代码。

要记住,学习底层原理不是为了炫技,而是为了更好地使用 Redis,遇到问题时能够快速定位和解决。

你:好的,我这就去学!

结尾

若干年后,你已经成为了公司的 Redis 专家。

不仅能熟练使用 Redis 解决各种业务问题,搭个 Redis 集群架构也是手拿把掐的。

你也像鱼皮当时一样,耐心地给新人分享学习 Redis 的经验,让他们谨记鱼皮的教诲 "Redis 是实战型技术,一定要多动手实践"。

再次遇到鱼皮是在一条昏暗的小巷,此时的他年过 35,灰头土脸。你什么都没说,只是给他点了个赞,投了 2 个币,不打扰,是你的温柔。

更多

💻 编程学习交流:编程导航 📃 简历快速制作:老鱼简历 ✏️ 面试刷题神器:面试鸭

相关推荐
oak隔壁找我3 小时前
Druid 数据库连接池源码详细解析
java·数据库·后端
_大学牲3 小时前
FuncAvatar: 你的头像氛围感神器 🤥🤥🤥
前端·javascript·程序员
不剪发的Tony老师3 小时前
Yearning:一个免费开源的SQL审核平台
数据库·sql·mysql
小姐姐味道3 小时前
AI应用时代:多读论文勤尝试,少做讨论少分享,是活下去的关键
人工智能·程序员·开源
Gauss松鼠会3 小时前
GaussDB慢sql信息收集和执行计划查看
数据库·sql·gaussdb
FreeBuf_4 小时前
AWS服务大规模中断,基础设施故障影响全球企业
网络·数据库·aws
NineData4 小时前
NineData 数据库 DevOps 新增支持 PolarDB-X
数据库·devops·数据库管理工具·ninedata·polardb-x·sql审核·sql管理工具