Redis 7.x 新特性详解
Redis 7.0 于 2022 年发布,带来了众多重要更新。本文将深入解析 Redis 7.x 的新特性,帮助你了解技术演进方向。
一、版本演进
1.1 Redis 版本历史
Redis 版本演进:
Redis 2.6 (2012) - Lua 脚本、过期时间改进
Redis 3.0 (2015) - Cluster 集群、哨兵改进
Redis 4.0 (2017) - 混合持久化、异步删除
Redis 5.0 (2018) - Stream 数据类型、事务改进
Redis 6.0 (2020) - TLS 加密、ACL 权限、多线程 IO
Redis 7.0 (2022) - Functions、ACL 改进、性能优化
Redis 8.0 (计划) - 向量搜索、AI 功能
1.2 Redis 7.0 核心特性
Redis 7.0 新特性总览:
┌─────────────────────────────────────┐
│ 1. Redis Functions │
│ - 替代 Lua 脚本 │
│ - 更好的可维护性 │
│ - 支持共享库 │
├─────────────────────────────────────┤
│ 2. ACL 权限改进 │
│ - 更细粒度的权限控制 │
│ - 支持通道权限 │
├─────────────────────────────────────┤
│ 3. 性能优化 │
│ - 命令执行效率提升 │
│ - 内存使用优化 │
├─────────────────────────────────────┤
│ 4. 新命令 │
│ - LMOVE, BLMOVE │
│ - ZMPOP, BZMPOP │
│ - EXPIRETIME │
├─────────────────────────────────────┤
│ 5. 可观测性增强 │
│ - 更好的监控指标 │
│ - 慢查询改进 │
└─────────────────────────────────────┘
二、Redis Functions
2.1 什么是 Functions
Functions 是 Redis 7.0 引入的新特性:
- 替代 Lua 脚本
- 支持代码共享
- 更好的版本管理
- 更强的可维护性
Functions vs Lua:
┌─────────────────────────────────────┐
│ 特性 │ Lua │ Functions │
├─────────────────────────────────────┤
│ 代码共享 │ 不支持 │ 支持 │
│ 版本管理 │ 无 │ 有 │
│ 调试 │ 困难 │ 容易 │
│ 内存隔离 │ 无 │ 有 │
│ 超时控制 │ 有限 │ 完善 │
└─────────────────────────────────────┘
2.2 Function 基础使用
-- 定义 Function 库
-- library.lua
#!lua name=mylib
-- 函数 1:字符串处理
redis.register_function('str_upper', function(keys, args)
local key = keys[1]
local value = redis.call('GET', key)
if value then
return string.upper(value)
end
return nil
end)
-- 函数 2:计数器(带最大值限制)
redis.register_function('incr_with_max', function(keys, args)
local key = keys[1]
local max = tonumber(args[1])
local current = redis.call('GET', key)
if current and tonumber(current) >= max then
return nil
end
return redis.call('INCR', key)
end)
-- 函数 3:批量操作
redis.register_function('mget_json', function(keys, args)
local result = {}
for i, key in ipairs(keys) do
result[i] = redis.call('GET', key)
end
return cjson.encode(result)
end)
加载 Function 库:
# 加载 Function 库
redis-cli -x FUNCTION LOAD < library.lua
# 查看已加载的库
redis-cli FUNCTION LIST
# 查看库的详细信息
redis-cli FUNCTION LIST WITHCODE
# 删除库
redis-cli FUNCTION DELETE mylib
# 更新库(重新加载)
redis-cli FUNCTION LOAD REPLACE < library.lua
调用 Function:
# 调用函数
redis-cli FCALL mylib.str_upper 1 user:1001:name
# 带参数调用
redis-cli FCALL mylib.incr_with_max 1 counter:views 1000
# 批量调用
redis-cli FCALL mylib.mget_json 3 key1 key2 key3
2.3 Java 使用 Functions
import redis.clients.jedis.Jedis;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class RedisFunctionsExample {
private Jedis jedis;
public RedisFunctionsExample(Jedis jedis) {
this.jedis = jedis;
}
/**
* 加载 Function 库
*/
public void loadLibrary(String libraryPath) throws IOException {
String code = new String(Files.readAllBytes(Paths.get(libraryPath)));
jedis.functionLoad(code, true); // true 表示替换已存在的库
}
/**
* 调用 Function
*/
public Object fcall(String functionName, java.util.List<String> keys,
java.util.List<String> args) {
return jedis.fcall(functionName, keys, args);
}
/**
* 示例:字符串处理
*/
public String strUpper(String key) {
Object result = fcall("mylib.str_upper",
java.util.Collections.singletonList(key),
java.util.Collections.emptyList());
return (String) result;
}
/**
* 示例:带限制的计数器
*/
public Long incrWithMax(String key, long max) {
Object result = fcall("mylib.incr_with_max",
java.util.Collections.singletonList(key),
java.util.Collections.singletonList(String.valueOf(max)));
return (Long) result;
}
/**
* 示例:批量获取 JSON
*/
public String mgetJson(java.util.List<String> keys) {
Object result = fcall("mylib.mget_json",
keys,
java.util.Collections.emptyList());
return (String) result;
}
/**
* 删除库
*/
public void deleteLibrary(String libraryName) {
jedis.functionDelete(libraryName);
}
/**
* 列出所有库
*/
public void listLibraries() {
jedis.functionList().forEach(lib -> {
System.out.println("库名:" + lib.getLibraryName());
System.out.println("引擎:" + lib.getEngine());
System.out.println("函数数量:" + lib.getFunctions().size());
});
}
}
2.4 Function 最佳实践
#!lua name=utils
-- 最佳实践 1:错误处理
redis.register_function('safe_get', function(keys, args)
local key = keys[1]
local ok, result = pcall(function()
return redis.call('GET', key)
end)
if not ok then
redis.log(redis.LOG_WARNING, "Error getting key: " .. key)
return nil
end
return result
end)
-- 最佳实践 2:参数验证
redis.register_function('safe_set', function(keys, args)
local key = keys[1]
local value = args[1]
local ttl = args[2]
-- 验证参数
if not value or value == "" then
error("Value cannot be empty")
end
if ttl and (tonumber(ttl) <= 0 or tonumber(ttl) > 86400) then
error("TTL must be between 1 and 86400 seconds")
end
redis.call('SET', key, value)
if ttl then
redis.call('EXPIRE', key, tonumber(ttl))
end
return "OK"
end)
-- 最佳实践 3:使用 KEYS 和 ARGS
-- 避免硬编码 key,提高可复用性
redis.register_function('user_profile_get', function(keys, args)
local user_key = keys[1] -- 传入 user:{id}:profile
local field = args[1]
return redis.call('HGET', user_key, field)
end)
三、ACL 权限改进
3.1 ACL 基础
Redis 6.0 引入 ACL,7.0 增强:
# 创建用户
ACL SETUSER username on >password ~pattern +command
# 示例:创建只读用户
ACL SETUSER readonly on >readpass ~* +@read
# 示例:创建特定 key 权限用户
ACL SETUSER appuser on >apppass ~app:* +@write +@read
# 示例:创建管理员用户
ACL SETUSER admin on >adminpass ~* +@all
3.2 通道权限(Redis 7.0 新增)
# Pub/Sub 通道权限
ACL SETUSER pubsubuser on >pubpass &channel:* +PUBLISH +SUBSCRIBE
# 限制只能访问特定通道
ACL SETUSER newsuser on >newspass &news:* +PUBLISH +SUBSCRIBE
# 测试通道权限
redis-cli -a newspass SUBSCRIBE news:sports
# 如果尝试订阅其他通道,会被拒绝
3.3 ACL 配置文件
# redis.conf ACL 配置
# 默认用户(保持兼容性)
user default on nopass ~* +@all
# 只读用户
user readonly on >readonlypass ~* +@read -@dangerous
# 应用用户
user appuser on >apppass ~app:* +@write +@read +INCR +DECR
# 监控用户(只能执行 INFO 命令)
user monitoruser on >monitorpass ~* +INFO +PING +CLIENT
# 备份用户
user backupuser on >backuppass ~* +BGSAVE +LASTSAVE +INFO
# 保存 ACL 到文件
acl-save
# 从文件加载 ACL
acl-load
3.4 Java 使用 ACL
import redis.clients.jedis.Jedis;
import redis.clients.jedis.auth.Protocol;
public class ACLExample {
/**
* 创建用户
*/
public void createUser(Jedis jedis, String username, String password) {
// 创建只读用户
jedis.aclSetUser(username,
"on",
">" + password,
"~*",
"+@read",
"-@dangerous"
);
}
/**
* 使用 ACL 用户连接
*/
public Jedis connectWithACL(String host, int port,
String username, String password) {
// Redis 6.0+ 支持用户名密码
return new Jedis(host, port,
Protocol.DEFAULT_TIMEOUT,
username, // 用户名
password // 密码
);
}
/**
* 检查用户权限
*/
public void checkPermissions(Jedis jedis, String username) {
// 获取用户信息
var userInfo = jedis.aclGetUser(username);
System.out.println("用户:" + userInfo.get(0));
System.out.println("标志:" + userInfo.get(1));
System.out.println("密码:" + userInfo.get(2));
System.out.println("命令:" + userInfo.get(3));
System.out.println("Keys:" + userInfo.get(4));
System.out.println("通道:" + userInfo.get(5));
}
/**
* 删除用户
*/
public void deleteUser(Jedis jedis, String username) {
jedis.aclDelUser(username);
}
/**
* 列出所有用户
*/
public void listUsers(Jedis jedis) {
jedis.aclList().forEach(user -> System.out.println(user));
}
}
四、性能优化
4.1 命令执行优化
Redis 7.0 性能改进:
1. 命令执行效率提升
- 减少锁竞争
- 优化内存分配
- 改进数据结构
2. 集群性能提升
- 减少网络往返
- 优化槽位计算
- 改进故障检测
3. 持久化优化
- RDB 压缩改进
- AOF 重写优化
- 减少 fork 阻塞
性能对比:
# Redis 6.0 vs Redis 7.0
# SET 性能
# Redis 6.0: ~100,000 ops/sec
# Redis 7.0: ~120,000 ops/sec (+20%)
# GET 性能
# Redis 6.0: ~150,000 ops/sec
# Redis 7.0: ~180,000 ops/sec (+20%)
# 集群性能
# Redis 6.0: ~80,000 ops/sec
# Redis 7.0: ~100,000 ops/sec (+25%)
4.2 内存优化
Redis 7.0 内存优化:
1. 主动内存碎片整理
- 自动整理内存碎片
- 减少内存浪费
2. 改进的淘汰算法
- LFU 算法优化
- 更智能的淘汰策略
3. 压缩优化
- List 压缩改进
- Hash 压缩优化
配置示例:
# 主动内存碎片整理
activedefrag yes
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100
active-defrag-cycle-min 1
active-defrag-cycle-max 25
# 内存淘汰策略
maxmemory-policy noeviction
maxmemory-samples 10
五、新命令
5.1 List 命令
# LMOVE - 从列表一端移动元素到另一端
LMOVE source destination LEFT RIGHT
# BLMOVE - 阻塞式 LMOVE
BLMOVE source destination LEFT RIGHT 5.0
# 示例:任务队列
LPUSH queue:pending task1 task2 task3
LMOVE queue:pending queue:processing LEFT RIGHT
5.2 ZSet 命令
# ZMPOP - 从多个有序集合中弹出元素
ZMPOP 2 zset1 zset2 MIN COUNT 1
# BZMPOP - 阻塞式 ZMPOP
BZMPOP 5.0 2 zset1 zset2 MIN COUNT 1
# 示例:多队列消费
BZMPOP 0 3 queue:high queue:medium queue:low MIN COUNT 1
5.3 Key 命令
# EXPIRETIME - 获取 key 的过期时间(绝对时间戳)
EXPIRETIME key
# 返回:过期时间戳(秒)
# -1: key 存在但无过期时间
# -2: key 不存在
# PEXPIRETIME - 毫秒级过期时间
PEXPIRETIME key
# 示例
SET key value EX 3600
EXPIRETIME key
# 返回:1725530400(过期时间戳)
六、可观测性增强
6.1 监控指标改进
# INFO 命令增强
INFO everything
# 新增指标
INFO modules # 模块信息
INFO memory # 详细内存信息
INFO clients # 客户端详细信息
6.2 慢查询改进
# 慢查询配置
slowlog-log-slower-than 10000 # 10ms
slowlog-max-len 128
# 新增:慢查询包含更多信息
SLOWLOG GET 10
# 输出包含:
# - 命令执行时间
# - 客户端信息
# - 执行时间戳
6.3 延迟监控
# 延迟监控
redis-cli --latency
# 延迟历史
redis-cli --latency-history -i 1
# 延迟分布
redis-cli --latency-dist
七、升级建议
7.1 升级步骤
# 1. 备份数据
redis-cli BGSAVE
cp /var/lib/redis/dump.rdb /backup/dump.rdb
# 2. 停止 Redis
redis-cli shutdown
# 3. 备份配置文件
cp /etc/redis/redis.conf /etc/redis/redis.conf.bak
# 4. 安装 Redis 7.0
tar -xzf redis-7.0.0.tar.gz
cd redis-7.0.0
make && make install
# 5. 更新配置(如有必要)
# 检查不兼容的配置项
# 6. 启动 Redis 7.0
redis-server /etc/redis/redis.conf
# 7. 验证启动
redis-cli ping
redis-cli info server
# 8. 验证数据
redis-cli dbsize
7.2 兼容性检查
# 检查不兼容的命令
redis-cli ACL CAT
# 检查 Lua 脚本兼容性
# Redis 7.0 的 Functions 与 Lua 脚本可以共存
# 建议逐步迁移到 Functions
# 检查客户端兼容性
# 确保 Jedis/Lettuce 等客户端支持 Redis 7.0
八、总结
8.1 Redis 7.0 核心优势
-
Functions
- 更好的代码组织
- 支持共享库
- 易于维护
-
ACL 增强
- 更细粒度权限
- 通道权限控制
- 安全性提升
-
性能优化
- 命令执行更快
- 内存使用更优
- 集群性能提升
-
可观测性
- 监控指标更丰富
- 调试更便捷
8.2 升级建议
- 生产环境先测试验证
- 逐步迁移 Lua 到 Functions
- 完善 ACL 权限配置
- 监控性能指标变化
参考资料