Redis - 高性能内存数据库
1. 什么是Redis?
Redis(Remote Dictionary Server)是一个开源的、高性能的键值对存储数据库,它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等。Redis的主要特点是将数据存储在内存中,因此具有极高的读写速度,同时也支持数据持久化到磁盘,确保数据安全。
1.1 核心特性
- 高性能:内存存储,读写速度极快
- 支持多种数据结构:字符串、哈希、列表、集合、有序集合、位图、超日志等
- 数据持久化:支持RDB和AOF两种持久化方式
- 主从复制:支持数据复制,实现高可用性
- 集群:支持分布式集群,实现水平扩展
- 发布/订阅:支持消息传递模式
- 事务:支持原子性操作
- Lua脚本:支持自定义脚本执行
2. 安装与配置
2.1 安装Redis
Linux系统
# Ubuntu/Debian
apt update
apt install redis-server
# CentOS/RHEL
yum install epel-release
yum install redis
# 启动Redis服务
systemctl start redis
systemctl enable redisWindows系统
- 从 GitHub 下载Redis Windows版本
- 解压到指定目录
- 运行
redis-server.exe启动服务 - 运行
redis-cli.exe启动客户端
macOS系统
# 使用Homebrew
brew install redis
# 启动Redis服务
brew services start redis2.2 配置Redis
Redis的配置文件通常位于 /etc/redis/redis.conf(Linux)或Redis安装目录下(Windows)。
# 基本配置
port 6379 # 端口号
bind 127.0.0.1 # 绑定地址,0.0.0.0表示所有地址
requirepass yourpassword # 密码认证
# 持久化配置
save 900 1 # 900秒内有1个修改就持久化
save 300 10 # 300秒内有10个修改就持久化
save 60 10000 # 60秒内有10000个修改就持久化
appendonly yes # 开启AOF持久化
# 内存配置
maxmemory 2gb # 最大内存限制
maxmemory-policy allkeys-lru # 内存满时的淘汰策略2.3 连接Redis
# 本地连接
redis-cli
# 远程连接
redis-cli -h host -p port -a password
# 测试连接
redis-cli ping
# 响应: PONG3. 基本数据结构
3.1 字符串(String)
字符串是Redis最基本的数据类型,可存储任何类型的字符串,包括二进制数据。
# 设置值
SET key value
# 获取值
GET key
# 设置过期时间(秒)
SETEX key seconds value
# 设置过期时间(毫秒)
PSETEX key milliseconds value
# 自增
INCR key
# 自增指定值
INCRBY key increment
# 自减
DECR key
# 自减指定值
DECRBY key decrement
# 追加字符串
APPEND key value
# 获取字符串长度
STRLEN key3.2 哈希(Hash)
哈希适用于存储对象,如用户信息、商品信息等。
# 设置哈希字段
HSET key field value
# 获取哈希字段
HGET key field
# 设置多个哈希字段
HMSET key field1 value1 field2 value2
# 获取多个哈希字段
HMGET key field1 field2
# 获取所有哈希字段和值
HGETALL key
# 获取所有哈希字段
HKEYS key
# 获取所有哈希值
HVALS key
# 获取哈希字段数量
HLEN key
# 检查字段是否存在
HEXISTS key field
# 删除哈希字段
HDEL key field
# 哈希字段自增
HINCRBY key field increment3.3 列表(List)
列表是有序的字符串集合,可在两端添加元素。
# 从左侧添加元素
LPUSH key value1 value2
# 从右侧添加元素
RPUSH key value1 value2
# 从左侧弹出元素
LPOP key
# 从右侧弹出元素
RPOP key
# 获取列表长度
LLEN key
# 获取列表元素(0表示第一个元素,-1表示最后一个)
LRANGE key start stop
# 获取指定索引的元素
LINDEX key index
# 设置指定索引的元素
LSET key index value
# 在指定元素前插入元素
LINSERT key BEFORE pivot value
# 在指定元素后插入元素
LINSERT key AFTER pivot value
# 删除指定数量的元素
LREM key count value3.4 集合(Set)
集合是无序的字符串集合,不允许重复元素。
# 添加元素
SADD key member1 member2
# 获取所有元素
SMEMBERS key
# 检查元素是否存在
SISMEMBER key member
# 删除元素
SREM key member1 member2
# 获取集合大小
SCARD key
# 随机弹出元素
SPOP key
# 随机获取元素
SRANDMEMBER key [count]
# 集合交集
SINTER key1 key2
# 集合并集
SUNION key1 key2
# 集合差集
SDIFF key1 key23.5 有序集合(Sorted Set)
有序集合是有序的字符串集合,每个元素关联一个分数,用于排序。
# 添加元素(with score)
ZADD key score1 member1 score2 member2
# 获取元素分数
ZSCORE key member
# 获取元素排名(从小到大)
ZRANK key member
# 获取元素排名(从大到小)
ZREVRANK key member
# 获取指定范围的元素(从小到大)
ZRANGE key start stop [WITHSCORES]
# 获取指定范围的元素(从大到小)
ZREVRANGE key start stop [WITHSCORES]
# 获取指定分数范围的元素
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
# 增加元素分数
ZINCRBY key increment member
# 删除元素
ZREM key member1 member2
# 获取集合大小
ZCARD key
# 获取指定分数范围内的元素数量
ZCOUNT key min max4. 高级特性
4.1 发布/订阅(Pub/Sub)
Redis的发布/订阅功能允许消息的发布者和订阅者之间进行消息传递。
# 订阅频道
SUBSCRIBE channel1 channel2
# 发布消息
PUBLISH channel message
# 订阅模式
PSUBSCRIBE pattern* # 如 news* 订阅所有以news开头的频道
# 取消订阅
UNSUBSCRIBE channel1 channel2
# 取消模式订阅
PUNSUBSCRIBE pattern* # 如 news* 取消订阅所有以news开头的频道4.2 事务(Transaction)
Redis事务允许将多个命令打包执行,要么全部执行,要么全部不执行。
# 开始事务
MULTI
# 执行命令(入队)
SET key1 value1
SET key2 value2
GET key1
# 执行事务
EXEC
# 取消事务
DISCARD4.3 Lua脚本
Redis支持使用Lua脚本执行原子操作,提高性能和灵活性。
# 执行Lua脚本
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 key value
# 缓存Lua脚本
SCRIPT LOAD "return redis.call('SET', KEYS[1], ARGV[1])"
# 响应: "script_sha1"
# 执行缓存的脚本
EVALSHA script_sha1 1 key value
# 检查脚本是否存在
SCRIPT EXISTS script_sha1
# 清除所有缓存的脚本
SCRIPT FLUSH4.4 管道(Pipeline)
管道允许将多个命令一次性发送到Redis服务器,减少网络往返时间。
# 使用redis-cli的管道
redis-cli << EOF
SET key1 value1
SET key2 value2
GET key1
GET key2
EOF
# 使用客户端库的管道(以Node.js为例)
const redis = require('redis');
const client = redis.createClient();
const pipeline = client.pipeline();
pipeline.set('key1', 'value1');
pipeline.set('key2', 'value2');
pipeline.get('key1');
pipeline.get('key2');
pipeline.exec((err, results) => {
console.log(results);
});5. 持久化
5.1 RDB持久化
RDB(Redis Database)是Redis默认的持久化方式,通过创建数据快照的方式将数据持久化到磁盘。
# 手动触发RDB持久化
SAVE # 阻塞式
BGSAVE # 非阻塞式,后台执行
# 查看RDB持久化状态
INFO persistence5.2 AOF持久化
AOF(Append Only File)持久化通过记录所有写命令来恢复数据,更安全但文件较大。
# 开启AOF持久化(在配置文件中)
appendonly yes
# AOF重写(压缩AOF文件)
BGREWRITEAOF
# 查看AOF持久化状态
INFO persistence5.3 混合持久化
Redis 4.0+支持混合持久化,结合了RDB和AOF的优点。
# 开启混合持久化(在配置文件中)
aof-use-rdb-preamble yes6. 主从复制
6.1 配置主从复制
主服务器配置
# 主服务器无需特殊配置,默认即可
port 6379从服务器配置
# 从服务器配置
port 6380
slaveof master_ip master_port
# Redis 5.0+ 使用 replicaof
# replicaof master_ip master_port
# 从服务器只读
slave-read-only yes
# Redis 5.0+ 使用 replica-read-only
# replica-read-only yes6.2 检查复制状态
# 在主服务器上查看
INFO replication
# 在从服务器上查看
INFO replication6.3 手动切换主从
# 在从服务器上执行,使其成为主服务器
SLAVEOF NO ONE
# Redis 5.0+ 使用
# REPLICAOF NO ONE
# 将其他从服务器指向新的主服务器
SLAVEOF new_master_ip new_master_port
# Redis 5.0+ 使用
# REPLICAOF new_master_ip new_master_port7. Redis集群
7.1 集群架构
Redis集群采用去中心化的架构,由多个主节点和从节点组成,每个主节点负责一部分哈希槽。
7.2 搭建集群
# 创建集群目录
mkdir -p redis-cluster/{7000,7001,7002,7003,7004,7005}
# 配置每个节点(以7000为例)
cat > redis-cluster/7000/redis.conf << EOF
port 7000
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
appendonly yes
EOF
# 复制配置到其他节点
cp redis-cluster/7000/redis.conf redis-cluster/7001/
cp redis-cluster/7000/redis.conf redis-cluster/7002/
cp redis-cluster/7000/redis.conf redis-cluster/7003/
cp redis-cluster/7000/redis.conf redis-cluster/7004/
cp redis-cluster/7000/redis.conf redis-cluster/7005/
# 修改每个节点的端口号
# 编辑 redis-cluster/7001/redis.conf 等文件
# 启动所有节点
for port in {7000..7005}; do
redis-server redis-cluster/${port}/redis.conf
done
# 创建集群
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
# 连接集群
redis-cli -c -p 7000
# 检查集群状态
CLUSTER INFO
CLUSTER NODES7.3 集群操作
# 添加节点
redis-cli --cluster add-node new_node_ip:new_node_port existing_node_ip:existing_node_port
# 添加从节点
redis-cli --cluster add-node --cluster-slave --cluster-master-id master_id new_node_ip:new_node_port existing_node_ip:existing_node_port
# 删除节点
redis-cli --cluster del-node existing_node_ip:existing_node_port node_id
# 重新分片
redis-cli --cluster reshard existing_node_ip:existing_node_port8. 内存管理
8.1 内存配置
# 最大内存限制
maxmemory 2gb
# 内存淘汰策略
# allkeys-lru: 从所有键中选择最近最少使用的
# volatile-lru: 从设置了过期时间的键中选择最近最少使用的
# allkeys-random: 从所有键中随机选择
# volatile-random: 从设置了过期时间的键中随机选择
# volatile-ttl: 从设置了过期时间的键中选择剩余时间最短的
# noeviction: 不淘汰,内存满时拒绝写入
maxmemory-policy allkeys-lru8.2 内存优化
- 合理设置过期时间:对于临时数据,设置适当的过期时间
- 使用合适的数据结构:根据业务场景选择最合适的数据结构
- 压缩数据:对于字符串,可以使用压缩算法
- 避免大键:将大键拆分为多个小键
- 定期清理无用数据:删除不再使用的键
8.3 监控内存使用
# 查看内存使用情况
INFO memory
# 查看大键
redis-cli --bigkeys
# 查看键的过期时间
TTL key
PTTL key
# 查看所有设置了过期时间的键
SCAN 0 MATCH * COUNT 1000
# 然后对每个键执行 TTL9. 最佳实践
9.1 命名规范
- 使用冒号分隔命名空间:
service:module:id - 使用小写字母:避免大小写敏感问题
- 保持命名简洁:便于记忆和使用
- 使用统一前缀:便于管理和监控
9.2 性能优化
- 使用连接池:减少连接开销
- 批量操作:使用MSET、MGET等批量命令
- 使用管道:减少网络往返时间
- 避免阻塞命令:如KEYS、SMEMBERS等
- 使用Lua脚本:减少网络往返和保证原子性
- 合理设置持久化策略:根据业务需求选择合适的持久化方式
9.3 高可用性
- 主从复制:实现数据备份和读写分离
- 哨兵模式:实现自动故障转移
- 集群模式:实现水平扩展和高可用性
- 定期备份:定期备份RDB和AOF文件
- 监控告警:监控Redis的运行状态和性能指标
9.4 安全措施
- 设置密码:使用强密码
- 绑定IP:只允许特定IP访问
- 使用SSL:加密传输
- 最小权限:使用普通用户运行Redis
- 定期更新:及时更新Redis版本
- 禁用危险命令:如FLUSHALL、FLUSHDB等
10. 实际应用场景
10.1 缓存
Redis最常见的应用场景是作为缓存,减少数据库压力。
// 以Node.js为例
const redis = require('redis');
const client = redis.createClient();
async function getData(key) {
// 尝试从缓存获取
const cachedData = await client.get(key);
if (cachedData) {
return JSON.parse(cachedData);
}
// 从数据库获取
const data = await db.query('SELECT * FROM table WHERE id = ?', key);
// 存入缓存,设置过期时间
await client.setex(key, 3600, JSON.stringify(data));
return data;
}10.2 会话管理
Redis可用于存储用户会话数据,支持分布式部署。
// 以Express.js为例
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: 'your-secret',
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 24 * 60 * 60 * 1000 // 24小时
}
}));10.3 计数器
Redis的INCR命令可用于实现各种计数器,如页面访问量、下载次数等。
// 页面访问量统计
app.get('/page/:id', async (req, res) => {
const pageId = req.params.id;
// 自增计数器
await client.incr(`page:view:${pageId}`);
// 获取当前访问量
const views = await client.get(`page:view:${pageId}`);
res.render('page', { views });
});10.4 排行榜
有序集合非常适合实现排行榜功能。
// 添加分数
await client.zadd('leaderboard', score, userId);
// 获取排行榜(从高到低)
const leaderboard = await client.zrevrange('leaderboard', 0, 9, 'WITHSCORES');
// 获取用户排名
const rank = await client.zrevrank('leaderboard', userId);10.5 分布式锁
Redis可用于实现分布式锁,解决并发问题。
async function acquireLock(key, value, expireTime) {
// 使用SETNX命令尝试获取锁
const result = await client.setnx(key, value);
if (result === 1) {
// 设置过期时间,避免死锁
await client.expire(key, expireTime);
return true;
}
return false;
}
async function releaseLock(key, value) {
// 使用Lua脚本保证原子性
const script = `
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
`;
const result = await client.eval(script, 1, key, value);
return result === 1;
}10.6 消息队列
Redis的列表可用于实现简单的消息队列。
// 生产者
async function produceMessage(queue, message) {
await client.lpush(queue, JSON.stringify(message));
}
// 消费者
async function consumeMessage(queue) {
// 阻塞式获取消息,设置超时时间
const message = await client.brpop(queue, 0);
if (message) {
return JSON.parse(message[1]);
}
return null;
}11. 监控与维护
11.1 监控工具
- Redis自带监控:
INFO命令 - Redis Sentinel:监控主从复制和故障转移
- Redis Cluster:监控集群状态
- 第三方监控工具:
- Prometheus + Grafana
- Datadog
- New Relic
- Zabbix
11.2 常见问题与解决方案
| 问题 | 症状 | 解决方案 |
|---|---|---|
| 内存不足 | OOM command not allowed when used memory > 'maxmemory' |
增加内存、调整淘汰策略、清理无用数据 |
| 连接数过多 | max number of clients reached |
增加 maxclients 配置、使用连接池 |
| 持久化失败 | 日志中出现持久化错误 | 检查磁盘空间、调整持久化策略 |
| 复制延迟 | 从服务器数据落后于主服务器 | 检查网络连接、调整复制配置 |
| 集群故障 | 集群状态异常 | 检查节点状态、重新分片、手动故障转移 |
11.3 备份与恢复
# 备份RDB文件
cp /var/lib/redis/dump.rdb /backup/
# 备份AOF文件
cp /var/lib/redis/appendonly.aof /backup/
# 恢复数据
# 1. 停止Redis服务
# 2. 将备份文件复制到Redis数据目录
# 3. 启动Redis服务12. 总结
Redis是一个功能强大、性能优异的内存数据库,广泛应用于缓存、会话管理、计数器、排行榜、分布式锁、消息队列等场景。通过本教程的学习,您应该已经掌握了Redis的基本概念、数据结构、高级特性和最佳实践,可以开始在实际项目中应用它来构建高性能的应用系统。
12.1 核心优势
- 高性能:内存存储,读写速度极快
- 丰富的数据结构:支持多种数据结构,满足不同业务需求
- 灵活的持久化:支持RDB和AOF两种持久化方式
- 高可用性:支持主从复制和集群
- 丰富的功能:支持发布/订阅、事务、Lua脚本等
- 简单易用:命令简洁明了,学习曲线平缓
12.2 适用场景
- 缓存:减少数据库压力,提高响应速度
- 会话管理:支持分布式部署
- 计数器:实时统计
- 排行榜:游戏、活动等场景
- 分布式锁:解决并发问题
- 消息队列:异步处理任务
- 实时数据分析:快速处理和分析数据
12.3 未来发展
Redis不断演进,新版本引入了更多功能和性能优化:
- Redis 6.0:引入多线程IO、ACL访问控制等
- Redis 7.0:引入时间序列数据结构、函数等
- Redis 8.0:持续优化性能和功能
通过持续学习和实践,您可以充分发挥Redis的优势,为您的应用系统提供更高的性能和可靠性。