Redis基础阶段笔记

ops/2025/3/18 3:49:35/

01 Redis简介

一、Redis 的背景

诞生时间:2009 年,由意大利开发者 Salvatore Sanfilippo 为解决网站高并发问题而设计。
名字含义Remote Dictionary Server(远程字典服务器),强调其“键值对”存储结构。
定位:填补传统关系型数据库(如 MySQL)在高并发、实时数据处理上的不足,专注高性能和灵活性。


二、Redis 是什么?

核心特点
内存存储:数据主要存储在内存中,读写速度极快(微秒级响应)。
单线程架构:通过单线程处理请求,避免多线程竞争,保证原子性。
持久化支持:可将内存数据保存到磁盘(RDB快照/AOF日志)。
分布式扩展:支持主从复制、哨兵(Sentinel)、集群(Cluster)等高可用方案。


三、Redis 的典型应用场景
  1. 缓存加速
    用途:缓存热点数据(如用户信息、商品详情),减轻数据库压力。
    优势:降低延迟,提升系统吞吐量。
    案例:社交媒体平台缓存用户动态、热门帖子。

  2. 实时排行榜
    实现:使用 Sorted Set 结构,根据分数自动排序。
    案例:游戏积分榜、电商销量排行、热搜榜单。

  3. 分布式锁
    实现:通过 SET key value NX EX 实现互斥锁。
    场景:秒杀活动、资源争抢、分布式任务调度。

  4. 消息队列
    实现:使用 List 结构的 LPUSH/BRPOP 命令实现简单队列。
    扩展:结合 Stream 类型支持多消费者、消息回溯(Redis 5.0+)。

  5. 会话管理(Session Storage)
    用途:存储用户登录状态,支持分布式系统共享会话。
    场景:微服务架构中跨服务的用户身份验证。

  6. 实时数据分析
    实现:通过 HyperLogLog 统计独立访客,Bitmap 记录用户行为。
    案例:统计日活用户(UV)、实时在线人数。


四、Redis 的核心数据结构
数据结构特点与操作示例
String存储文本、数字或二进制数据。
SET count 100INCR 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 的高可用与扩展
  1. 主从复制(Replication)
    用途:数据备份与读写分离。
    配置:主节点写,从节点读,支持级联复制。

  2. 哨兵模式(Sentinel)
    功能:监控主节点,自动故障转移(Failover)。
    场景:保证高可用性(HA),避免单点故障。

  3. 集群模式(Cluster)
    原理:数据分片(16384个哈希槽),支持横向扩展。
    优势:突破单机内存限制,支持多节点并发读写。


六、学习资源推荐
  1. 官方文档:https://redis.io/docs
  2. 中文文档:https://redis.io/docs

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 万次请求,测试 SETGET 命令的性能。
输出示例

  === 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️⃣ 核心参数说明
参数说明示例
-hRedis 服务器地址-h 192.168.1.100
-pRedis 端口-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
类似 EXPIRETTL,但时间单位为毫秒。


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 keyRESTORE key ttl serialized-value
序列化键的值(DUMP)并恢复(RESTORE)。

UNLINK key [key ...]
异步删除键(类似 DEL,但非阻塞,适合删除大键)。


注意事项

  1. KEYS * 慎用:会遍历所有键,可能阻塞 Redis 服务(生产环境建议用 SCAN)。
  2. 键的命名:建议用 : 分隔层级(如 user:1001:profile)。
  3. 过期时间精度: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
