前面将多线程、锁、线程安全部分内容完整了解了一遍,接下来准备点啥呢?发现最开始Redis部分的内容写的有些凌乱,想着趁着这次机会把Redis部分从头到尾梳理一遍,如果想了解或者梳理Redis知识的小伙伴,看这个系列的几篇Redis文章就够了~
第1章:Redis 简介与概览
1.1 什么是Redis?
如果把传统的关系型数据库(如MySQL)比作一个"大型仓库"——数据存放有序但存取较慢,那么Redis就是一个"超高速工作台"——所有常用工具都摆在手边,随取随用!
Redis(Remote Dictionary Server) 是一个开源的、基于内存的键值对存储系统。它不仅仅支持简单的Key-Value,还提供了丰富的数据结构,可以用作数据库、缓存和消息中间件。
1.2 Redis为什么这么快?核心优势解析
1. 基于内存操作
就像从书架上拿书(硬盘) vs 从桌面上拿便签(内存),Redis基于内存的操作让它拥有惊人的速度:
# 速度对比 硬盘读取速度: 约 100MB/s 内存读取速度: 约 10GB/s Redis读取速度: 约 100,000次/秒
2. 单线程架构
听起来反直觉?但正是Redis的"独门绝技"!想象一下:银行只有一个超级高效的柜员,但他能同时处理多个窗口的客户请求,没有内部协调的混乱。这就是Redis的单线程+IO多路复用模型:
- 避免了线程切换和锁竞争的开销
- 一个线程同时处理多个客户端请求
3. 高效的数据结构
Redis不是简单的Key-Value,而是为不同场景量身定制的数据结构服务器,就像多功能工具箱,每种数据结构都是不同的专用工具。
1.3 Redis vs 其他数据库:什么时候该用Redis?
| 场景 | 推荐使用 | 不推荐使用 |
|---|---|---|
| 热点数据缓存 | ✅ 完美适合 | ❌ |
| 会话存储(Session) | ✅ 完美适合 | ❌ |
| 排行榜/计数器 | ✅ 完美适合 | ❌ |
| 交易记录存储 | ❌ | ✅ 用MySQL |
| 复杂关联查询 | ❌ | ✅ 用MySQL |
简单判断法则:如果你的数据需要快速读写且不要求100%持久化安全,就用Redis!
第2章:Redis 安装与配置
2.1 Linux (Ubuntu) 安装
# 1. 更新包管理器 sudo apt update # 2. 安装Redis sudo apt install redis-server # 3. 启动Redis服务 sudo systemctl start redis-server # 4. 设置开机自启 sudo systemctl enable redis-server # 5. 检查状态 sudo systemctl status redis-server # 6. 测试连接 redis-cli ping # 如果返回 PONG,恭喜安装成功!
2.2 macOS 安装
# 1. 使用Homebrew安装 brew install redis # 2. 启动Redis服务(后台运行) brew services start redis # 3. 或者手动启动(前台运行,方便调试) redis-server /usr/local/etc/redis.conf # 4. 测试连接 redis-cli ping
2.3 Windows 安装
注意:官方不支持Windows,但有以下选择:
方案一:WSL2(推荐)
# 在Windows Terminal中开启WSL Ubuntu,然后按照Linux步骤安装
方案二:Microsoft维护的Windows版本
- 访问:https://github.com/microsoftarchive/redis/releases
- 下载
Redis-x64-3.2.100.msi - 双击安装,Redis会作为Windows服务运行
2.4 Docker 安装(跨平台通用)
# 1. 拉取最新Redis镜像 docker pull redis:latest # 2. 运行Redis容器 docker run -d --name my-redis -p 6379:6379 -v /path/on/host:/data redis:latest # 参数解释: # -d : 后台运行 # --name : 容器名称 # -p 6379:6379 : 端口映射(主机:容器) # -v : 数据卷挂载,持久化数据 # 3. 进入容器执行命令 docker exec -it my-redis redis-cli # 4. 或者直接在主机连接 redis-cli -h 127.0.0.1 -p 6379
2.5 核心配置文件解读
Redis的"大脑"是redis.conf文件,让我们看看关键配置:
# 找到配置文件位置 find / -name redis.conf 2>/dev/null # 通常位置:/etc/redis/redis.conf # 查看关键配置 cat /etc/redis/redis.conf | grep -v "^#" | grep -v "^$"
重要配置项详解:
# 网络相关 bind 127.0.0.1 # 只允许本地连接,远程访问改为 0.0.0.0 port 6379 # 默认端口号 protected-mode yes # 保护模式,生产环境建议yes # 持久化相关 dir /var/lib/redis # 数据存储目录 dbfilename dump.rdb # RDB文件名 # 内存管理 maxmemory 100mb # 最大内存限制 maxmemory-policy allkeys-lru # 内存满时的淘汰策略 # 安全相关 requirepass your_strong_password # 设置访问密码
第3章:Redis 核心数据结构 (上) - 基础五虎将
数据结构总览
Redis不是简单的Key-Value,而是数据结构服务器!就像一个多功能工具箱,每种数据结构都是不同的工具:
| 数据结构 | 比喻 | 典型应用 |
|---|---|---|
| String | 📝 便利贴 | 缓存、计数器 |
| Hash | 📋 表格 | 用户信息、对象存储 |
| List | 📚 书架 | 消息队列、最新列表 |
| Set | 🎯 集合 | 标签、共同好友 |
| Sorted Set | 🏆 排行榜 | 排行榜、延迟队列 |
3.1 String(字符串) - 万能选手
就像办公室的便利贴,简单直接,什么都能记!
# ========== 基础操作 ========== # 设置键值对 SET username "redis_learner" # ✅ 结果:OK # 💡 使用建议:最简单的缓存用法 # ⚠️ 雷点:如果key已存在会覆盖,慎用! # 获取值 GET username # ✅ 结果:"redis_learner" # ⚠️ 雷点:如果key不存在返回nil,注意空值处理 # 设置并获取原值(原子操作) GETSET username "new_learner" # ✅ 结果:"redis_learner" (返回旧值) # ✅ 现在username的值是:"new_learner" # ========== 数字操作 ========== # 设置数字 SET page_views 100 # 递增 INCR page_views # ✅ 结果:101 # 💡 使用建议:完美用于计数器,原子操作不怕并发 # 增加指定数值 INCRBY page_views 5 # ✅ 结果:106 # 递减 DECR page_views # ✅ 结果:105 # ========== 批量操作 ========== # 批量设置 MSET user:1000:name "Alice" user:1000:age 25 user:1000:city "Beijing" # ✅ 结果:OK # 💡 使用建议:减少网络开销,提升性能 # 批量获取 MGET user:1000:name user:1000:age user:1000:city # ✅ 结果:1) "Alice" 2) "25" 3) "Beijing" # ========== 生存时间 ========== # 设置值并指定10秒后过期 SETEX session_token 10 "abc123" # ✅ 结果:OK # 💡 使用建议:Session管理、验证码场景 # 设置key的生存时间(秒) EXPIRE username 60 # ✅ 结果:(integer) 1 (设置成功) # 查看剩余生存时间 TTL username # ✅ 结果:(integer) 57 (剩余57秒)
String使用场景:
- 🔥 缓存HTML片段、API响应
- 🔢 文章阅读量、点赞数计数器
- 🔑 Session存储、临时令牌
- ⏰ 验证码、限流器
3.2 Hash(哈希) - 对象存储器
就像Excel表格,一个key对应多个字段,完美存储对象!
# ========== 基本操作 ========== # 设置单个字段 HSET user:1000 name "Alice" # ✅ 结果:(integer) 1 (新增字段数) # 同时设置多个字段 HSET user:1000 age 25 city "Beijing" profession "Engineer" # ✅ 结果:(integer) 3 # 获取单个字段 HGET user:1000 name # ✅ 结果:"Alice" # 获取所有字段和值 HGETALL user:1000 # ✅ 结果: # 1) "name" # 2) "Alice" # 3) "age" # 4) "25" # 5) "city" # 6) "Beijing" # 7) "profession" # 8) "Engineer" # 💡 使用建议:适合存储对象,但字段不宜过多(建议<1000) # ⚠️ 雷点:字段太多会占用大量内存,考虑分拆 # ========== 批量操作 ========== # 批量获取指定字段 HMGET user:1000 name age # ✅ 结果:1) "Alice" 2) "25" # ========== 数字操作 ========== # 字段值递增 HINCRBY user:1000 age 1 # ✅ 结果:(integer) 26 # 💡 使用建议:用户年龄更新、积分变动等 # ========== 查询操作 ========== # 获取所有字段名 HKEYS user:1000 # ✅ 结果:1) "name" 2) "age" 3) "city" 4) "profession" # 获取所有字段值 HVALS user:1000 # ✅ 结果:1) "Alice" 2) "26" 3) "Beijing" 4) "Engineer" # 获取字段数量 HLEN user:1000 # ✅ 结果:(integer) 4 # 检查字段是否存在 HEXISTS user:1000 email # ✅ 结果:(integer) 0 (不存在)
Hash使用场景:
- 👥 用户信息存储
- 🛒 购物车商品信息
- 📊 对象属性缓存
3.3 List(列表) - 有序队列
就像排队的人群,可以从队头或队尾加入,保持顺序!
# ========== 从左侧操作 ========== # 从左侧插入(类似队列头部) LPUSH tasks "task1" # ✅ 结果:(integer) 1 LPUSH tasks "task2" "task3" # ✅ 结果:(integer) 3 # 💡 当前列表:["task3", "task2", "task1"] # 从左侧弹出 LPOP tasks # ✅ 结果:"task3" # 💡 剩余列表:["task2", "task1"] # ========== 从右侧操作 ========== # 从右侧插入(类似队列尾部) RPUSH tasks "task4" # ✅ 结果:(integer) 3 # 💡 当前列表:["task2", "task1", "task4"] # 从右侧弹出 RPOP tasks # ✅ 结果:"task4" # 💡 剩余列表:["task2", "task1"] # ========== 查询操作 ========== # 获取列表长度 LLEN tasks # ✅ 结果:(integer) 2 # 获取指定范围的元素 LRANGE tasks 0 -1 # 0到-1表示获取所有 # ✅ 结果:1) "task2" 2) "task1" LRANGE tasks 0 0 # 获取第一个元素 # ✅ 结果:1) "task2" # ========== 高级操作 ========== # 阻塞式弹出(等待任务,超时时间5秒) BLPOP new_tasks 5 # 💡 使用建议:消息队列场景,队列为空时等待 # ✅ 结果:如果5秒内有元素返回元素,否则返回nil # 修剪列表,只保留指定范围 LPUSH numbers 1 2 3 4 5 LTRIM numbers 0 2 # 只保留前3个元素 LRANGE numbers 0 -1 # ✅ 结果:1) "5" 2) "4" 3) "3"
List使用场景:
- 📨 消息队列(LPUSH + BRPOP)
- 🆕 最新文章列表
- 📝 操作日志记录
3.4 Set(集合) - 无序唯一
就像数学里的集合,元素无序但唯一,适合做关系运算!
# ========== 基本操作 ========== # 添加元素 SADD tags "redis" "database" "cache" # ✅ 结果:(integer) 3 # 添加重复元素(自动去重) SADD tags "redis" "new_tag" # ✅ 结果:(integer) 1 (只新增了1个) # 获取所有元素 SMEMBERS tags # ✅ 结果:1) "cache" 2) "database" 3) "redis" 4) "new_tag" # ⚠️ 雷点:元素无序!不要依赖返回顺序 # 检查元素是否存在 SISMEMBER tags "redis" # ✅ 结果:(integer) 1 (存在) # 获取集合大小 SCARD tags # ✅ 结果:(integer) 4 # 随机弹出一个元素 SPOP tags # ✅ 结果:"new_tag" (随机) # 💡 使用建议:抽奖场景 # ========== 集合运算 ========== # 创建两个集合 SADD group_A "user1" "user2" "user3" SADD group_B "user3" "user4" "user5" # 交集 - 共同好友 SINTER group_A group_B # ✅ 结果:1) "user3" # 并集 - 所有用户 SUNION group_A group_B # ✅ 结果:1) "user1" 2) "user2" 3) "user3" 4) "user4" 5) "user5" # 差集 - A有B没有 SDIFF group_A group_B # ✅ 结果:1) "user1" 2) "user2" # 将交集存储到新集合 SINTERSTORE common_users group_A group_B SMEMBERS common_users # ✅ 结果:1) "user3"
Set使用场景:
- 🏷️ 文章标签系统
- 👥 社交网络共同好友
- 🎲 随机抽奖、唯一值存储
3.5 Sorted Set(有序集合) - 带分队的集合
就像游戏排行榜,每个玩家都有分数,可以按分数排序!
# ========== 基本操作 ========== # 添加带分数的成员 ZADD leaderboard 1000 "Alice" ZADD leaderboard 850 "Bob" 1200 "Charlie" 900 "David" # ✅ 结果:(integer) 3 # 按分数升序获取(从小到大) ZRANGE leaderboard 0 -1 WITHSCORES # ✅ 结果: # 1) "Bob" 2) "850" # 3) "David" 4) "900" # 5) "Alice" 6) "1000" # 7) "Charlie" 8) "1200" # 按分数降序获取(从大到小) ZREVRANGE leaderboard 0 -1 WITHSCORES # ✅ 结果: # 1) "Charlie" 2) "1200" # 3) "Alice" 4) "1000" # 5) "David" 6) "900" # 7) "Bob" 8) "850" # ========== 分数操作 ========== # 增加成员分数 ZINCRBY leaderboard 50 "Bob" # ✅ 结果:"900" # 💡 使用建议:实时更新排行榜分数 # 获取成员分数 ZSCORE leaderboard "Alice" # ✅ 结果:"1000" # 获取成员排名(从0开始,按分数升序) ZRANK leaderboard "Alice" # ✅ 结果:(integer) 2 (第三名) # 获取成员排名(按分数降序) ZREVRANK leaderboard "Alice" # ✅ 结果:(integer) 1 (第二名) # ========== 范围查询 ========== # 按分数范围查询 ZRANGEBYSCORE leaderboard 900 1100 WITHSCORES # ✅ 结果: # 1) "David" 2) "900" # 3) "Alice" 4) "1000" # 查询分数大于1000的成员 ZRANGEBYSCORE leaderboard (1000 +inf WITHSCORES # ✅ 结果:1) "Charlie" 2) "1200" # 查询前3名 ZREVRANGE leaderboard 0 2 WITHSCORES # ✅ 结果: # 1) "Charlie" 2) "1200" # 3) "Alice" 4) "1000" # 5) "David" 6) "900" # ========== 统计操作 ========== # 统计成员数量 ZCARD leaderboard # ✅ 结果:(integer) 4 # 统计分数范围内的成员数量 ZCOUNT leaderboard 800 1000 # ✅ 结果:(integer) 3
Sorted Set使用场景:
- 🏆 游戏排行榜、热度排名
- ⏰ 延迟队列(用时间戳作为分数)
- 📈 带权重的任务调度
本章总结
Redis的"基础五虎将"各有绝活:
- String - 简单直接,万能型选手
- Hash - 对象存储,结构化专家
- List - 有序队列,消息传递高手
- Set - 无序唯一,关系运算大师
- Sorted Set - 带分排序,排行榜王者
选择数据结构的心法:
- 要存单个值? → String
- 要存对象? → Hash
- 要维护顺序列表? → List
- 要保证唯一性? → Set
- 要按分数排序? → Sorted Set
记住:选择合适的数据结构,性能提升立竿见影!
在接下来的章节中,我们将深入Redis的持久化、高可用等高级特性。准备好迎接更精彩的Redis世界吧!