Redis 教程
项目概述
Redis(Remote Dictionary Server)是一个开源的内存数据结构存储,用作数据库、缓存和消息代理。它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,提供了丰富的操作命令,并且具有高性能、可持久化、分布式等特性。
- 项目链接:https://github.com/redis/redis
- 官方网站:https://redis.io/
- GitHub Stars:61k+
- 适用环境:缓存、会话存储、消息队列、实时应用
安装设置
1. 本地安装
Windows 安装
- 访问 Redis 官方 GitHub 发布页 下载 Windows 版本
- 解压到本地目录
- 运行
redis-server.exe启动服务器 - 运行
redis-cli.exe启动客户端
Linux 安装
# Ubuntu/Debian
sudo apt update
sudo apt install redis-server
# 启动 Redis 服务
sudo systemctl start redis-server
# 检查 Redis 服务状态
sudo systemctl status redis-servermacOS 安装
# 使用 Homebrew
brew install redis
# 启动 Redis 服务
brew services start redis2. 使用 Docker
# 拉取 Redis 镜像
docker pull redis
# 运行 Redis 容器
docker run --name redis-container -p 6379:6379 -d redis
# 进入 Redis 容器并使用客户端
docker exec -it redis-container redis-cli3. 客户端连接
// 使用 Node.js 客户端
const redis = require('redis');
// 创建客户端
const client = redis.createClient({
url: 'redis://localhost:6379'
});
// 连接到 Redis
client.connect().then(() => {
console.log('Redis 连接成功');
}).catch(err => {
console.error('Redis 连接失败:', err);
});
// 关闭连接
client.quit();核心数据结构
1. 字符串 (String)
字符串是 Redis 最基本的数据类型,最大可以存储 512MB 的数据。
# 设置字符串
SET key value
# 获取字符串
GET key
# 设置带过期时间的字符串
SETEX key seconds value
# 自增
INCR key
# 自减
DECR key
# 追加字符串
APPEND key value// Node.js 示例
await client.set('name', 'Redis');
const name = await client.get('name');
console.log(name); // 输出: Redis
await client.setEx('temp', 60, '临时值'); // 60秒过期
await client.incr('counter'); // 自增2. 哈希 (Hash)
哈希是一个键值对的集合,适合存储对象。
# 设置哈希字段
HSET key field value
# 获取哈希字段
HGET key field
# 获取所有字段和值
HGETALL key
# 获取所有字段
HKEYS key
# 获取所有值
HVALS key
# 删除字段
HDEL key field// Node.js 示例
await client.hSet('user:1', {
name: '张三',
age: 25,
email: 'zhangsan@example.com'
});
const user = await client.hGetAll('user:1');
console.log(user); // 输出: { name: '张三', age: '25', email: 'zhangsan@example.com' }3. 列表 (List)
列表是一个有序的字符串集合,支持在两端进行操作。
# 从左侧插入
LPUSH key value1 value2
# 从右侧插入
RPUSH key value1 value2
# 从左侧弹出
LPOP key
# 从右侧弹出
RPOP key
# 获取列表范围
LRANGE key start stop
# 获取列表长度
LLEN key// Node.js 示例
await client.lPush('tasks', '任务1', '任务2');
await client.rPush('tasks', '任务3');
const tasks = await client.lRange('tasks', 0, -1);
console.log(tasks); // 输出: ['任务2', '任务1', '任务3']4. 集合 (Set)
集合是一个无序的字符串集合,不允许重复元素。
# 添加元素
SADD key member1 member2
# 移除元素
SREM key member1 member2
# 检查元素是否存在
SISMEMBER key member
# 获取所有元素
SMEMBERS key
# 获取集合大小
SCARD key
# 集合交集
SINTER key1 key2
# 集合并集
SUNION key1 key2
# 集合差集
SDIFF key1 key2// Node.js 示例
await client.sAdd('tags', 'javascript', 'nodejs', 'redis');
const tags = await client.sMembers('tags');
console.log(tags); // 输出: ['javascript', 'nodejs', 'redis']
const exists = await client.sIsMember('tags', 'javascript');
console.log(exists); // 输出: true5. 有序集合 (Sorted Set)
有序集合是一个有序的字符串集合,每个元素都有一个分数,根据分数排序。
# 添加元素
ZADD key score1 member1 score2 member2
# 获取元素分数
ZSCORE key member
# 获取排名范围内的元素(按分数从小到大)
ZRANGE key start stop
# 获取排名范围内的元素(按分数从大到小)
ZREVRANGE key start stop
# 获取分数范围内的元素
ZRANGEBYSCORE key min max
# 增加元素分数
ZINCRBY key increment member
# 获取集合大小
ZCARD key// Node.js 示例
await client.zAdd('leaderboard', {
score: 100, member: '用户1'
}, {
score: 90, member: '用户2'
}, {
score: 80, member: '用户3'
});
const leaderboard = await client.zRevRange('leaderboard', 0, -1, { WITH_SCORES: true });
console.log(leaderboard); // 输出: ['用户1', '100', '用户2', '90', '用户3', '80']高级功能
1. 发布/订阅
Redis 支持发布/订阅模式,用于消息传递。
# 发布消息
PUBLISH channel message
# 订阅频道
SUBSCRIBE channel
# 订阅模式
PSUBSCRIBE pattern// 发布者
const publisher = redis.createClient();
await publisher.connect();
await publisher.publish('news', 'Redis 7.0 发布了!');
// 订阅者
const subscriber = redis.createClient();
await subscriber.connect();
await subscriber.subscribe('news', (message) => {
console.log('收到消息:', message);
});2. 事务
Redis 支持简单的事务,通过 MULTI、EXEC、DISCARD 命令实现。
# 开始事务
MULTI
# 执行命令(入队)
SET key1 value1
SET key2 value2
# 执行事务
EXEC
# 取消事务
DISCARD// Node.js 示例
const transaction = client.multi();
transaction.set('key1', 'value1');
transaction.set('key2', 'value2');
const result = await transaction.exec();
console.log(result); // 输出: [OK, OK]3. 持久化
Redis 提供两种持久化方式:RDB 和 AOF。
RDB(Redis Database)
- 定期将内存中的数据快照保存到磁盘
- 适合备份和灾难恢复
- 配置示例:
# 900秒内有1个键被修改时保存
save 900 1
# 300秒内有10个键被修改时保存
save 300 10
# 60秒内有10000个键被修改时保存
save 60 10000AOF(Append Only File)
- 记录每个写操作到日志文件
- 更可靠,但文件会逐渐增大
- 配置示例:
# 启用 AOF
appendonly yes
# 每秒同步一次
appendfsync everysec4. 过期时间
Redis 支持为键设置过期时间,过期后自动删除。
# 设置过期时间(秒)
EXPIRE key seconds
# 设置过期时间(毫秒)
PEXPIRE key milliseconds
# 查看剩余过期时间(秒)
TTL key
# 查看剩余过期时间(毫秒)
PTTL key
# 移除过期时间
PERSIST key// Node.js 示例
await client.set('temp', '临时值');
await client.expire('temp', 60); // 60秒过期
const ttl = await client.ttl('temp');
console.log(ttl); // 输出: 剩余过期时间(秒)实际应用场景
1. 缓存系统
// 缓存示例
const getCachedData = async (key) => {
// 尝试从缓存获取
const cachedData = await client.get(key);
if (cachedData) {
console.log('从缓存获取数据');
return JSON.parse(cachedData);
}
// 缓存未命中,从数据库获取
console.log('从数据库获取数据');
const data = await fetchDataFromDatabase();
// 存入缓存,设置过期时间
await client.setEx(key, 3600, JSON.stringify(data));
return data;
};
// 使用缓存
const userData = await getCachedData('user:123');2. 会话存储
// 会话存储示例
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const app = express();
app.use(session({
store: new RedisStore({ client }),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 24 * 60 * 60 * 1000 // 24小时
}
}));
app.get('/', (req, res) => {
if (req.session.views) {
req.session.views++;
} else {
req.session.views = 1;
}
res.send(`浏览次数: ${req.session.views}`);
});3. 消息队列
// 简单消息队列
const queueName = 'tasks';
// 生产者
const produceMessage = async (message) => {
await client.lPush(queueName, JSON.stringify(message));
console.log('消息已入队:', message);
};
// 消费者
const consumeMessages = async () => {
while (true) {
// 从队列右侧弹出消息,阻塞式
const message = await client.brPop(queueName, 0);
if (message) {
const task = JSON.parse(message.element);
console.log('处理消息:', task);
// 处理任务...
}
}
};
// 启动消费者
consumeMessages();
// 生产消息
produceMessage({ id: 1, name: '任务1' });
produceMessage({ id: 2, name: '任务2' });4. 计数器
// 页面访问计数器
const incrementPageView = async (pageId) => {
const key = `page:${pageId}:views`;
const count = await client.incr(key);
return count;
};
// 获取页面访问量
const getPageViews = async (pageId) => {
const key = `page:${pageId}:views`;
const count = await client.get(key);
return parseInt(count) || 0;
};
// 使用
const views = await incrementPageView('home');
console.log('首页访问量:', views);5. 分布式锁
// 简单分布式锁
const acquireLock = async (lockName, timeout = 10000) => {
const lockKey = `lock:${lockName}`;
const lockValue = Date.now() + timeout;
// 尝试获取锁
const acquired = await client.set(lockKey, lockValue, {
NX: true, // 仅当键不存在时设置
EX: Math.ceil(timeout / 1000) // 过期时间(秒)
});
return acquired ? lockValue : false;
};
const releaseLock = async (lockName, lockValue) => {
const lockKey = `lock:${lockName}`;
const currentValue = await client.get(lockKey);
// 只有持有锁的线程才能释放
if (currentValue === lockValue.toString()) {
await client.del(lockKey);
return true;
}
return false;
};
// 使用锁
const lockValue = await acquireLock('resource');
if (lockValue) {
try {
// 执行临界区代码
console.log('获取锁成功,执行操作');
} finally {
// 释放锁
await releaseLock('resource', lockValue);
console.log('锁已释放');
}
} else {
console.log('获取锁失败,资源忙');
}代码优化建议
- 连接管理:使用连接池管理 Redis 连接,避免频繁创建和销毁连接
- 键命名规范:使用统一的键命名规范,如
{prefix}:{id}:{field} - 过期时间:为缓存数据设置合理的过期时间,避免内存溢出
- 批量操作:使用批量操作减少网络往返时间,如
MSET、HMSET - 管道操作:使用管道(Pipeline)执行多个命令,提高性能
- 内存优化:
- 使用适当的数据结构
- 避免存储过大的值
- 定期清理无用数据
- 错误处理:实现 Redis 连接失败的重试机制和错误处理
- 监控和告警:监控 Redis 内存使用、命中率等指标,设置合理的告警阈值