第四章:Redis进阶特性
4.1 事务机制
事务特点
- 保证原子性:事务中的命令要么全部执行,要么全部不执行
- 不支持回滚:Redis事务不支持回滚操作
- 隔离性:事务执行过程中不会被其他命令打断
基本命令
# 开启事务
MULTI
# 执行命令
SET key1 value1
SET key2 value2
# 提交事务
EXEC
# 取消事务
DISCARD
# 监视键
WATCH key
# 取消监视
UNWATCH
事务示例
1. 简单事务
# 开启事务
MULTI
OK
# 执行命令
SET key1 value1
QUEUED
SET key2 value2
QUEUED
# 提交事务
EXEC
1) OK
2) OK
2. 事务取消
# 开启事务
MULTI
OK
# 执行命令
SET key1 value1
QUEUED
SET key2 value2
QUEUED
# 取消事务
DISCARD
OK
3. WATCH机制
# 监视键
WATCH balance:1001
OK
# 开启事务
MULTI
OK
# 执行命令
DECRBY balance:1001 100
INCRBY balance:1002 100
QUEUED
QUEUED
# 提交事务(如果balance:1001未被修改)
EXEC
1) (integer) 900
2) (integer) 1100
# 如果balance:1001被其他客户端修改,事务会失败
EXEC
(nil)
事务应用场景
1. 转账
# 开启事务
MULTI
# 扣款
DECRBY balance:1001 100
# 收款
INCRBY balance:1002 100
# 提交事务
EXEC
2. 库存扣减
# 监视库存
WATCH product:1001:stock
# 检查库存
GET product:1001:stock
# 开启事务
MULTI
# 扣减库存
DECRBY product:1001:stock 1
# 提交事务
EXEC
4.2 发布订阅
发布订阅特点
- 消息广播
- 多个订阅者可以订阅同一个频道
- 发布者不需要知道订阅者
基本命令
# 订阅频道
SUBSCRIBE channel1 channel2 ...
# 取消订阅
UNSUBSCRIBE [channel1 channel2 ...]
# 发布消息
PUBLISH channel message
# 按模式订阅
PSUBSCRIBE pattern1 pattern2 ...
# 按模式取消订阅
PUNSUBSCRIBE [pattern1 pattern2 ...]
发布订阅示例
1. 简单订阅
# 订阅者1
SUBSCRIBE channel:news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:news"
3) (integer) 1
# 订阅者2
SUBSCRIBE channel:news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:news"
3) (integer) 1
# 发布者
PUBLISH channel:news "今日头条:Redis 7.0发布"
(integer) 2
# 订阅者1和订阅者2都会收到消息
1) "message"
2) "channel:news"
3) "今日头条:Redis 7.0发布"
2. 模式订阅
# 订阅者
PSUBSCRIBE channel:*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "channel:*"
3) (integer) 1
# 发布者
PUBLISH channel:news "新闻"
PUBLISH channel:sports "体育"
# 订阅者会收到所有匹配的消息
1) "pmessage"
2) "channel:*"
3) "channel:news"
4) "新闻"
1) "pmessage"
2) "channel:*"
3) "channel:sports"
4) "体育"
发布订阅应用场景
1. 消息通知
# 订阅用户通知
SUBSCRIBE user:1001:notifications
# 发送通知
PUBLISH user:1001:notifications "您有新消息"
PUBLISH user:1001:notifications "您的订单已发货"
2. 系统广播
# 订阅系统消息
SUBSCRIBE system:announcements
# 发送系统公告
PUBLISH system:announcements "系统将于今晚22:00进行维护"
3. 实时聊天
# 订阅聊天室
SUBSCRIBE chat:room:1001
# 发送消息
PUBLISH chat:room:1001 "user1:大家好"
4.3 Lua脚本
Lua脚本特点
- 原子性执行
- 支持复杂逻辑
- 减少网络开销
- 复用脚本
基本命令
# 执行Lua脚本
EVAL script numkeys key1 key2 ... arg1 arg2 ...
# 计算脚本SHA1
SCRIPT LOAD script
# 执行缓存的脚本
EVALSHA sha1 numkeys key1 key2 ... arg1 arg2 ...
# 杀死正在执行的脚本
SCRIPT KILL
# 清空脚本缓存
SCRIPT FLUSH
# 查看脚本缓存
SCRIPT EXISTS sha1 sha2 ...
Lua脚本示例
1. 简单脚本
# 执行简单脚本
EVAL "return 'Hello Redis'" 0
"Hello Redis"
# 使用KEYS和ARGV
EVAL "return KEYS[1] .. ' ' .. ARGV[1]" 1 key1 value1
"key1 value1"
2. 复杂脚本
# 批量设置键值
EVAL "
for i=1,#KEYS do
redis.call('SET', KEYS[i], ARGV[i])
end
return #KEYS
" 3 key1 key2 key3 value1 value2 value3
(integer) 3
3. 条件操作
# 仅当key不存在时设置值
EVAL "
if redis.call('EXISTS', KEYS[1]) == 0 then
return redis.call('SET', KEYS[1], ARGV[1])
else
return 0
end
" 1 key1 value1
Lua脚本应用场景
1. 原子性操作
— 原子性转账
local from = KEYS[1]
local to = KEYS[2]
local amount = tonumber(ARGV[1])
local balance = tonumber(redis.call('GET', from))
if balance < amount then
return 0
end
redis.call('DECRBY', from, amount)
redis.call('INCRBY', to, amount)
return 1
2. 限流
— 限流脚本
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire = tonumber(ARGV[2])
local current = tonumber(redis.call('GET', key) or '0')
if current + 1 > limit then
return 0
else
redis.call('INCR', key)
if current == 0 then
redis.call('EXPIRE', key, expire)
end
return 1
end
3. 分布式锁
— 获取锁
local key = KEYS[1]
local value = ARGV[1]
local expire = tonumber(ARGV[2])
if redis.call('SETNX', key, value) == 1 then
redis.call('EXPIRE', key, expire)
return 1
else
return 0
end
— 释放锁
local key = KEYS[1]
local value = ARGV[1]
if redis.call('GET', key) == value then
return redis.call('DEL', key)
else
return 0
end
4.4 管道技术
管道技术特点
- 批量执行命令
- 减少网络往返
- 提高性能
- 不保证原子性
管道使用示例
1. 命令行使用
# 使用管道执行多个命令
(ECHO "SET key1 value1"; ECHO "SET key2 value2"; ECHO "GET key1") | redis-cli –pipe
2. Python示例
import redis
# 创建连接
r = redis.Redis(host='localhost', port=6379)
# 使用管道
pipe = r.pipeline()
# 批量执行命令
pipe.set('key1', 'value1')
pipe.set('key2', 'value2')
pipe.get('key1')
# 执行所有命令
results = pipe.execute()
print(results) # [True, True, b'value1']
3. 事务管道
import redis
# 创建连接
r = redis.Redis(host='localhost', port=6379)
# 使用事务管道
pipe = r.pipeline(transaction=True)
# 批量执行命令
pipe.set('key1', 'value1')
pipe.set('key2', 'value2')
pipe.get('key1')
# 执行所有命令
results = pipe.execute()
print(results) # [True, True, b'value1']
管道应用场景
1. 批量插入
import redis
r = redis.Redis(host='localhost', port=6379)
# 批量插入数据
pipe = r.pipeline()
for i in range(1000):
pipe.set(f'key:{i}', f'value:{i}')
pipe.execute()
2. 批量查询
import redis
r = redis.Redis(host='localhost', port=6379)
# 批量查询数据
pipe = r.pipeline()
for i in range(1000):
pipe.get(f'key:{i}')
results = pipe.execute()
4.5 慢查询日志
慢查询配置
# 慢查询阈值(微秒)
slowlog-log-slower-than 10000
# 慢查询日志最大长度
slowlog-max-len 128
慢查询命令
# 获取慢查询日志
SLOWLOG GET [n]
# 获取慢查询日志长度
SLOWLOG LEN
# 清空慢查询日志
SLOWLOG RESET
慢查询示例
# 查看慢查询
SLOWLOG GET 10
1) 1) (integer) 1
2) (integer) 15000
3) (integer) 1600000000
4) "GET user:1001"
# 解释:
# 1) 日志ID
# 2) 执行时间(微秒)
# 3) 时间戳
# 4) 执行的命令
慢查询分析
# 查看慢查询日志长度
SLOWLOG LEN
(integer) 10
# 清空慢查询日志
SLOWLOG RESET
OK
4.6 Redis模块
常用Redis模块
1. RedisJSON
# 安装RedisJSON
# https://oss.redislabs.com/redisjson/
# 设置JSON值
JSON.SET user:1001 $ '{"name":"张三","age":25}'
# 获取JSON值
JSON.GET user:1001
# 获取JSON字段
JSON.GET user:1001 $.name
# 更新JSON字段
JSON.SET user:1001 $.age 26
2. RedisSearch
# 安装RedisSearch
# https://oss.redislabs.com/redisearch/
# 创建索引
FT.CREATE user:idx ON JSON PREFIX 1 user: SCHEMA $.name AS name TEXT $.age AS age NUMERIC
# 搜索
FT.SEARCH user:idx "@name:(张三)"
# 聚合
FT.AGGREGATE user:idx "*" GROUPBY 0 REDUCE COUNT 0 AS total
3. RedisTimeSeries
# 安装RedisTimeSeries
# https://oss.redislabs.com/redistimeseries/
# 创建时间序列
TS.CREATE temperature:room1 RETENTION 0 LABELS room room1
# 添加数据点
TS.ADD temperature:room1 1600000000 25
# 查询数据
TS.RANGE temperature:room1 – + WITHLABELS
# 聚合查询
TS.MRANGE – + WITHLABELS AGGREGATION avg 60000 FILTER room=room1



