Redis 学习笔记(第二期):核心数据类型与消息队列实战
本笔记为 Redis 系列第二期,基于第一期(概述、安装、配置、缓存理论)深入讲解五种核心数据类型(String、List、Set、ZSet、Hash)的完整命令、底层原理、生产案例,以及消息队列实现(List 模式、Pub/Sub 模式、Streams 简介),最后通过 Python 程序演示如何连接 Redis 并操作各种数据类型。本期额外补充了大量注释、对比表格、最佳实践和常见错误排查,适合初学者系统学习,也便于复习强化。
一、五种数据类型总览
Redis 所有的 key(键)都是字符串,而 value(值)支持以下五种基础数据类型:
| 数据类型 | 底层实现(简要) | 特点 | 适用场景 |
|---|---|---|---|
| String | SDS(简单动态字符串) | 二进制安全,可存整数、文本、序列化对象 | 缓存对象、计数器、分布式锁、验证码 |
| List | quicklist(双向链表) | 有序,可重复,左右操作高效 | 消息队列、最新消息列表、历史记录 |
| Set | intset / hashtable | 无序,唯一 | 标签系统、共同好友、抽奖去重 |
| ZSet(Sorted Set) | ziplist / skiplist + dict | 有序,唯一,按分数排序 | 排行榜、延时队列、带权重的任务 |
| Hash | ziplist / hashtable | 适合存储对象(多个字段) | 用户信息、商品详情、配置项 |
记忆技巧:Redis 的 value 可以是"单值(String)"、"列表"、"集合"、"有序集合"、"对象(Hash)"。
二、String 类型(字符串)
2.1 底层结构简图
Redis 的 String 使用 SDS(Simple Dynamic String) 实现,相比 C 字符串有以下优点:
- 预分配空间:减少内存重分配次数。
- 惰性释放:缩短后不立即回收,便于下次追加。
- 二进制安全:可存储任何数据(图片、序列化对象等)。
2.2 命令速查表
| 命令 | 示例 | 说明 |
|---|---|---|
| `SET key value EX seconds [NX | XX]` | SET name "Tom" |
GET key |
GET name |
获取键值 |
MSET key1 val1 key2 val2 ... |
MSET a 1 b 2 |
批量设置 |
MGET key1 key2 ... |
MGET a b |
批量获取 |
SETEX key seconds value |
SETEX code 60 "123456" |
设置键值并指定过期时间(秒) |
SETNX key value |
SETNX lock 1 |
键不存在时设置(分布式锁常用) |
INCR key |
INCR views |
值加 1(值须为整数) |
INCRBY key increment |
INCRBY score 10 |
值加指定整数 |
DECR key |
DECR stock |
值减 1 |
DECRBY key decrement |
DECRBY stock 2 |
值减指定整数 |
APPEND key value |
APPEND msg " world" |
追加内容 |
STRLEN key |
STRLEN name |
获取字符串长度(字节数) |
GETSET key new_value |
GETSET counter 0 |
设置新值并返回旧值 |
DEL key |
DEL name |
删除键(通用命令) |
EXISTS key |
EXISTS name |
判断键是否存在 |
EXPIRE key seconds |
EXPIRE name 30 |
设置过期时间 |
TTL key |
TTL name |
查看剩余生存时间(-1 永不过期,-2 不存在) |
2.3 命令详细演示
bash
# 连接 Redis(无密码)
redis-cli
基础设置与获取
bash
# SET / GET
127.0.0.1:6379> SET name "zhangsan"
OK
127.0.0.1:6379> GET name
"zhangsan"
# 判断类型
127.0.0.1:6379> TYPE name
string
# 带过期时间(EX 秒,PX 毫秒)
127.0.0.1:6379> SET token "abc123" EX 10
OK
127.0.0.1:6379> TTL token
(integer) 7
# 仅在 key 不存在时设置(SETNX)
127.0.0.1:6379> SETNX lock 1
(integer) 1 # 成功,key 不存在
127.0.0.1:6379> SETNX lock 2
(integer) 0 # 失败,key 已存在
127.0.0.1:6379> GET lock
"1"
# 仅在 key 存在时设置(SET ... XX)
127.0.0.1:6379> SET lock 3 XX
OK
127.0.0.1:6379> GET lock
"3"
批量操作(减少 RTT)
bash
127.0.0.1:6379> MSET key1 value1 key2 value2
OK
127.0.0.1:6379> MGET key1 key2
1) "value1"
2) "value2"
数字计数器(原子操作)
bash
127.0.0.1:6379> SET views 100
OK
127.0.0.1:6379> INCR views
(integer) 101
127.0.0.1:6379> INCRBY views 50
(integer) 151
127.0.0.1:6379> DECR views
(integer) 150
127.0.0.1:6379> DECRBY views 20
(integer) 130
# 对不存在的 key 做 INCR,会自动初始化为 0 再加
127.0.0.1:6379> INCR new_counter
(integer) 1
# 对非数字值使用 INCR 会报错
127.0.0.1:6379> SET name "abc"
OK
127.0.0.1:6379> INCR name
(error) ERR value is not an integer or out of range
其他实用命令
bash
# 追加
127.0.0.1:6379> SET msg "Hello"
OK
127.0.0.1:6379> APPEND msg " Redis"
(integer) 11 # 返回追加后的字节长度
127.0.0.1:6379> GET msg
"Hello Redis"
# 获取长度(字节数,中文字符按编码)
127.0.0.1:6379> STRLEN msg
(integer) 11
# GETSET:设置新值并返回旧值
127.0.0.1:6379> GETSET counter 0
"130" # 返回旧值 130,新值变为 0
127.0.0.1:6379> GET counter
"0"
2.4 生产案例
案例1:文章阅读量计数器
bash
# 每次访问文章 ID=1001,增加阅读量
INCR article:1001:views
# 获取阅读量
GET article:1001:views
注意 :计数器应定期持久化,或配合 EXPIRE 设置统计周期。
案例2:分布式锁(简化版)
bash
# 加锁:key 不存在时设置,过期 30 秒
SET lock:order:1001 "client_id" NX EX 30
# 解锁:需原子性判断并删除(实际用 Lua 脚本)
# 此处仅演示加锁部分
案例3:短信验证码存储
bash
# 生成6位验证码,5分钟有效
SETEX sms:13800138000 300 "123456"
2.5 注意事项与最佳实践
- 大 key 问题:String 类型建议不超过 10KB。过大的值(如存储序列化的大对象)会降低性能。
- 避免
KEYS \*:生产环境禁止使用,会阻塞 Redis。用SCAN代替。 - 整数范围:INCR/DECR 支持 64 位有符号整数。
- 内存优化 :对于大量短字符串,可考虑开启
hash-max-ziplist-entries等压缩配置(但 String 本身无压缩)。
三、List 类型(列表)
3.1 底层结构
Redis 3.2 之后使用 quicklist(双向链表 + ziplist 分段),兼顾内存效率和插入删除性能。特点:
- 有序,可重复。
- 左右两端操作(LPUSH/RPUSH、LPOP/RPOP)时间复杂度 O(1)。
- 索引操作(LINDEX)时间复杂度 O(N),慎用。
3.2 命令速查表
| 命令 | 示例 | 说明 |
|---|---|---|
LPUSH key value [value ...] |
LPUSH queue a b c |
从左边(头部)插入一个或多个元素 |
RPUSH key value [value ...] |
RPUSH queue x y |
从右边(尾部)插入 |
LPOP key |
LPOP queue |
移除并返回左边第一个元素 |
RPOP key |
RPOP queue |
移除并返回右边第一个元素 |
LRANGE key start stop |
LRANGE queue 0 -1 |
获取指定范围元素(支持负索引) |
LLEN key |
LLEN queue |
获取列表长度 |
LINDEX key index |
LINDEX queue 1 |
获取指定索引的元素 |
LSET key index value |
LSET queue 1 new |
修改指定索引的元素 |
LTRIM key start stop |
LTRIM queue 0 100 |
修剪列表,只保留指定区间 |
BLPOP key [key ...] timeout |
BLPOP queue 10 |
阻塞式左弹出(超时秒数) |
BRPOP key [key ...] timeout |
BRPOP queue 10 |
阻塞式右弹出 |
RPOPLPUSH source dest |
RPOPLPUSH list1 list2 |
右弹出一个元素并左压入另一个列表 |
DEL key |
DEL queue |
删除整个列表 |
3.3 命令详细演示
bash
# 从左边插入
127.0.0.1:6379> LPUSH list1 tom
(integer) 1
127.0.0.1:6379> LPUSH list1 jack
(integer) 2
127.0.0.1:6379> LRANGE list1 0 -1
1) "jack" # 后插入的在左边(头部)
2) "tom"
# 从右边插入
127.0.0.1:6379> RPUSH list2 a b c
(integer) 3
127.0.0.1:6379> LRANGE list2 0 -1
1) "a"
2) "b"
3) "c"
# 同时从左边插入多个
127.0.0.1:6379> LPUSH list3 x y z
(integer) 3
127.0.0.1:6379> LRANGE list3 0 -1
1) "z" # 注意顺序:z y x,因为依次插入
2) "y"
3) "x"
# 弹出
127.0.0.1:6379> LPOP list3
"z"
127.0.0.1:6379> RPOP list3
"x"
127.0.0.1:6379> LRANGE list3 0 -1
1) "y"
# 获取长度
127.0.0.1:6379> LLEN list2
(integer) 3
# 按索引操作
127.0.0.1:6379> LINDEX list2 1
"b"
127.0.0.1:6379> LSET list2 1 new_b
OK
127.0.0.1:6379> LRANGE list2 0 -1
1) "a"
2) "new_b"
3) "c"
# 修剪(只保留索引 1 到 2)
127.0.0.1:6379> LTRIM list2 1 2
OK
127.0.0.1:6379> LRANGE list2 0 -1
1) "new_b"
2) "c"
阻塞式弹出(经典消息队列)
打开两个终端:
终端 A(消费者,阻塞等待):
bash
127.0.0.1:6379> BRPOP task_queue 5 # 超时 5 秒
# 此时没有数据,阻塞等待
终端 B(生产者,推送消息):
bash
127.0.0.1:6379> LPUSH task_queue "job1"
(integer) 1
终端 A 立即输出:
text
1) "task_queue"
2) "job1"
(2.34s)
说明 :
BRPOP返回的第一个元素是列表名,第二个是值。
3.4 生产案例
案例1:简单消息队列(生产者-消费者)
bash
# 生产者(多个进程)
LPUSH order_queue "order:1001"
LPUSH order_queue "order:1002"
# 消费者(多个进程,阻塞等待)
BRPOP order_queue 0 # 0 表示无限阻塞
特点:一条消息只能被一个消费者获取,适合任务分发。
案例2:最新消息列表(如评论、动态)
bash
# 用户发表一条动态,ID 为 5001
LPUSH user:1001:posts "5001"
# 只保留最近 50 条
LTRIM user:1001:posts 0 49
# 分页查询
LRANGE user:1001:posts 0 9 # 第一页(最新10条)
3.5 注意事项
- 列表索引操作(
LINDEX、LSET)对于长列表效率较低,应尽量避免在循环中频繁使用。 - 阻塞弹出(
BLPOP/BRPOP)的超时设为 0 表示无限等待,注意客户端超时机制。 - 列表长度超过
list-max-ziplist-size配置时会从 ziplist 转为 quicklist 节点,但命令行为不变。
四、Set 类型(集合)
4.1 底层结构
Set 满足元素唯一性。当所有元素都是整数且数量小于 set-max-intset-entries(默认 512)时,使用 intset (紧凑数组);否则使用 hashtable。
4.2 命令速查表
| 命令 | 示例 | 说明 |
|---|---|---|
SADD key member [member ...] |
SADD fruits apple banana |
添加一个或多个成员 |
SMEMBERS key |
SMEMBERS fruits |
获取所有成员(慎用于大集合) |
SREM key member [member ...] |
SREM fruits apple |
删除成员 |
SISMEMBER key member |
SISMEMBER fruits apple |
判断成员是否存在 |
SCARD key |
SCARD fruits |
获取成员数量 |
SINTER key1 [key2 ...] |
SINTER set1 set2 |
交集 |
SUNION key1 [key2 ...] |
SUNION set1 set2 |
并集 |
SDIFF key1 [key2 ...] |
SDIFF set1 set2 |
差集(属于 key1 不属于 key2) |
SPOP key [count] |
SPOP fruits 2 |
随机弹出 count 个成员 |
SRANDMEMBER key [count] |
SRANDMEMBER fruits 3 |
随机返回成员(不删除) |
SMOVE source dest member |
SMOVE set1 set2 apple |
移动成员到另一集合 |
4.3 命令详细演示
bash
# 添加
127.0.0.1:6379> SADD set1 v1 v2 v3
(integer) 3
127.0.0.1:6379> SADD set2 v2 v3 v4
(integer) 3
# 查看所有成员(顺序不固定)
127.0.0.1:6379> SMEMBERS set1
1) "v2"
2) "v3"
3) "v1"
# 判断存在
127.0.0.1:6379> SISMEMBER set1 v1
(integer) 1
127.0.0.1:6379> SISMEMBER set1 v5
(integer) 0
# 删除
127.0.0.1:6379> SREM set1 v1
(integer) 1
127.0.0.1:6379> SMEMBERS set1
1) "v2"
2) "v3"
# 集合运算
127.0.0.1:6379> SINTER set1 set2
1) "v2"
2) "v3"
127.0.0.1:6379> SUNION set1 set2
1) "v2"
2) "v3"
3) "v4"
127.0.0.1:6379> SDIFF set1 set2 # 只属于 set1 的
(empty list or set) # 因为 v2,v3 都在 set2 中
127.0.0.1:6379> SDIFF set2 set1 # 只属于 set2 的
1) "v4"
# 随机弹出(抽奖)
127.0.0.1:6379> SADD lottery 101 102 103 104 105
(integer) 5
127.0.0.1:6379> SPOP lottery 2
1) "103"
2) "105"
127.0.0.1:6379> SMEMBERS lottery
1) "101"
2) "102"
3) "104"
4.4 生产案例
案例1:共同好友(社交系统)
bash
# 用户 A 的好友集合
SADD user:1001:friends 2001 2002 2003
# 用户 B 的好友集合
SADD user:1002:friends 2002 2003 2004
# 共同好友
SINTER user:1001:friends user:1002:friends
# 推荐好友(用户 A 的好友集合中减去 B 的好友)
SDIFF user:1001:friends user:1002:friends
案例2:抽奖去重(唯一参与用户)
bash
# 活动期间,用户每次点击参与抽奖,添加用户ID
SADD lottery:20250101 1001
SADD lottery:20250101 1002
# 抽随机获奖者
SRANDMEMBER lottery:20250101 1
# 或者弹出获奖者(消耗)
SPOP lottery:20250101 3
4.5 注意事项
SMEMBERS命令在大集合(百万级)时会阻塞 Redis,应改用SSCAN迭代。- 集合运算(SINTER、SUNION、SDIFF)复杂度较高,可能阻塞,建议在从库执行或使用
SINTERSTORE等命令存储结果。
五、ZSet 类型(有序集合)
5.1 底层结构
ZSet 内部使用 skiplist(跳表)+ dict(哈希表) ,达到 O(log N) 的查询和插入效率。每个元素关联一个 double 类型的分数(score),按分数从小到大排序,分数相同则按字典序排列。
5.2 命令速查表
| 命令 | 示例 | 说明 |
|---|---|---|
ZADD key score member [score member ...] |
ZADD rank 100 "Alice" |
添加元素,可同时指定多个 |
ZRANGE key start stop [WITHSCORES] |
ZRANGE rank 0 -1 WITHSCORES |
按分数升序返回索引范围内的成员 |
ZREVRANGE key start stop [WITHSCORES] |
ZREVRANGE rank 0 -1 WITHSCORES |
按分数降序返回 |
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] |
ZRANGEBYSCORE rank 80 100 |
按分数范围返回(包含边界) |
ZREM key member [member ...] |
ZREM rank Alice |
删除一个或多个成员 |
ZCARD key |
ZCARD rank |
获取成员数量 |
ZCOUNT key min max |
ZCOUNT rank 80 100 |
统计分数范围内的成员数 |
ZRANK key member |
ZRANK rank Alice |
获取成员排名(升序,从 0 开始) |
ZREVRANK key member |
ZREVRANK rank Alice |
获取成员排名(降序) |
ZSCORE key member |
ZSCORE rank Alice |
获取成员的分数 |
ZINCRBY key increment member |
ZINCRBY rank 5 Alice |
增加成员的分数 |
ZREMRANGEBYRANK key start stop |
ZREMRANGEBYRANK rank 0 9 |
删除按排名范围(前10名删除) |
ZREMRANGEBYSCORE key min max |
ZREMRANGEBYSCORE rank 0 60 |
删除按分数范围 |
5.3 命令详细演示
bash
# 添加课程分数
127.0.0.1:6379> ZADD course 90 linux 99 go 60 python 50 cloud
(integer) 4
# 升序查看所有(不带分数)
127.0.0.1:6379> ZRANGE course 0 -1
1) "cloud"
2) "python"
3) "linux"
4) "go"
# 带分数
127.0.0.1:6379> ZRANGE course 0 -1 WITHSCORES
1) "cloud"
2) "50"
3) "python"
4) "60"
5) "linux"
6) "90"
7) "go"
8) "99"
# 降序(从高到低)
127.0.0.1:6379> ZREVRANGE course 0 -1 WITHSCORES
1) "go"
2) "99"
3) "linux"
4) "90"
5) "python"
6) "60"
7) "cloud"
8) "50"
# 按分数范围查询(60 到 100)
127.0.0.1:6379> ZRANGEBYSCORE course 60 100 WITHSCORES
1) "python"
2) "60"
3) "linux"
4) "90"
5) "go"
6) "99"
# 统计分数≥80的人数
127.0.0.1:6379> ZCOUNT course 80 100
(integer) 2
# 查看排名(升序,0 是第一名)
127.0.0.1:6379> ZRANK course go
(integer) 3
127.0.0.1:6379> ZREVRANK course go # 降序排名(第一名是 0)
(integer) 0
# 增加某成员分数
127.0.0.1:6379> ZINCRBY course 5 python
"65"
127.0.0.1:6379> ZSCORE course python
"65"
# 删除成员
127.0.0.1:6379> ZREM course cloud
(integer) 1
5.4 生产案例
案例1:游戏排行榜(实时)
bash
# 用户 1001 获得 500 分
ZADD leaderboard 500 1001
# 用户 1002 获得 600 分
ZADD leaderboard 600 1002
# 用户 1001 再得 100 分(增加)
ZINCRBY leaderboard 100 1001
# 获取前三名(降序)
ZREVRANGE leaderboard 0 2 WITHSCORES
# 查看用户 1001 的排名(降序排名,0 开始)
ZREVRANK leaderboard 1001
案例2:延时队列(任务调度)
bash
# 将任务添加到延时队列(以执行时间戳作为分数)
ZADD delay_queue 1734220800 "task:send_email:1001"
ZADD delay_queue 1734220900 "task:gen_report:2002"
# 消费者定期取到期任务(当前时间戳 now=1734220850)
ZRANGEBYSCORE delay_queue 0 1734220850 WITHSCORES
# 取出后删除
ZREMRANGEBYSCORE delay_queue 0 1734220850
5.5 注意事项
- ZSet 的元素(member)不能重复,但分数(score)可以重复。
- 避免在 ZSet 中存储过大的 member(如长文本),会影响性能。
- 大量使用
ZRANGEBYSCORE且不加 LIMIT 可能导致网络阻塞,建议分页。
六、Hash 类型(哈希)
6.1 底层结构
Hash 用于存储对象(多个字段-值对)。当字段数少且值较短时使用 ziplist ,超过阈值(hash-max-ziplist-entries 默认 512,hash-max-ziplist-value 默认 64 字节)转为 hashtable。
6.2 命令速查表
| 命令 | 示例 | 说明 |
|---|---|---|
HSET key field value [field value ...] |
HSET user:1001 name "Tom" age 25 |
设置一个或多个字段 |
HGET key field |
HGET user:1001 name |
获取指定字段的值 |
HMSET key field value [field value ...] |
HMSET user:1002 name "Jerry" age 30 |
批量设置(Redis 4.0+ 可用 HSET 替代) |
HMGET key field [field ...] |
HMGET user:1001 name age |
批量获取 |
HGETALL key |
HGETALL user:1001 |
获取所有字段和值(慎用) |
HKEYS key |
HKEYS user:1001 |
获取所有字段名 |
HVALS key |
HVALS user:1001 |
获取所有值 |
HDEL key field [field ...] |
HDEL user:1001 age |
删除字段 |
HEXISTS key field |
HEXISTS user:1001 name |
判断字段是否存在 |
HINCRBY key field increment |
HINCRBY user:1001 score 10 |
对整数字段自增 |
HLEN key |
HLEN user:1001 |
获取字段数量 |
6.3 命令详细演示
bash
# 设置用户信息
127.0.0.1:6379> HSET user:9527 name "zhouxingxing" age 20
(integer) 2
127.0.0.1:6379> TYPE user:9527
hash
# 获取单个字段
127.0.0.1:6379> HGET user:9527 name
"zhouxingxing"
# 批量设置
127.0.0.1:6379> HMSET user:9527 city "hongkong" gender male
OK
# 获取多个字段
127.0.0.1:6379> HMGET user:9527 name age
1) "zhouxingxing"
2) "20"
# 获取所有字段和值
127.0.0.1:6379> HGETALL user:9527
1) "name"
2) "zhouxingxing"
3) "age"
4) "20"
5) "city"
6) "hongkong"
7) "gender"
8) "male"
# 获取所有字段名
127.0.0.1:6379> HKEYS user:9527
1) "name"
2) "age"
3) "city"
4) "gender"
# 获取所有值
127.0.0.1:6379> HVALS user:9527
1) "zhouxingxing"
2) "20"
3) "hongkong"
4) "male"
# 自增操作(分数)
127.0.0.1:6379> HINCRBY user:9527 score 5
(integer) 5
127.0.0.1:6379> HGET user:9527 score
"5"
# 判断字段存在
127.0.0.1:6379> HEXISTS user:9527 age
(integer) 1
# 删除字段
127.0.0.1:6379> HDEL user:9527 age
(integer) 1
127.0.0.1:6379> HGETALL user:9527
1) "name"
2) "zhouxingxing"
3) "city"
4) "hongkong"
5) "gender"
6) "male"
7) "score"
8) "5"
6.4 生产案例
案例1:用户信息存储
bash
HSET user:1001 name "张三" age 25 email "zhang@example.com"
# 获取用户年龄
HGET user:1001 age
# 更新用户邮箱
HSET user:1001 email "new@example.com"
案例2:购物车(简单版)
bash
# 用户 1001 添加商品 2001 数量 2
HSET cart:1001 2001 2
# 增加商品 2001 数量 1
HINCRBY cart:1001 2001 1
# 查看购物车
HGETALL cart:1001
# 删除商品
HDEL cart:1001 2001
6.5 注意事项
HGETALL在 Hash 很大时会阻塞,应使用HSCAN迭代。- Hash 适合存储对象,每个字段独立操作,比用 String 序列化整个对象更灵活。
- 合理设置
hash-max-ziplist-entries和hash-max-ziplist-value以平衡内存和性能。
七、消息队列深度对比
教材中介绍了 List 实现的简单消息队列和 Pub/Sub 模式,这里补充两者的对比以及 Streams(Redis 5.0+)简介。
7.1 List 模式(生产者/消费者)
特点:
- 一个消息只能被一个消费者消费(竞态)。
- 支持阻塞弹出(BRPOP),避免轮询。
- 消息不持久化(若消费者未消费而 Redis 宕机,消息可能丢失)。
适用场景:简单的异步任务队列,对消息可靠性要求不高。
7.2 Pub/Sub 模式(发布/订阅)
特点:
- 一个消息被所有订阅者接收(广播)。
- 消息实时,但不持久化(订阅者离线期间的消息会丢失)。
- 不支持消息确认和重试。
适用场景:实时通知、聊天室、广播。
7.3 Streams(Redis 5.0+)
特点:
- 持久化消息,支持消费者组、消息确认、消息重放。
- 功能类似 Kafka,但更轻量。
教材未详细讲解,此处仅作补充知识(不展开命令)。如需可靠消息队列,可学习 Streams。
7.4 对比表格
| 特性 | List (BRPOP) | Pub/Sub | Streams |
|---|---|---|---|
| 消息持久化 | ❌ | ❌ | ✅ (AOF/RDB) |
| 消息确认机制 | ❌ | ❌ | ✅ (XACK) |
| 消费关系 | 一条消息一个消费者 | 一条消息多个消费者 | 消费者组(可分组) |
| 历史消息 | ❌ (消费后删除) | ❌ | ✅ |
| 复杂度 | 低 | 低 | 中高 |
八、Python 连接 Redis 实战
8.1 安装 redis-py
bash
pip install redis
8.2 连接池配置与基本操作
python
import redis
# 使用连接池(推荐)
pool = redis.ConnectionPool(
host='192.168.108.172',
port=6379,
password='123456',
db=0,
max_connections=10
)
r = redis.Redis(connection_pool=pool)
# 测试连接
print(r.ping()) # True 表示成功
8.3 完整脚本:演示五种数据类型
python
import redis
# 连接 Redis(无密码示例)
r = redis.Redis(host='127.0.0.1', port=6379, db=0)
# ========== String ==========
print("=== String ===")
r.set('name', 'Alice')
print(r.get('name')) # b'Alice' (bytes)
print(r.incr('counter')) # 1
print(r.incrby('counter', 5)) # 6
# ========== List ==========
print("\n=== List ===")
r.lpush('tasks', 'task1', 'task2')
r.rpush('tasks', 'task3')
print(r.lrange('tasks', 0, -1)) # [b'task2', b'task1', b'task3']
print(r.brpop('tasks', timeout=1)) # (b'tasks', b'task3')
# ========== Set ==========
print("\n=== Set ===")
r.sadd('tags', 'python', 'redis', 'database')
r.sadd('tags', 'redis') # 重复添加无效
print(r.smembers('tags')) # {b'database', b'redis', b'python'}
print(r.sismember('tags', 'python')) # True
# ========== ZSet ==========
print("\n=== ZSet ===")
r.zadd('rank', {'Alice': 100, 'Bob': 90, 'Charlie': 95})
print(r.zrange('rank', 0, -1, withscores=True))
# [(b'Bob', 90.0), (b'Charlie', 95.0), (b'Alice', 100.0)]
print(r.zrevrank('rank', 'Alice')) # 0 (第一名)
# ========== Hash ==========
print("\n=== Hash ===")
r.hset('user:1', mapping={'name': 'Tom', 'age': 25})
r.hset('user:1', 'city', 'Beijing')
print(r.hgetall('user:1'))
# {b'name': b'Tom', b'age': b'25', b'city': b'Beijing'}
print(r.hincrby('user:1', 'age', 1)) # 26
运行输出示例:
text
=== String ===
b'Alice'
1
6
=== List ===
[b'task2', b'task1', b'task3']
(b'tasks', b'task3')
=== Set ===
{b'database', b'redis', b'python'}
True
=== ZSet ===
[(b'Bob', 90.0), (b'Charlie', 95.0), (b'Alice', 100.0)]
0
=== Hash ===
{b'name': b'Tom', b'age': b'25', b'city': b'Beijing'}
26
8.4 注意事项(Python)
redis-py默认返回 bytes 类型,如需字符串可设置decode_responses=True。- 密码参数建议从环境变量读取,不要硬编码。
- 使用连接池提高并发性能。
九、速查表与常见错误汇总
9.1 五种数据类型命令速查表(一页精华)
| 类型 | 增/改 | 查 | 删 | 其他常用 |
|---|---|---|---|---|
| String | SET, MSET, SETEX, SETNX |
GET, MGET, STRLEN |
DEL |
INCR, DECR, APPEND |
| List | LPUSH, RPUSH, LSET |
LRANGE, LINDEX, LLEN |
LPOP, RPOP, LTRIM |
BLPOP, BRPOP |
| Set | SADD |
SMEMBERS, SISMEMBER, SCARD |
SREM, SPOP |
SINTER, SUNION, SDIFF |
| ZSet | ZADD, ZINCRBY |
ZRANGE, ZREVRANGE, ZSCORE, ZRANK |
ZREM, ZREMRANGEBYRANK |
ZRANGEBYSCORE |
| Hash | HSET, HMSET, HINCRBY |
HGET, HMGET, HGETALL, HKEYS, HVALS |
HDEL |
HEXISTS, HLEN |
9.2 常见错误与解决方案
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
WRONGTYPE Operation against a key holding the wrong kind of value |
对 key 使用了错误类型的命令(如对 String 执行 LPUSH) | 先 TYPE key 查看类型,再用正确命令 |
ERR value is not an integer or out of range |
对非数字值执行 INCR / DECR |
确保值可转为整数,或先 SET 数字 |
(error) CLUSTERDOWN The cluster is down |
集群模式下槽位未全覆盖或部分节点故障 | 检查集群状态 CLUSTER INFO |
MISCONF Redis is configured to save RDB snapshots |
磁盘空间不足或权限问题 | 检查 dir 目录权限和磁盘空间 |
ERR unknown command 'FLUSHALL' |
生产环境禁用了危险命令 | 检查配置文件 rename-command 设置 |
Python 返回 b'value' |
decode_responses 未开启 |
创建 Redis 对象时加 decode_responses=True |
| 阻塞操作超时 | BRPOP 或 BLPOP 超时设置太小 |
调大超时值或设为 0(无限阻塞) |
十、本期知识点一览表
| 模块 | 核心内容 | 关键命令/案例 |
|---|---|---|
| String | 底层 SDS,原子计数器,分布式锁基础 | SET NX EX, INCR, MSET, GETSET |
| List | quicklist,左右端操作,阻塞队列 | LPUSH + BRPOP, LTRIM 保持长度 |
| Set | intset/hashtable,集合运算 | SADD, SINTER 共同好友,SPOP 抽奖 |
| ZSet | skiplist + dict,排行榜,延时队列 | ZADD, ZREVRANGE, ZINCRBY, ZRANGEBYSCORE |
| Hash | ziplist/hashtable,对象存储 | HSET, HGETALL, HINCRBY |
| 消息队列 | List 模式 vs Pub/Sub vs Streams | 生产者-消费者,发布-订阅 |
| Python 连接 | redis-py 库 | 连接池,各类型操作示例 |
| 速查与排错 | 命令速查表,常见错误 | 类型不匹配、数字转换、集群状态 |
下一期预告:Redis 持久化(RDB、AOF)与主从复制(一主两从搭建、复制原理)。将深入讲解数据安全与高可用的基础。