01 Redis简介
一、Redis 的背景
• 诞生时间:2009 年,由意大利开发者 Salvatore Sanfilippo 为解决网站高并发问题而设计。
• 名字含义:Remote Dictionary Server(远程字典服务器),强调其“键值对”存储结构。
• 定位:填补传统关系型数据库(如 MySQL)在高并发、实时数据处理上的不足,专注高性能和灵活性。
二、Redis 是什么?
• 核心特点:
• 内存存储:数据主要存储在内存中,读写速度极快(微秒级响应)。
• 单线程架构:通过单线程处理请求,避免多线程竞争,保证原子性。
• 持久化支持:可将内存数据保存到磁盘(RDB快照/AOF日志)。
• 分布式扩展:支持主从复制、哨兵(Sentinel)、集群(Cluster)等高可用方案。
三、Redis 的典型应用场景
-
缓存加速
• 用途:缓存热点数据(如用户信息、商品详情),减轻数据库压力。
• 优势:降低延迟,提升系统吞吐量。
• 案例:社交媒体平台缓存用户动态、热门帖子。 -
实时排行榜
• 实现:使用Sorted Set
结构,根据分数自动排序。
• 案例:游戏积分榜、电商销量排行、热搜榜单。 -
分布式锁
• 实现:通过SET key value NX EX
实现互斥锁。
• 场景:秒杀活动、资源争抢、分布式任务调度。 -
消息队列
• 实现:使用List
结构的LPUSH
/BRPOP
命令实现简单队列。
• 扩展:结合Stream
类型支持多消费者、消息回溯(Redis 5.0+)。 -
会话管理(Session Storage)
• 用途:存储用户登录状态,支持分布式系统共享会话。
• 场景:微服务架构中跨服务的用户身份验证。 -
实时数据分析
• 实现:通过HyperLogLog
统计独立访客,Bitmap
记录用户行为。
• 案例:统计日活用户(UV)、实时在线人数。
四、Redis 的核心数据结构
数据结构 | 特点与操作示例 |
---|---|
String | 存储文本、数字或二进制数据。SET count 100 ,INCR count → 原子计数器 |
List | 双向链表,支持头部/尾部操作。LPUSH tasks "work" ,RPOP tasks → 队列实现 |
Hash | 存储对象字段。HSET user:1 name "Alice" ,HGETALL user:1 → 用户属性管理 |
Set | 无序唯一集合。SADD tags "redis" ,SINTER tags1 tags2 → 共同关注计算 |
Sorted Set | 带权重的有序集合。ZADD rank 100 "A" ,ZREVRANGE rank 0 9 → 排行榜 |
Stream | 消息流(Redis 5.0+)。XADD orders * product "Book" → 订单日志 |
Geospatial | 地理位置查询。GEOADD cities 116.40 39.90 "Beijing" → 附近地点搜索 |
五、Redis 的高可用与扩展
-
主从复制(Replication)
• 用途:数据备份与读写分离。
• 配置:主节点写,从节点读,支持级联复制。 -
哨兵模式(Sentinel)
• 功能:监控主节点,自动故障转移(Failover)。
• 场景:保证高可用性(HA),避免单点故障。 -
集群模式(Cluster)
• 原理:数据分片(16384个哈希槽),支持横向扩展。
• 优势:突破单机内存限制,支持多节点并发读写。
六、学习资源推荐
02 Redis入门
一、Redis 基础操作
1️⃣ 连接 Redis
# 启动命令行客户端
redis-cli# 测试连接是否正常
PING # 返回 "PONG" 表示成功
2️⃣ 基本命令
# 设置键值对
SET user:1:name "Alice"
SET user:1:age 30# 获取值
GET user:1:name # 返回 "Alice"# 删除键
DEL user:1:age# 检查键是否存在
EXISTS user:1:name # 返回 1(存在)# 设置过期时间(10秒后自动删除)
SETEX temp_key 10 "expiring data"
二、Redis 核心数据结构
1️⃣ 字符串(String)
• 存储文本、数字或二进制数据。
SET counter 100
INCR counter # 自增1 → 101
GET counter # "101"
2️⃣ 列表(List)
• 有序元素集合,支持头部/尾部操作。
LPUSH tasks "task1" # 左侧插入
RPUSH tasks "task3" # 右侧插入
LRANGE tasks 0 -1 # 获取所有元素 → ["task1", "task3"]
3️⃣ 哈希(Hash)
• 存储对象属性(类似 JSON)。
HSET user:1 name "Alice" age 30
HGET user:1 name # "Alice"
HGETALL user:1 # 获取所有字段和值
4️⃣ 集合(Set)
• 无序唯一元素集合。
SADD tags "redis" "database"
SMEMBERS tags # 获取所有元素 → ["redis", "database"]
SISMEMBER tags "redis" # 检查是否存在 → 1(存在)
5️⃣ 有序集合(Sorted Set)
• 带权重的元素集合,自动排序。
ZADD leaderboard 100 "Alice" 200 "Bob"
ZRANGE leaderboard 0 -1 WITHSCORES # 按分数升序返回
三、持久化配置
Redis 提供两种数据持久化方式:
1️⃣ RDB(快照)
• 定期生成数据快照(默认启用)。
• 配置文件 redis.conf
中设置:
save 900 1 # 900秒内至少1次修改触发保存
save 300 10 # 300秒内至少10次修改触发保存
2️⃣ AOF(追加日志)
• 记录所有写操作命令(更安全但性能略低)。
• 配置文件 redis.conf
中启用:
appendonly yes
appendfsync everysec # 每秒同步一次日志
redisbenchmark_189">03 redis-benchmark
redisbenchmark__192">一、redis-benchmark 是什么?
• 定位:Redis 官方内置的性能测试工具,无需额外安装。
• 作用:模拟客户端并发请求,测试 Redis 服务器的吞吐量、延迟等关键指标。
• 适用场景:
• 评估 Redis 服务器的最大性能(如 QPS)。
• 对比不同硬件或配置的性能差异。
• 验证网络带宽或延迟对 Redis 的影响。
二、基本使用命令
1️⃣ 默认测试
redis-benchmark
• 默认行为:使用 50 个并发连接,发送 10 万次请求,测试 SET
和 GET
命令的性能。
• 输出示例:
=== SET ===100000 requests completed in 1.76 seconds50 parallel clients3 bytes payloadkeep alive: 1Latency (毫秒):0.081 (最小值) / 1.234 (最大值) / 0.512 (平均值)Requests per second: 56818.07
2️⃣ 核心参数说明
参数 | 说明 | 示例 |
---|---|---|
-h | Redis 服务器地址 | -h 192.168.1.100 |
-p | Redis 端口 | -p 6379 |
-a | 密码认证 | -a yourpassword |
-c | 并发连接数 | -c 100 |
-n | 总请求数 | -n 1000000 |
-t | 测试指定命令 | -t get,set |
-d | 数据大小(字节) | -d 1024 (测试 1KB 数据) |
-P | 管道(Pipeline)请求数 | -P 10 (批量发送 10 个命令) |
-k | 是否保持连接 | -k 1 (保持连接) |
--csv | 输出 CSV 格式结果 | --csv |
三、典型测试场景示例
1️⃣ 测试不同命令的吞吐量
# 测试 SET、GET、HSET 命令
redis-benchmark -t set,get,hset -c 50 -n 100000
2️⃣ 模拟高并发场景
# 使用 200 个并发连接,测试 50 万次请求
redis-benchmark -c 200 -n 500000
3️⃣ 测试大尺寸数据
# 测试 1KB 数据的 SET/GET 性能
redis-benchmark -d 1024 -t set,get
4️⃣ 使用 Pipeline 提升性能
# 每次批量发送 100 个命令(减少网络往返)
redis-benchmark -P 100 -n 1000000
5️⃣ 测试集群性能
# 测试集群模式下所有节点的性能
redis-benchmark -h your-cluster-endpoint -p 6379 --cluster
四、结果解读与优化建议
1️⃣ 关键指标
• QPS(每秒请求数):反映服务器处理能力。
• 单线程 Redis 的典型 QPS 在 5 万~10 万之间(取决于命令复杂度)。
• 延迟(Latency):单个请求的响应时间。
• 正常值在 0.1~2 毫秒(内存操作),若超过 10 毫秒需排查问题。
• 吞吐量(Throughput):单位时间传输的数据量(受带宽限制)。
04 RedisKey基本命令
以下是 Redis 中与 Key(键) 相关的基本命令,涵盖常用操作如创建、删除、查询和管理键:
1. 基础操作
• EXISTS key [key ...]
检查一个或多个键是否存在,返回存在的数量。
EXISTS mykey # 返回 1(存在)或 0(不存在)
• DEL key [key ...]
删除一个或多个键,返回被删除的键的数量。
DEL key1 key2 # 删除 key1 和 key2
• TYPE key
返回键存储的数据类型(如 string
, hash
, list
, set
等)。
TYPE mykey # 返回数据类型
2. 过期时间
• EXPIRE key seconds
为键设置过期时间(秒)。
EXPIRE mykey 60 # 60 秒后自动删除
• TTL key
查看键的剩余存活时间(秒)。
• -1
: 键未设置过期时间(永久存在)。
• -2
: 键已过期或不存在。
• PERSIST key
移除键的过期时间,使其永久存在。
PERSIST mykey
• PEXPIRE
/ PTTL
类似 EXPIRE
和 TTL
,但时间单位为毫秒。
3. 查找键
• KEYS pattern
按通配符模式查找键(如 *
匹配任意字符,?
匹配单个字符)。
KEYS user:* # 查找所有以 "user:" 开头的键
• SCAN cursor [MATCH pattern] [COUNT n]
安全迭代所有键(避免 KEYS
的性能问题)。
SCAN 0 MATCH user:* COUNT 100 # 分批返回匹配的键
4. 重命名与移动
• RENAME key newkey
重命名键(若 newkey
已存在,则覆盖其值)。
RENAME oldkey newkey
• RENAMENX key newkey
仅当 newkey
不存在时,才重命名键。
• MOVE key db
将键移动到另一个数据库(如 db 0-15
)。
MOVE mykey 1 # 移动到数据库 1
5. 其他实用命令
• RANDOMKEY
随机返回一个存在的键。
• DUMP key
和 RESTORE key ttl serialized-value
序列化键的值(DUMP
)并恢复(RESTORE
)。
• UNLINK key [key ...]
异步删除键(类似 DEL
,但非阻塞,适合删除大键)。
注意事项
KEYS *
慎用:会遍历所有键,可能阻塞 Redis 服务(生产环境建议用SCAN
)。- 键的命名:建议用
:
分隔层级(如user:1001:profile
)。 - 过期时间精度:Redis 过期时间精度为毫秒级,但实际删除操作可能稍有延迟。
掌握这些命令后,可以高效管理 Redis 中的键和数据生命周期。
05 String字符串类型
1. String 类型特点
• 基础数据类型:可存储文本、数字、二进制数据(如图片序列化内容)。
• 最大容量:单值最大 512MB。
• 灵活操作:支持数值计算、位操作、子字符串处理等。
2. 基础命令
设置与获取值
• SET key value [EX seconds] [PX milliseconds] [NX|XX]
设置键值对,支持可选参数:
• EX
/ PX
:设置过期时间(秒/毫秒)。
• NX
:仅当键不存在时设置。
• XX
:仅当键存在时设置。
SET username "admin" EX 60 # 设置键,60秒后过期
• GET key
获取键对应的值。
GET username # 返回 "admin"
• DEL key
删除键(通用命令,适用于所有类型)。
批量操作
• MSET key1 value1 key2 value2 ...
批量设置多个键值对。
MSET name "Alice" age 30 email "alice@example.com"
• MGET key1 key2 ...
批量获取多个键的值。
MGET name age email # 返回 ["Alice", "30", "alice@example.com"]
3. 数字操作
• INCR key
将键存储的整数值 +1(原子操作,适合计数器)。
SET views 100
INCR views # 返回 101
• INCRBY key increment
/ DECRBY key decrement
增减指定数值。
INCRBY views 10 # 值变为 111
DECRBY views 5 # 值变为 106
• INCRBYFLOAT key increment
增减浮点数。
SET price 99.5
INCRBYFLOAT price 0.5 # 返回 100.0
4. 字符串操作
• APPEND key value
向字符串末尾追加内容,返回新长度。
APPEND username "@domain" # "admin" → "admin@domain"
• STRLEN key
获取字符串长度。
STRLEN username # 返回 12("admin@domain"的长度)
• GETRANGE key start end
获取子字符串(类似 substring
,下标从 0 开始)。
GETRANGE username 0 4 # 返回 "admin"
• SETRANGE key offset value
从指定位置覆盖字符串。
SETRANGE username 5 "-user" # "admin@domain" → "admin-user"
5. 位操作(Bitmaps)
• SETBIT key offset 0|1
设置二进制位的值(0
或 1
)。
SETBIT login_log 100 1 # 记录用户ID 100的登录状态
• GETBIT key offset
获取指定二进制位的值。
GETBIT login_log 100 # 返回 1
• BITCOUNT key [start end]
统计值为 1
的位数(常用于活跃用户统计)。
BITCOUNT login_log # 返回总登录用户数
• BITOP operation destkey key1 key2 ...
对多个 Bitmap 执行按位运算(AND
/OR
/XOR
/NOT
)。
6. 过期相关
• SETEX key seconds value
设置键值对并指定过期时间(秒)。
SETEX session_token 3600 "abc123"
• PSETEX key milliseconds value
类似 SETEX
,但时间单位为毫秒。
7. 其他命令
• GETSET key value
设置新值并返回旧值(适用于原子替换场景)。
GETSET counter 0 # 重置计数器并返回旧值
8. 使用场景
- 缓存:存储热点数据(如 HTML 片段、用户信息)。
- 计数器:文章阅读量、用户点赞数(利用
INCR
原子性)。 - 会话管理:存储临时 Token 或 Session ID(配合
SETEX
)。 - 位图统计:记录用户活跃状态、签到打卡。
06 List列表类型
1. List 类型特点
• 有序集合:元素按插入顺序排列,允许重复值。
• 双向操作:支持从头部(Left)或尾部(Right)插入/删除元素。
• 底层结构:使用快速列表(QuickList,由多个压缩的链表节点组成),兼顾内存和性能。
2. 基础命令
插入元素
• LPUSH key value [value ...]
从列表**头部(Left)**插入一个或多个元素。
LPUSH tasks "task1" "task2" # 列表顺序:task2 → task1
• RPUSH key value [value ...]
从列表**尾部(Right)**插入一个或多个元素。
RPUSH tasks "task3" # 列表顺序:task2 → task1 → task3
• LINSERT key BEFORE|AFTER pivot value
在指定元素(pivot
)的前或后插入新元素。
LINSERT tasks BEFORE "task1" "task0" # 插入到 task1 前
查询元素
• LRANGE key start stop
获取指定索引范围的元素(支持负数,-1
表示最后一个元素)。
LRANGE tasks 0 -1 # 返回所有元素
• LINDEX key index
获取指定索引位置的元素。
LINDEX tasks 0 # 返回第一个元素
• LLEN key
返回列表长度。
LLEN tasks # 返回元素总数
删除元素
• LPOP key [count]
从头部移除并返回一个或多个元素(Redis 6.2+ 支持 count
参数)。
LPOP tasks # 移除并返回头部元素
• RPOP key [count]
从尾部移除并返回一个或多个元素。
• LREM key count value
删除列表中与 value
相等的元素:
• count > 0
:从头部开始删除 count
个匹配项。
• count < 0
:从尾部开始删除 |count|
个匹配项。
• count = 0
:删除所有匹配项。
LREM tasks 0 "task1" # 删除所有值为 "task1" 的元素
• LTRIM key start stop
保留指定索引范围内的元素,其余删除。
LTRIM tasks 0 2 # 仅保留前3个元素
3. 阻塞操作
• BLPOP key [key ...] timeout
从头部阻塞移除并返回元素,直到列表有元素或超时(timeout
单位为秒,0
表示无限等待)。
BLPOP tasks 30 # 等待 tasks 列表的元素,最多等30秒
• BRPOP key [key ...] timeout
类似 BLPOP
,但从尾部移除元素。
• BRPOPLPUSH source destination timeout
从 source
尾部弹出元素,并插入到 destination
头部,阻塞直到有元素或超时。
4. 其他命令
• RPOPLPUSH source destination
原子操作:从 source
尾部弹出元素,并插入到 destination
头部。
RPOPLPUSH tasks backup # 将 tasks 尾部元素移动到 backup 头部
• LSET key index value
设置指定索引位置的元素值。
LSET tasks 0 "new_task" # 修改第一个元素的值
5. 使用场景
-
消息队列
• 生产者用LPUSH
插入任务,消费者用BRPOP
阻塞获取任务。
• 示例:# 生产者 LPUSH queue "msg1" # 消费者 BRPOP queue 30
-
最新消息列表
• 用LPUSH
插入最新消息,LRANGE 0 9
获取前10条最新消息。 -
分页查询
• 结合LRANGE
实现分页(如LRANGE list 0 9
获取第一页)。 -
循环存储
• 用LTRIM
限制列表长度,保留最近 N 条数据(如日志记录)。
07 Set集合类型
1. Set 类型特点
• 无序性:元素存储无固定顺序(不可通过索引访问)。
• 唯一性:元素不可重复,自动去重。
• 集合运算:支持交集(SINTER
)、并集(SUNION
)、差集(SDIFF
)等操作。
• 底层结构:基于哈希表或整数集合(元素全为整数时优化内存)。
2. 基础命令
元素操作
• SADD key member [member ...]
向集合添加一个或多个元素,返回成功添加的数量。
SADD tags "redis" "database" "nosql"
• SREM key member [member ...]
删除集合中指定元素,返回成功删除的数量。
SREM tags "nosql"
• SMEMBERS key
返回集合中所有元素(无序)。
SMEMBERS tags # 返回 ["redis", "database"]
• SISMEMBER key member
检查元素是否在集合中,返回 1
(存在)或 0
(不存在)。
SISMEMBER tags "redis" # 返回 1
• SCARD key
返回集合元素总数。
SCARD tags # 返回 2
随机操作
• SPOP key [count]
随机移除并返回一个或多个元素(适合抽奖场景)。
SPOP tags 1 # 随机移除一个元素
• SRANDMEMBER key [count]
随机返回一个或多个元素(不删除)。
SRANDMEMBER tags 2 # 随机返回两个元素
集合运算
• SINTER key [key ...]
返回多个集合的交集。
SINTER set1 set2 # 返回 set1 和 set2 的共同元素
• SUNION key [key ...]
返回多个集合的并集。
• SDIFF key [key ...]
返回第一个集合与其他集合的差集(即仅存在于第一个集合的元素)。
• SINTERSTORE
/ SUNIONSTORE
/ SDIFFSTORE
将集合运算结果存储到新集合。
SINTERSTORE result_set set1 set2 # 交集存入 result_set
元素移动
• SMOVE source destination member
将元素从 source
集合移动到 destination
集合。
SMOVE tags new_tags "redis"
3. 使用场景
-
唯一性存储
• 存储用户 ID、IP 地址等需去重的数据。SADD unique_ips "192.168.1.1" # 自动过滤重复 IP
-
标签系统
• 为对象(如文章)打标签,并计算共同标签。# 文章 1001 的标签 SADD article:1001:tags "tech" "database" # 文章 1002 的标签 SADD article:1002:tags "tech" "cloud" # 查找两篇文章的共同标签 SINTER article:1001:tags article:1002:tags # 返回 ["tech"]
-
社交关系
• 存储用户的好友/关注列表,计算共同好友。SADD user:1001:friends "2001" "2002" SADD user:2001:friends "1001" "3003" SINTER user:1001:friends user:2001:friends # 返回 ["2001"]
-
随机抽奖
• 使用SPOP
或SRANDMEMBER
实现随机选取。# 参与抽奖用户 SADD lottery "user1" "user2" "user3" # 抽取一名获奖者 SPOP lottery 1 # 随机移除并返回一个用户
08 Hash哈希类型
1. Hash 类型特点
• 结构化存储:存储字段-值(field-value)对,适合表示对象(如用户、商品)。
• 高效部分更新:可单独操作字段,无需读取整个对象。
• 内存优化:小哈希使用 ziplist 编码(紧凑存储),大哈希自动转为 hashtable。
• 最大容量:单个哈希可存储多达 2^32 - 1 个字段-值对。
2. 基础命令
设置与获取字段
• HSET key field value [field value ...]
设置一个或多个字段的值(Redis 4.0+ 支持多字段)。
HSET user:1001 name "Alice" age 30
• HGET key field
获取指定字段的值。
HGET user:1001 name # 返回 "Alice"
• HMSET key field value [field value ...]
批量设置字段(旧版命令,Redis 4.0+ 推荐用 HSET
)。
• HMGET key field [field ...]
批量获取多个字段的值。
HMGET user:1001 name age # 返回 ["Alice", "30"]
• HGETALL key
返回所有字段和值(格式为交替排列的字段和值)。
HGETALL user:1001 # 返回 ["name", "Alice", "age", "30"]
删除与检查
• HDEL key field [field ...]
删除一个或多个字段。
HDEL user:1001 age # 删除 age 字段
• HEXISTS key field
检查字段是否存在,返回 1
(存在)或 0
(不存在)。
HEXISTS user:1001 email # 返回 0
• HKEYS key
返回所有字段名。
HKEYS user:1001 # 返回 ["name"]
• HVALS key
返回所有字段值。
HVALS user:1001 # 返回 ["Alice"]
数值操作
• HINCRBY key field increment
将字段的整数值增加指定数值。
HINCRBY user:1001 score 5 # 值 +5(若字段不存在,从0开始)
• HINCRBYFLOAT key field increment
将字段的浮点数值增加指定数值。
HINCRBYFLOAT user:1001 balance 10.5
3. 其他命令
• HLEN key
返回字段总数。
HLEN user:1001 # 返回 1(仅剩 name 字段)
• HSETNX key field value
仅当字段不存在时设置其值。
HSETNX user:1001 email "alice@example.com" # 成功返回 1
• HSTRLEN key field
返回字段值的字符串长度。
HSTRLEN user:1001 name # 返回 5("Alice")
• HSCAN key cursor [MATCH pattern] [COUNT n]
安全迭代哈希的字段-值对,避免阻塞。
HSCAN user:1001 0 MATCH *name* COUNT 10
4. 使用场景
-
对象存储
• 存储用户、商品等结构化数据,支持部分更新。HSET product:5001 title "Laptop" price 999.99 stock 10
-
计数器组
• 统计多维数据(如用户点赞数、访问次数)。HINCRBY user:1001 likes 1 # 点赞数 +1 HINCRBY user:1001 views 1 # 浏览数 +1
-
配置管理
• 集中管理应用配置项,支持动态修改。HSET app_config cache_enabled 1 max_connections 100
-
购物车
• 字段为商品ID,值为数量,方便增减操作。HINCRBY cart:1001 item:2001 2 # 商品2001数量加2
5. 示例:用户信息管理
# 创建用户
HSET user:1001 name "Alice" age 30 email "alice@example.com"# 更新年龄
HSET user:1001 age 31# 增加积分
HINCRBY user:1001 points 100# 获取邮箱
HGET user:1001 email# 删除字段(如临时字段)
HDEL user:1001 temporary_token# 获取所有信息
HGETALL user:1001
09 Zset有序集合
1. Zset 类型特点
• 有序性:成员(member)按分数(score)排序,分数可重复,但成员唯一。
• 高效范围查询:支持按分数、排名(下标)、字典序范围检索。
• 底层结构:结合跳跃表(SkipList)和哈希表,保证排序与查询性能。
• 适用场景:排行榜、优先级队列、时间轴等需要排序的场景。
2. 基础命令
添加与更新成员
• ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
添加或更新成员,支持选项:
• NX
:仅添加新成员(不更新已有成员)。
• XX
:仅更新已有成员。
• INCR
:对成员分数增加指定值(类似 ZINCRBY
)。
ZADD leaderboard 100 "Alice" 90 "Bob" # 添加成员及分数
ZADD leaderboard XX CH 110 "Alice" # 更新 Alice 的分数,并返回变更数量
• ZINCRBY key increment member
为指定成员增加分数(原子操作)。
ZINCRBY leaderboard 10 "Alice" # Alice 的分数变为 110
查询成员
• ZSCORE key member
返回成员的分数。
ZSCORE leaderboard "Alice" # 返回 110
• ZRANK key member [WITHSCORE]
返回成员按分数升序的排名(从 0 开始)。
ZRANK leaderboard "Bob" # 返回 0(分数最低)
• ZREVRANK key member
返回成员按分数降序的排名。
• ZCARD key
返回集合中成员总数。
3. 范围查询
按分数或排名
• ZRANGE key start stop [WITHSCORES]
按升序返回指定排名范围的成员(WITHSCORES
包含分数)。
ZRANGE leaderboard 0 -1 WITHSCORES # 返回所有成员及分数(升序)
• ZREVRANGE key start stop [WITHSCORES]
按降序返回指定排名范围的成员。
• ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
返回分数在 [min, max]
之间的成员(升序)。
ZRANGEBYSCORE leaderboard 90 110 # 分数在 90~110 的成员
• ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
返回分数在 [min, max]
之间的成员(降序)。
按字典序(LEX)
• ZRANGEBYLEX key min max [LIMIT offset count]
当成员分数相同时,按字典序范围查询(需所有成员分数相同)。
ZADD logins 0 "user:1001" 0 "user:1002" 0 "user:1005"
ZRANGEBYLEX logins [user:1001 (user:1005 # 返回 user:1001 和 user:1002
4. 集合运算
• ZINTERSTORE destkey numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX]
计算多个 Zset 的交集,结果存入 destkey
。
ZINTERSTORE result 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM
• ZUNIONSTORE destkey numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX]
计算多个 Zset 的并集。
5. 删除与统计
• ZREM key member [member ...]
删除一个或多个成员。
ZREM leaderboard "Bob"
• ZREMRANGEBYRANK key start stop
删除指定排名范围的成员。
ZREMRANGEBYRANK leaderboard 0 0 # 删除升序排名第0的成员
• ZREMRANGEBYSCORE key min max
删除分数在 [min, max]
之间的成员。
• ZCOUNT key min max
统计分数在 [min, max]
之间的成员数量。
6. 阻塞命令
• BZPOPMAX key [key ...] timeout
阻塞获取并删除分数最高的成员(支持多个 Zset)。
BZPOPMAX tasks 30 # 等待 tasks 中的元素,最多30秒
• BZPOPMIN key [key ...] timeout
类似 BZPOPMAX
,但操作分数最低的成员。
7. 使用场景
-
实时排行榜
• 用分数表示用户积分,按排名获取 Top N。ZADD leaderboard 300 "Alice" 250 "Bob" 200 "Charlie" ZREVRANGE leaderboard 0 2 WITHSCORES # 前三名
-
延时队列
• 将任务到期时间戳作为分数,定时轮询到期任务。ZADD tasks 1672531200 "task1" # 2023-01-01 00:00:00 执行 ZRANGEBYSCORE tasks 0 1672531200 # 获取所有到期任务
-
范围筛选
• 按价格区间筛选商品,或按时间范围查询日志。ZRANGEBYSCORE products:price 50 100 # 价格50~100的商品
8. 示例:游戏排行榜
# 添加玩家得分
ZADD game_rank 1500 "PlayerA" 1200 "PlayerB" 1800 "PlayerC"# 更新得分(PlayerB 得分+300)
ZINCRBY game_rank 300 "PlayerB"# 获取前三名(降序)
ZREVRANGE game_rank 0 2 WITHSCORES# 查询 PlayerA 的排名
ZREVRANK game_rank "PlayerA"# 删除得分低于1400的玩家
ZREMRANGEBYSCORE game_rank -inf 1399
10 Geospatial地理位置类型
1. Geospatial 类型特点
• 基于 Zset 实现:地理位置数据存储在有序集合中,成员唯一,分数(score)为经纬度编码的 GEOHASH 值。
• 高效查询:支持计算两点距离、搜索半径内成员、聚合地理位置数据。
• 适用场景:附近的人、地点搜索、配送范围计算等。
2. 核心命令
添加地理位置
• GEOADD key [NX|XX] longitude latitude member [longitude latitude member ...]
添加一个或多个地理位置(经纬度 + 成员名)。
• NX
:仅添加新成员(不更新已有成员)。
• XX
:仅更新已有成员。
GEOADD restaurants 116.405285 39.904989 "KFC" 121.473701 31.230416 "Starbucks"
查询地理位置
• GEOPOS key member [member ...]
返回成员的经纬度坐标。
GEOPOS restaurants "KFC" # 返回 ["116.405285", "39.904989"]
• GEOHASH key member [member ...]
返回成员的 GEOHASH 编码(缩短的字符串表示,便于存储和比较)。
GEOHASH restaurants "KFC" # 返回 ["wx4g0b7xrt0"]
距离计算
• GEODIST key member1 member2 [m|km|ft|mi]
计算两个成员间的距离,默认单位米(m
)。
GEODIST restaurants "KFC" "Starbucks" km # 返回两地距离(千米)
范围搜索
• GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT n] [ASC|DESC]
查询指定经纬度半径内的成员,支持返回坐标、距离、GEOHASH 等。
GEORADIUS restaurants 116.40 39.90 5 km WITHDIST # 返回5公里内的餐厅及距离
• GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] ...
以某个成员为中心,搜索半径内的其他成员。
GEORADIUSBYMEMBER restaurants "KFC" 10 km # 返回 KFC 周围10公里的其他餐厅
3. 使用场景
1. 附近的人/地点
# 用户上传位置
GEOADD users:locations 116.408 39.905 "user1001"# 查找用户周边3公里内的其他人
GEORADIUS users:locations 116.408 39.905 3 km WITHDIST
2. 配送范围校验
# 商家设置配送范围(如5公里)
GEOADD shops 116.40 39.90 "shop_001"# 用户地址是否在配送范围内
GEODIST shops "shop_001" "user_address" km <= 5
3. 地点聚合统计
# 统计某区域内所有咖啡店数量
GEORADIUS shops 116.40 39.90 5 km STORE coffee_shops # 结果存入新集合
ZCARD coffee_shops # 获取数量
4. 注意事项
-
经纬度范围
• 经度范围:-180 到 180
,纬度范围:-85.05112878 到 85.05112878
(超出会报错)。 -
数据存储本质
• 底层使用 Zset 存储,可通过ZRANGE
、ZREM
等命令操作成员,但分数为 GEOHASH 值(非直观经纬度)。 -
精度与性能
• GEOHASH 精度越高,搜索越精确,但计算量增大。需根据场景平衡精度与性能。 -
地球模型假设
• Redis 使用 WGS84 坐标系(地球近似为球体),实际应用中需注意与地图平台(如高德、百度)坐标系的差异(可能需转换)。
5. 示例:咖啡店搜索系统
# 添加咖啡店位置
GEOADD coffee_shops 116.406275 39.904199 "Starbucks_A" 116.407812 39.903845 "Luckin_B"# 用户当前位置:116.406000, 39.904000
# 搜索1公里内的咖啡店,按距离升序排列,返回店名和距离
GEORADIUS coffee_shops 116.406000 39.904000 1 km WITHDIST ASC
11 HyperLogLog基数统计类型
1. HyperLogLog 特点
• 基数统计:用于估算集合中不重复元素的数量(如统计 UV、日活用户)。
• 内存极低:固定占用 12KB 内存(误差率约 0.81%
),与元素数量无关。
• 去重不存储:只计算基数,不保存元素本身,无法查询具体内容。
2. 核心命令
• PFADD key element [element ...]
向 HyperLogLog 添加元素,返回 1
表示基数变化,0
表示无变化。
PFADD daily_uv "user1" "user2" "user3"
• PFCOUNT key [key ...]
估算单个或多个 HyperLogLog 的基数(合并后去重统计)。
PFCOUNT daily_uv # 返回估算的 UV 值(如 3)
• PFMERGE destkey sourcekey [sourcekey ...]
合并多个 HyperLogLog 到 destkey
(用于统计多日总 UV)。
PFMERGE weekly_uv day1_uv day2_uv day3_uv
PFCOUNT weekly_uv # 估算合并后的总 UV
3. 使用场景
-
网站 UV 统计
• 统计每日独立访客(无需精确值,内存敏感场景)。# 用户访问时添加 PFADD 2023-10-01:uv "192.168.1.1" "10.0.0.2" # 当日 UV 查询 PFCOUNT 2023-10-01:uv
-
多维度聚合统计
• 合并多日/多来源数据,估算总 UV。PFMERGE total_uv oct_uv nov_uv PFCOUNT total_uv
-
大规模去重场景
• 日志去重、广告点击用户数统计(内存有限时替代 Set)。
4. 注意事项
-
误差率
• 标准误差约0.81%
,实际误差可能随数据分布波动。
• 示例:若真实 UV 为 100 万,估算结果在991,900 ~ 1,008,100
之间。 -
不可逆性
• 无法获取原始数据或判断某个元素是否存在(如user1
是否访问过)。 -
内存固定
• 无论存储 1 个还是 1 亿个元素,均占用 12KB(除非启用稀疏编码优化)。 -
不支持删除操作
• 无法删除已添加的元素(需重新统计或使用新键)。
5. 示例:统计网站周活跃用户
# 每日添加 UV
PFADD 2023-10-01:uv "user1" "user2" "user5"
PFADD 2023-10-02:uv "user1" "user3" "user4"# 合并一周数据
PFMERGE week_uv 2023-10-01:uv 2023-10-02:uv ... # 获取周活用户估算值
PFCOUNT week_uv
6. 对比其他方案
方案 | 精确性 | 内存消耗 | 适用场景 |
---|---|---|---|
Set | 精确 | 高(存储元素) | 小数据量 + 需精确查询 |
Bitmap | 精确 | 中等(按位存储) | 整数 ID 场景(如用户) |
HyperLogLog | 估算 | 极低(12KB) | 大数据量 + 容忍误差 |
12 Bitmap位图类型
1. Bitmap 类型特点
• 本质是 String:底层使用字符串存储二进制位数组,每个位(bit)可表示 0
或 1
。
• 高效存储:适合存储大量二元状态(是/否、存在/不存在),极大节省内存。
• 位运算支持:支持按位操作(AND/OR/XOR/NOT),适合批量统计与分析。
2. 核心命令
设置与查询位
• SETBIT key offset 0|1
设置指定偏移量(offset)的二进制位值(0 或 1)。
SETBIT user:1001:signin 5 1 # 第5天签到(偏移量从0开始)
• GETBIT key offset
获取指定偏移量的位值。
GETBIT user:1001:signin 5 # 返回1(已签到)
统计与运算
• BITCOUNT key [start end]
统计位图中值为 1
的位数(支持字节范围)。
BITCOUNT user:1001:signin # 总签到天数
• BITPOS key bit [start] [end]
查找第一个值为 bit
(0 或 1)的偏移量。
BITPOS user:1001:signin 1 # 找到第一个签到日
• BITOP operation destkey key [key ...]
对多个位图进行按位运算(AND
/OR
/XOR
/NOT
),结果存入 destkey
。
BITOP AND active_all active_day1 active_day2 # 两日均活跃的用户
批量操作
• BITFIELD key [GET type offset] [SET type offset value] ...
原子操作多个位字段,支持整数类型(如 u8
表示8位无符号整数)。
BITFIELD user:flags SET u1 0 1 SET u1 1 0 # 设置第0位为1,第1位为0
3. 使用场景
1. 用户签到系统
• 每日签到:用偏移量表示日期(如一年365天),每个用户一个位图。
# 用户1001第5天签到
SETBIT sign:202310:1001 4 1 # 偏移量从0开始,4表示第5天
# 统计本月签到次数
BITCOUNT sign:202310:1001
2. 实时活跃用户统计
• 每日活跃用户:将用户ID映射为偏移量,快速标记和统计。
# 用户ID=1001活跃
SETBIT active:20231001 1001 1
# 统计某天活跃用户总数
BITCOUNT active:20231001
3. 布隆过滤器(Bloom Filter)
• 去重判断:结合多个哈希函数,用位图判断元素是否可能存在(存在误差)。
# 添加元素(哈希到多个位)
SETBIT bloom_filter 100 1
SETBIT bloom_filter 200 1
# 检查元素是否存在(所有位均为1才可能存在)
GETBIT bloom_filter 100 && GETBIT bloom_filter 200
4. 示例:统计连续签到天数
# 用户连续签到第3天、第4天、第5天
SETBIT user:1001:signin 2 1
SETBIT user:1001:signin 3 1
SETBIT user:1001:signin 4 1# 查找最长连续签到(需结合 BITPOS 和 BITCOUNT)
# 或使用 Lua 脚本实现复杂逻辑
5. 适用场景总结
• 二元状态跟踪:签到、在线状态、特征标记。
• 批量统计:活跃用户数、满足条件的用户群。
• 高性能过滤:布隆过滤器、黑白名单。
13 各大类型的区别
1. 基础类型对比
类型 | 存储结构 | 核心特点 | 适用场景 | 注意事项 |
---|---|---|---|---|
String | 简单键值对 | 可存文本、数字、二进制数据,支持原子增减 | 缓存、计数器、分布式锁 | 大 Value 影响性能,最大 512MB |
List | 双向链表(QuickList) | 有序、可重复,支持头尾操作 | 消息队列、最新列表、分页 | 中间操作(如 LINSERT)性能差 |
Hash | 字段-值对(HashTable) | 结构化存储,支持部分更新 | 对象存储(用户、商品)、配置项 | 大 Hash 的 HGETALL 会阻塞 |
Set | 无序集合(HashTable) | 元素唯一,支持集合运算 | 标签、好友关系、抽奖 | 无序,无法通过索引访问 |
Zset | 有序集合(SkipList) | 成员唯一,按分数排序,范围查询高效 | 排行榜、优先级队列、延时任务 | 分数精度问题,大集合查询性能低 |
2. 特殊类型对比
类型 | 底层实现 | 核心特点 | 适用场景 | 注意事项 |
---|---|---|---|---|
Bitmap | String 二进制位 | 节省内存,支持位运算 | 签到、在线状态、布隆过滤器 | 稀疏数据浪费内存 |
HyperLogLog | 固定 12KB 结构 | 超低内存基数估算,误差约 0.81% | 大规模 UV 统计 | 无法获取具体数据,只能估算 |
Geospatial | Zset(GEOHASH) | 地理位置存储与范围查询 | 附近的人、地点搜索 | 坐标需符合 WGS84 范围 |
3. 关键区别总结
数据组织方式
• String:单一值存储,无结构。
• Hash:字段-值对,类似 JSON 对象。
• List/Set/Zset:集合型结构,List 有序可重复,Set 无序唯一,Zset 有序唯一。
• Bitmap:二进制位数组,位操作高效。
• Geospatial:基于 Zset,用 GEOHASH 编码地理位置。
查询与操作
• 高效范围查询:Zset(分数范围)、Geospatial(半径范围)。
• 集合运算:Set(交并差)、Zset(交并差带权重)。
• 原子计数:String(INCR)、Hash(HINCRBY)、Zset(分数更新)。
内存与性能
• 内存敏感:
• HyperLogLog(固定 12KB) < Bitmap(按需分配) < String(存储简单值)。
• 大 Key 风险:
• List/Hash/Set/Zset 元素过多时,全量查询(如 LRANGE)会阻塞服务。
精确性
• 精确存储:String、List、Hash、Set、Zset、Bitmap。
• 估算统计:HyperLogLog(基数估算)。
4. 场景化选择指南
• 需要排序和范围查询 → Zset(如排行榜)
• 存储对象多个属性 → Hash(如用户资料)
• 去重且无需排序 → Set(如标签系统)
• 简单键值或计数器 → String(如缓存、计数器)
• 二进制状态标记 → Bitmap(如用户签到)
• 大规模独立用户统计 → HyperLogLog(如 UV 统计)
• 地理位置服务 → Geospatial(如附近的人)
5. 底层数据结构影响
• Zset 使用跳跃表(范围查询快) + 哈希表(快速定位成员)。
• Hash/Set 小数据时用 ziplist(内存紧凑),大数据转 hashtable。
• List 使用 QuickList(多节点链表 + ziplist 压缩)。
14 Redis基本事务操作
1. 事务核心命令
Redis 事务通过 MULTI
、EXEC
、DISCARD
、WATCH
实现,保证命令的批量原子执行。
1.1 开启与执行事务
• MULTI
标记事务开始,后续命令加入队列(不会立即执行)。
MULTI
SET balance:1001 1000
SET balance:1002 2000
EXEC # 提交事务,执行队列中的命令
• EXEC
执行事务队列中的所有命令,返回各命令的执行结果。
• DISCARD
取消事务,清空命令队列。
1.2 监控键值(CAS 操作)
• WATCH key [key ...]
监视一个或多个键,若在事务执行前这些键被其他客户端修改,则事务中断(返回 nil
)。
WATCH balance:1001 # 监视账户1001的余额
MULTI
INCRBY balance:1001 -100
INCRBY balance:1002 100
EXEC # 如果 balance:1001 被其他操作修改,事务失败
• UNWATCH
取消对所有键的监视(通常在事务失败后重试前调用)。
2. 事务执行流程
# 客户端A
WATCH key1 # 监视 key1
MULTI
SET key1 "new_value"
GET key1
EXEC # 提交事务# 若其他客户端在 WATCH 和 EXEC 之间修改了 key1,事务不执行
3. 错误处理
3.1 入队错误(语法错误)
• 命令入队时立即报错(如语法错误),事务无法提交。
MULTI
SET key1 "value1"
INVALID_CMD # 错误命令,入队失败
EXEC # 直接报错,事务不执行
3.2 执行错误(运行时错误)
• 执行时出错(如对字符串执行 HINCRBY
),仅错误命令失败,其他命令继续执行。
MULTI
SET key1 "abc"
INCR key1 # 对字符串执行 INCR 会失败
EXEC # 返回 ["OK", (error)]
4. 使用场景
4.1 转账操作(CAS 模式)
WATCH balance:1001 balance:1002
balance = GET balance:1001
if balance >= 100:MULTIDECRBY balance:1001 100INCRBY balance:1002 100EXEC # 提交事务
else:UNWATCH
4.2 批量操作原子性
MULTI
LPUSH task_queue "task1"
LPUSH task_queue "task2"
EXEC # 确保两个任务同时入队
5. 注意事项
-
无回滚机制
Redis 事务不支持回滚(Rollback),需自行处理部分失败场景。 -
性能影响
长时间事务会阻塞其他客户端,建议拆分大事务。 -
竞态条件
使用WATCH
实现乐观锁,避免数据竞争(需配合重试逻辑)。 -
替代方案
复杂逻辑可使用 Lua 脚本(原子性更强,无竞态问题):EVAL "if redis.call('GET', KEYS[1]) >= ARGV[1] then redis.call('DECRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[2], ARGV[1]) return 1 else return 0 end" 2 balance:1001 balance:1002 100
6. 事务与 Lua 脚本对比
特性 | 事务 | Lua 脚本 |
---|---|---|
原子性 | 批量执行,但无回滚 | 原子执行,脚本整体生效 |
竞态处理 | 需配合 WATCH + 重试 | 天然原子,无需额外处理 |
性能 | 适用于简单批量操作 | 适合复杂逻辑,减少网络开销 |
调试难度 | 易调试(命令独立执行) | 需处理脚本错误 |
掌握事务操作后,可应对简单原子批量操作,但复杂场景建议优先使用 Lua 脚本,避免事务的局限性。
15 乐观锁
1. 乐观锁核心原理
• 无锁机制:假设操作期间数据未被其他客户端修改,仅在提交时检查冲突。
• 冲突检测:使用 WATCH
监视关键键,若被修改则事务失败,需重试。
• 适用场景:高并发、冲突概率较低的场景(如库存扣减、账户转账)。
2. 实现步骤
步骤 1:监视关键键
使用 WATCH
命令监视需要操作的键。
WATCH balance:1001 balance:1002 # 监视账户1001和1002的余额
步骤 2:读取并校验数据
在事务外获取当前值,并进行业务逻辑校验。
current_balance = GET balance:1001
if current_balance < 100:UNWATCH # 取消监视,结束流程return "余额不足"
步骤 3:开启事务并提交
在 MULTI
事务中执行修改操作,通过 EXEC
提交。
MULTI
DECRBY balance:1001 100 # 扣减账户1001余额
INCRBY balance:1002 100 # 增加账户1002余额
result = EXEC # 提交事务
步骤 4:处理事务结果
• 成功:EXEC
返回事务内命令的结果列表。
• 失败:若 WATCH
的键被其他客户端修改,EXEC
返回 nil
,需重试。
if result is nil:# 重试整个流程(返回步骤1)
else:# 操作成功
3. 完整示例:转账操作
import redisdef transfer_funds(conn, from_account, to_account, amount, max_retries=3):retries = 0while retries < max_retries:try:# 监视账户余额conn.watch(f"balance:{from_account}", f"balance:{to_account}")# 读取当前余额from_balance = int(conn.get(f"balance:{from_account}") or 0)if from_balance < amount:conn.unwatch()return False # 余额不足# 开启事务pipeline = conn.pipeline()pipeline.multi()pipeline.decrby(f"balance:{from_account}", amount)pipeline.incrby(f"balance:{to_account}", amount)# 提交事务result = pipeline.execute()return True # 成功except redis.WatchError:# 事务冲突,重试retries += 1continuereturn False # 超过最大重试次数# 使用示例
r = redis.Redis()
transfer_funds(r, "1001", "1002", 100)
4. 注意事项
-
重试次数限制
必须设置最大重试次数(如max_retries=3
),避免无限循环。 -
键的监视范围
WATCH
的键不宜过多,否则增加冲突概率和性能开销。 -
性能优化
• 将WATCH
的键控制在最小范围(如只监视关键资源)。
• 在业务允许的情况下,缩短事务内的操作时间。 -
替代方案:Lua 脚本
对于复杂逻辑,直接使用 Lua 脚本(原子执行,无需WATCH
):local from_balance = tonumber(redis.call('GET', KEYS[1])) if from_balance < tonumber(ARGV[1]) thenreturn 0 end redis.call('DECRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[2], ARGV[1]) return 1
EVAL "..." 2 balance:1001 balance:1002 100
-
版本号模式
使用版本号字段(类似 CAS)优化冲突检测:WATCH item:1001:version current_version = GET item:1001:version MULTI SET item:1001:data "new_value" INCR item:1001:version # 版本号递增 EXEC
5. 与悲观锁的对比
特性 | 乐观锁 | 悲观锁(如 Redlock) |
---|---|---|
冲突处理 | 提交时检测冲突,失败重试 | 预先加锁,阻止其他操作 |
性能 | 高并发下更高效(无锁竞争) | 锁开销大,适合低频操作 |
适用场景 | 读多写少,冲突概率低 | 写多读少,冲突概率高 |
实现复杂度 | 简单(基于 WATCH + 重试) | 复杂(需处理锁超时、续期等问题) |
通过 WATCH
+ 事务的乐观锁机制,可在 Redis 中高效解决多数并发冲突问题,尤其适合高频轻量级操作。对于复杂场景,优先考虑 Lua 脚本或版本号模式。
16 通过Jedis操作Redis
1. 引入 Jedis 依赖
在 Maven 项目中添加依赖:
Maven
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.4.3</version> <!-- 使用最新版本 -->
</dependency>
2. 基本操作
2.1 创建 Jedis 连接
import redis.clients.jedis.Jedis;public class JedisDemo {public static void main(String[] args) {// 创建连接(默认端口 6379)try (Jedis jedis = new Jedis("localhost", 6379)) {// 认证(如果 Redis 启用了密码)jedis.auth("your_password");// 测试连接System.out.println("连接状态: " + jedis.ping()); // 应返回 "PONG"// 设置值jedis.set("key1", "Hello Redis");// 获取值String value = jedis.get("key1");System.out.println("key1 的值: " + value); // 输出 "Hello Redis"}}
}
2.2 使用连接池
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;public class JedisPoolDemo {public static void main(String[] args) {// 配置连接池JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(10); // 最大连接数poolConfig.setMaxIdle(5); // 最大空闲连接数poolConfig.setMinIdle(1); // 最小空闲连接数poolConfig.setTestOnBorrow(true); // 借出连接时校验try (JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379, 2000, "your_password")) {try (Jedis jedis = jedisPool.getResource()) {// 操作示例jedis.set("counter", "100");jedis.incr("counter");System.out.println(jedis.get("counter")); // 输出 "101"}}}
}
3. 操作数据类型
3.1 String(字符串)
jedis.set("user:1001:name", "Alice");
String name = jedis.get("user:1001:name");// 设置过期时间(30秒)
jedis.setex("temp_key", 30, "expire_me");
3.2 Hash(哈希)
jedis.hset("user:1001", "name", "Alice");
jedis.hset("user:1001", "age", "30");String name = jedis.hget("user:1001", "name");
Map<String, String> allFields = jedis.hgetAll("user:1001");
3.3 List(列表)
jedis.lpush("tasks", "task1", "task2");
List<String> tasks = jedis.lrange("tasks", 0, -1); // 获取所有元素
3.4 Set(集合)
jedis.sadd("tags", "java", "redis", "database");
Set<String> tags = jedis.smembers("tags");
3.5 ZSet(有序集合)
jedis.zadd("leaderboard", 100, "Alice");
jedis.zadd("leaderboard", 90, "Bob");// 获取前3名(按分数降序)
Set<String> top3 = jedis.zrevrange("leaderboard", 0, 2);
4. 事务操作
4.1 基本事务
try (Transaction tx = jedis.multi()) {tx.set("key1", "value1");tx.incr("counter");// 提交事务List<Object> results = tx.exec(); // 返回各命令的结果
}
4.2 乐观锁(WATCH)
jedis.watch("balance:1001"); // 监视键
int balance = Integer.parseInt(jedis.get("balance:1001"));if (balance >= 100) {Transaction tx = jedis.multi();tx.decrBy("balance:1001", 100);tx.incrBy("balance:1002", 100);List<Object> results = tx.exec();if (results == null) {System.out.println("事务冲突,请重试");}
} else {jedis.unwatch();
}
5. 高级功能
5.1 发布订阅
// 订阅频道
jedis.subscribe(new JedisPubSub() {@Overridepublic void onMessage(String channel, String message) {System.out.println("收到消息: " + message);}
}, "news_channel");// 发布消息
jedis.publish("news_channel", "Hello Subscribers!");
5.2 Pipeline(批量操作)
Pipeline pipeline = jedis.pipelined();
for (int i = 0; i < 1000; i++) {pipeline.set("key" + i, "value" + i);
}
pipeline.sync(); // 批量提交
6. 注意事项
-
资源释放
使用try-with-resources
或手动关闭Jedis
和JedisPool
,避免连接泄漏。 -
避免长连接
操作完成后及时释放连接,防止连接池耗尽。 -
异常处理
捕获JedisConnectionException
等异常,处理网络或 Redis 服务中断问题。 -
序列化
复杂对象需序列化为字符串存储(如 JSON、Protobuf)。
通过 Jedis,可以轻松实现 Redis 的各类操作,结合连接池和事务机制,满足高性能 Java 应用的需求。
17 Spring Boot 集成 Redis
1. 添加依赖
在 pom.xml
中引入 Spring Data Redis 依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 配置 Redis 连接
在 application.yml
或 application.properties
中配置 Redis 连接信息:
application.yml
spring:redis:host: localhostport: 6379password: your_password # 若无需密码可省略database: 0lettuce: # 使用 Lettuce 客户端(默认)pool:max-active: 8max-idle: 8min-idle: 0max-wait: -1ms
3. 注入 RedisTemplate
Spring Boot 自动配置了 RedisTemplate
和 StringRedisTemplate
,直接在代码中注入即可:
@Autowired
private RedisTemplate<String, Object> redisTemplate;@Autowired
private StringRedisTemplate stringRedisTemplate;
自定义序列化方式(可选)
默认使用 JDK 序列化,可能不适合可视化查看数据。推荐修改为 JSON 序列化:
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {//1. 创建 RedisTemplate 实例RedisTemplate<String, Object> template = new RedisTemplate<>();//2. 注入连接工厂:通过 setConnectionFactory(factory) 绑定到具体的 Redis 服务器。template.setConnectionFactory(factory);//3. 配置序列化器:分别设置 Key 和 Value 的序列化策略,确保读写一致性。// 3.1设置 Key 序列化为字符串template.setKeySerializer(RedisSerializer.string());template.setHashKeySerializer(RedisSerializer.string());// 3.2设置 Value 序列化为 JSONJackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);template.setValueSerializer(serializer);template.setHashValueSerializer(serializer);// 4. 返回配置后的 Bean:Spring 容器会管理此 Bean,供其他组件注入使用return template;}
}
4. 基础数据操作
4.1 String 类型
// 设置值
redisTemplate.opsForValue().set("user:1001:name", "Alice");
// 获取值
String name = (String) redisTemplate.opsForValue().get("user:1001:name");// 设置过期时间
redisTemplate.opsForValue().set("temp:key", "expire_me", 30, TimeUnit.SECONDS);
4.2 Hash 类型
// 设置字段
redisTemplate.opsForHash().put("user:1001", "name", "Alice");
// 获取字段
String name = (String) redisTemplate.opsForHash().get("user:1001", "name");// 获取所有字段
Map<Object, Object> userMap = redisTemplate.opsForHash().entries("user:1001");
4.3 List 类型
// 左端插入
redisTemplate.opsForList().leftPushAll("tasks", "task1", "task2");
// 获取列表
List<Object> tasks = redisTemplate.opsForList().range("tasks", 0, -1);
5. 使用缓存注解
5.1 启用缓存
在启动类添加 @EnableCaching
:
@SpringBootApplication
@EnableCaching
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
5.2 常用注解
• @Cacheable
:查询时缓存结果。
• @CachePut
:更新缓存。
• @CacheEvict
:删除缓存。
@Service
public class UserService {// 根据ID查询用户,结果缓存到 "users" 中,Key 为 #id@Cacheable(value = "users", key = "#id")public User getUserById(Long id) {// 模拟数据库查询return userRepository.findById(id).orElse(null);}// 更新用户信息,同时更新缓存@CachePut(value = "users", key = "#user.id")public User updateUser(User user) {return userRepository.save(user);}// 删除用户,同时清除缓存@CacheEvict(value = "users", key = "#id")public void deleteUser(Long id) {userRepository.deleteById(id);}
}
6. 事务管理
使用 SessionCallback
执行事务操作:
List<Object> results = redisTemplate.execute(new SessionCallback<>() {@Overridepublic List<Object> execute(RedisOperations operations) throws DataAccessException {operations.multi();operations.opsForValue().increment("counter");operations.opsForSet().add("processed", "task1");return operations.exec();}
});
7. 高级功能
7.1 发布订阅
// 发布消息
stringRedisTemplate.convertAndSend("news_channel", "Hello Subscribers!");// 订阅消息(需配置消息监听器)
@Component
public class RedisMessageListener implements MessageListener {@Overridepublic void onMessage(Message message, byte[] pattern) {System.out.println("收到消息: " + new String(message.getBody()));}
}@Configuration
public class RedisSubConfig {@Beanpublic RedisMessageListenerContainer container(RedisConnectionFactory factory, RedisMessageListener listener) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(factory);container.addMessageListener(listener, new ChannelTopic("news_channel"));return container;}
}
7.2 分布式锁
使用 Redisson
或 RedisTemplate
实现:
// 基于 RedisTemplate 的简单锁
public boolean tryLock(String key, String value, long expireTime) {return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS));
}public void unlock(String key) {redisTemplate.delete(key);
}
18 Spring Boot 中自定义 RedisTemplate
1. 创建配置类
定义一个配置类 RedisConfig
,用于自定义 RedisTemplate
的序列化方式。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 配置序列化器RedisSerializer<String> stringSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);// 配置 ObjectMapper 以支持类型信息ObjectMapper objectMapper = new ObjectMapper();objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);jsonSerializer.setObjectMapper(objectMapper);// Key 和 HashKey 使用字符串序列化template.setKeySerializer(stringSerializer);template.setHashKeySerializer(stringSerializer);// Value 和 HashValue 使用 JSON 序列化template.setValueSerializer(jsonSerializer);template.setHashValueSerializer(jsonSerializer);template.afterPropertiesSet();return template;}
}
2. 在业务代码中使用自定义的 RedisTemplate
在需要操作 Redis 的类中注入 RedisTemplate
:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public void saveUser(String key, User user) {redisTemplate.opsForValue().set(key, user);}public User getUser(String key) {return (User) redisTemplate.opsForValue().get(key);}
}
3. 配置说明
关键配置点
• 键的序列化:使用 StringRedisSerializer
,确保键在 Redis 中以字符串形式存储。
• 值的序列化:使用 Jackson2JsonRedisSerializer
,将对象序列化为 JSON 字符串。
• 类型信息支持:通过 ObjectMapper.activateDefaultTyping
添加类型信息,确保反序列化时能识别原始类型。
ObjectMapper 配置
• LaissezFaireSubTypeValidator.instance
:允许所有类型反序列化(生产环境建议限制为具体类型)。
• ObjectMapper.DefaultTyping.NON_FINAL
:为非 final 类添加类型信息。
4. 测试验证
写入数据
User user = new User("Alice", 30);
userService.saveUser("user:1001", user);
Redis CLI 查看
127.0.0.1:6379> GET user:1001
"{\"@class\":\"com.example.User\",\"name\":\"Alice\",\"age\":30}"
读取数据
User cachedUser = userService.getUser("user:1001");
System.out.println(cachedUser.getName()); // 输出 "Alice"