设置二进制位的值(01)。

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. 使用场景

  1. 缓存:存储热点数据(如 HTML 片段、用户信息)。
  2. 计数器:文章阅读量、用户点赞数(利用 INCR 原子性)。
  3. 会话管理:存储临时 Token 或 Session ID(配合 SETEX)。
  4. 位图统计:记录用户活跃状态、签到打卡。

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. 使用场景

  1. 消息队列
    • 生产者用 LPUSH 插入任务,消费者用 BRPOP 阻塞获取任务。
    • 示例:

    # 生产者
    LPUSH queue "msg1"
    # 消费者
    BRPOP queue 30
    
  2. 最新消息列表
    • 用 LPUSH 插入最新消息,LRANGE 0 9 获取前10条最新消息。

  3. 分页查询
    • 结合 LRANGE 实现分页(如 LRANGE list 0 9 获取第一页)。

  4. 循环存储
    • 用 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. 使用场景

  1. 唯一性存储
    • 存储用户 ID、IP 地址等需去重的数据。

    SADD unique_ips "192.168.1.1"  # 自动过滤重复 IP
    
  2. 标签系统
    • 为对象(如文章)打标签,并计算共同标签。

    # 文章 1001 的标签
    SADD article:1001:tags "tech" "database"
    # 文章 1002 的标签
    SADD article:1002:tags "tech" "cloud"
    # 查找两篇文章的共同标签
    SINTER article:1001:tags article:1002:tags  # 返回 ["tech"]
    
  3. 社交关系
    • 存储用户的好友/关注列表,计算共同好友。

    SADD user:1001:friends "2001" "2002"
    SADD user:2001:friends "1001" "3003"
    SINTER user:1001:friends user:2001:friends  # 返回 ["2001"]
    
  4. 随机抽奖
    • 使用 SPOPSRANDMEMBER 实现随机选取。

    # 参与抽奖用户
    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. 使用场景

  1. 对象存储
    • 存储用户、商品等结构化数据,支持部分更新。

    HSET product:5001 title "Laptop" price 999.99 stock 10
    
  2. 计数器组
    • 统计多维数据(如用户点赞数、访问次数)。

    HINCRBY user:1001 likes 1    # 点赞数 +1
    HINCRBY user:1001 views 1    # 浏览数 +1
    
  3. 配置管理
    • 集中管理应用配置项,支持动态修改。

    HSET app_config cache_enabled 1 max_connections 100
    
  4. 购物车
    • 字段为商品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. 使用场景

  1. 实时排行榜
    • 用分数表示用户积分,按排名获取 Top N。

    ZADD leaderboard 300 "Alice" 250 "Bob" 200 "Charlie"
    ZREVRANGE leaderboard 0 2 WITHSCORES  # 前三名
    
  2. 延时队列
    • 将任务到期时间戳作为分数,定时轮询到期任务。

    ZADD tasks 1672531200 "task1"  # 2023-01-01 00:00:00 执行
    ZRANGEBYSCORE tasks 0 1672531200  # 获取所有到期任务
    
  3. 范围筛选
    • 按价格区间筛选商品,或按时间范围查询日志。

    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. 注意事项

  1. 经纬度范围
    • 经度范围:-180 到 180,纬度范围:-85.05112878 到 85.05112878(超出会报错)。

  2. 数据存储本质
    • 底层使用 Zset 存储,可通过 ZRANGEZREM 等命令操作成员,但分数为 GEOHASH 值(非直观经纬度)。

  3. 精度与性能
    • GEOHASH 精度越高,搜索越精确,但计算量增大。需根据场景平衡精度与性能。

  4. 地球模型假设
    • 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. 使用场景

  1. 网站 UV 统计
    • 统计每日独立访客(无需精确值,内存敏感场景)。

    # 用户访问时添加
    PFADD 2023-10-01:uv "192.168.1.1" "10.0.0.2"
    # 当日 UV 查询
    PFCOUNT 2023-10-01:uv
    
  2. 多维度聚合统计
    • 合并多日/多来源数据,估算总 UV。

    PFMERGE total_uv oct_uv nov_uv
    PFCOUNT total_uv
    
  3. 大规模去重场景
    • 日志去重、广告点击用户数统计(内存有限时替代 Set)。


4. 注意事项

  1. 误差率
    • 标准误差约 0.81%,实际误差可能随数据分布波动。
    • 示例:若真实 UV 为 100 万,估算结果在 991,900 ~ 1,008,100 之间。

  2. 不可逆性
    • 无法获取原始数据或判断某个元素是否存在(如 user1 是否访问过)。

  3. 内存固定
    • 无论存储 1 个还是 1 亿个元素,均占用 12KB(除非启用稀疏编码优化)。

  4. 不支持删除操作
    • 无法删除已添加的元素(需重新统计或使用新键)。


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)可表示 01
高效存储:适合存储大量二元状态(是/否、存在/不存在),极大节省内存。
位运算支持:支持按位操作(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. 特殊类型对比

类型底层实现核心特点适用场景注意事项
BitmapString 二进制位节省内存,支持位运算签到、在线状态、布隆过滤器稀疏数据浪费内存
HyperLogLog固定 12KB 结构超低内存基数估算,误差约 0.81%大规模 UV 统计无法获取具体数据,只能估算
GeospatialZset(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 事务通过 MULTIEXECDISCARDWATCH 实现,保证命令的批量原子执行。

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. 注意事项

  1. 无回滚机制
    Redis 事务不支持回滚(Rollback),需自行处理部分失败场景。

  2. 性能影响
    长时间事务会阻塞其他客户端,建议拆分大事务。

  3. 竞态条件
    使用 WATCH 实现乐观锁,避免数据竞争(需配合重试逻辑)。

  4. 替代方案
    复杂逻辑可使用 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. 注意事项

  1. 重试次数限制
    必须设置最大重试次数(如 max_retries=3),避免无限循环。

  2. 键的监视范围
    WATCH 的键不宜过多,否则增加冲突概率和性能开销。

  3. 性能优化
    • 将 WATCH 的键控制在最小范围(如只监视关键资源)。
    • 在业务允许的情况下,缩短事务内的操作时间。

  4. 替代方案: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
    
  5. 版本号模式
    使用版本号字段(类似 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. 注意事项

  1. 资源释放
    使用 try-with-resources 或手动关闭 JedisJedisPool,避免连接泄漏。

  2. 避免长连接
    操作完成后及时释放连接,防止连接池耗尽。

  3. 异常处理
    捕获 JedisConnectionException 等异常,处理网络或 Redis 服务中断问题。

  4. 序列化
    复杂对象需序列化为字符串存储(如 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.ymlapplication.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 自动配置了 RedisTemplateStringRedisTemplate,直接在代码中注入即可:

@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 分布式锁

使用 RedissonRedisTemplate 实现:

// 基于 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"


http://www.ppmy.cn/ops/166673.html

相关文章

基于Python的PDF转PNG可视化工具开发

基于Python的PDF转PNG可视化工具开发 一、引言 在数字文档处理领域&#xff0c;PDF到图像格式的转换是常见需求。本文介绍如何利用Python的PyMuPDF库和Tkinter框架&#xff0c;开发一个带图形界面的PDF转PNG工具。该工具支持页面选择、分辨率调整等功能&#xff0c;并具有友好…

matlab 三维桥式起重机系统数学模型

1、内容简介 matlab161-三维桥式起重机系统数学模型 可以交流、咨询、答疑 2、内容说明 略 2.3.1 三维桥式起重机系统数学模型 假设&#xff1a;&#xff08;1&#xff09;钢丝绳长度变化忽略不计&#xff0c;且不考虑其柔性&#xff1b;&#xff08;2&#xff09;假设台车和…

C#核心笔记——(五)框架概述

.NET Ftamework中几乎所有功能都是通过大量的托管类型提供的。这些类型组织在层次化的命名空间中&#xff0c;并打包为一套程序集&#xff0c;与CLR一起构成了.NET平台。 有些.NET类型是由CLR直接使用的&#xff0c;且对于托管宿主环境而言是必不可少的。这些类型位于一个名为…

从零开始探索C++游戏开发:性能、控制与无限可能

一、为何选择C开发游戏&#xff1f; 在虚幻引擎5渲染的次世代画面背后&#xff0c;在《巫师3》的庞大开放世界中&#xff0c;在《毁灭战士》的丝滑60帧战斗里&#xff0c;C始终扮演着核心技术角色。这门诞生于1983年的语言&#xff0c;至今仍占据着游戏引擎开发语言使用率榜首…

WireShark自动抓包

背景 异常流量检测是当前保护网络空间安全的重要检测方法。 对流量的研究&#xff0c;首先需要在系统中进行抓包&#xff0c;并对包进行分析。 这里对WireShark自动抓包进行简要介绍。 操作步骤 1、选择“捕获”>“选项”。 2、在Input下&#xff0c;选择要抓包的网络接…

ESP32芯片模组方案,设备物联网无线通信,WiFi蓝牙交互控制应用

在当下&#xff0c;物联网正以前所未有的速度席卷全球&#xff0c;从繁华都市的智能建筑&#xff0c;到宁静乡村的智慧农业&#xff0c;从人们日常使用的可穿戴设备&#xff0c;到工业领域复杂精密的自动化生产线&#xff0c;物联网的触角已深入到生活与生产的每一个角落。 而…

FX-std::list

std::list 是 C 标准库中的一个双向链表容器&#xff0c;定义在 <list> 头文件中。它支持在任意位置高效地插入和删除元素&#xff0c;但不支持随机访问。以下是 std::list 的基本用法和一些常见操作&#xff1a; 1. 包含头文件 #include <list> 2. 定义和初始化…

技术栈分享之----Swagger

一&#xff1a;swagger介绍 相信无论是前端还是后端开发&#xff0c;都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力&#xff0c;经常来不及更新。其实无论是前端调用后端&#xff0c;还是后端调用…