Redis
介绍
Redis(Remote Dictionary Server)是一个开源的内存数据结构存储系统,它可以用作数据库、缓存和消息中间件。Redis支持多种数据结构,如字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等。它具有高性能、高可用性和灵活的数据结构等特点,被广泛应用于缓存、会话管理、实时分析和消息队列等场景。
原理
内存存储
Redis将数据存储在内存中,这使得它具有极高的读写性能。同时,Redis提供了持久化机制,可以将数据保存到磁盘中,以防止数据丢失。
数据结构
Redis支持多种数据结构:
- 字符串(String):最基本的数据结构,可以存储文本、数字等
- 哈希(Hash):键值对的集合,适合存储对象
- 列表(List):有序的字符串集合,支持在两端进行插入和删除操作
- 集合(Set):无序的字符串集合,支持交集、并集、差集等操作
- 有序集合(Sorted Set):有序的字符串集合,每个元素关联一个分数
- 位图(Bitmap):位操作,适合存储大量布尔值
- HyperLogLog:用于基数统计的概率数据结构
- 地理空间(Geo):存储地理位置信息
持久化机制
Redis提供两种持久化方式:
- RDB(Redis Database):在指定的时间间隔内将内存中的数据集快照写入磁盘
- AOF(Append Only File):记录每次写操作,当Redis重启时,重新执行这些命令来恢复数据
- 混合持久化:Redis 4.0及以上版本支持混合持久化,结合RDB和AOF的优点
高可用
- 主从复制:数据从主节点复制到从节点
- 哨兵模式:监控主节点状态,当主节点故障时,自动选举新的主节点
- 集群模式:将数据分布在多个节点上,提高可用性和扩展性
事件驱动模型
Redis使用单线程的事件驱动模型,通过IO多路复用机制处理多个客户端连接,避免了线程切换的开销,具有极高的性能。
事务支持
Redis支持简单的事务,通过MULTI、EXEC、DISCARD和WATCH命令实现。Redis事务是原子的,要么全部执行,要么全部不执行,但不支持回滚。
图示
Redis架构
┌─────────────────────────────────────────────────────────────────┐
│ 客户端应用 │
└───────────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────────▼─────────────────────────────────┐
│ Redis服务器 │
├───────────────────────────────┬─────────────────────────────────┤
│ 内存数据存储 │ 持久化模块 │
│ (String、Hash、List等) │ (RDB、AOF、混合持久化) │
├───────────────────────────────┼─────────────────────────────────┤
│ 事件循环 │ 网络通信 │
│ (单线程事件驱动模型) │ (IO多路复用) │
└───────────────────────────────┴─────────────────────────────────┘
Redis数据结构
┌─────────────────────────────────────────────────────────────────┐
│ 数据结构 │ 底层实现 │ 特点 │
├────────────┼──────────────────┼────────────────────────────────┤
│ String │ 简单动态字符串 │ 二进制安全,支持追加、截断等操作 │
│ Hash │ 哈希表 │ 适合存储对象,支持字段级操作 │
│ List │ 双向链表/压缩列表 │ 有序,支持两端操作 │
│ Set │ 哈希表/整数集合 │ 无序,支持集合运算 │
│ Sorted Set│ 跳跃表/压缩列表 │ 有序,支持范围查询 │
│ Bitmap │ 简单动态字符串 │ 位操作,节省空间 │
│ HyperLogLog│ 概率数据结构 │ 基数统计,占用空间小 │
│ Geo │ 有序集合 │ 地理位置计算 │
└────────────┴──────────────────┴────────────────────────────────┘
Redis集群架构
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 主节点1 │◄─────►│ 从节点1 │ │ 从节点2 │
│ (Master 1) │ │ (Slave 1) │ │ (Slave 2) │
└──────┬────────┘ └───────────────┘ └───────────────┘
│
│
▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 主节点2 │◄─────►│ 从节点3 │ │ 从节点4 │
│ (Master 2) │ │ (Slave 3) │ │ (Slave 4) │
└──────┬────────┘ └───────────────┘ └───────────────┘
│
│
▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 主节点3 │◄─────►│ 从节点5 │ │ 从节点6 │
│ (Master 3) │ │ (Slave 5) │ │ (Slave 6) │
└───────────────┘ └───────────────┘ └───────────────┘
实例
基本操作
const redis = require('redis');
const { promisify } = require('util');
// 创建客户端
const client = redis.createClient({
host: 'localhost',
port: 6379
});
// 转换为Promise风格
const getAsync = promisify(client.get).bind(client);
const setAsync = promisify(client.set).bind(client);
const hgetAsync = promisify(client.hget).bind(client);
const hsetAsync = promisify(client.hset).bind(client);
// 字符串操作
async function stringOperations() {
// 设置值
await setAsync('name', '张三');
await setAsync('age', 30);
await setAsync('counter', 1);
// 获取值
const name = await getAsync('name');
console.log('name:', name);
// 递增
await client.incr('counter');
const counter = await getAsync('counter');
console.log('counter:', counter);
// 设置过期时间
await setAsync('temp_key', '临时值', 'EX', 10);
}
// 哈希操作
async function hashOperations() {
// 设置哈希值
await hsetAsync('user:1', 'name', '张三');
await hsetAsync('user:1', 'age', 30);
await hsetAsync('user:1', 'email', 'zhangsan@example.com');
// 获取哈希值
const name = await hgetAsync('user:1', 'name');
console.log('user:1 name:', name);
// 获取所有字段
const user = await promisify(client.hgetall).bind(client)('user:1');
console.log('user:1:', user);
}
// 列表操作
async function listOperations() {
// 向列表添加元素
await client.lpush('users', '张三', '李四', '王五');
// 获取列表长度
const length = await promisify(client.llen).bind(client)('users');
console.log('users list length:', length);
// 获取列表元素
const users = await promisify(client.lrange).bind(client)('users', 0, -1);
console.log('users list:', users);
}
// 集合操作
async function setOperations() {
// 向集合添加元素
await client.sadd('tags', 'javascript', 'nodejs', 'redis');
// 获取集合大小
const size = await promisify(client.scard).bind(client)('tags');
console.log('tags set size:', size);
// 获取集合所有元素
const tags = await promisify(client.smembers).bind(client)('tags');
console.log('tags set:', tags);
}
// 有序集合操作
async function sortedSetOperations() {
// 向有序集合添加元素
await client.zadd('scores', 90, '张三', 85, '李四', 95, '王五');
// 获取有序集合大小
const size = await promisify(client.zcard).bind(client)('scores');
console.log('scores sorted set size:', size);
// 获取排名前两名的元素
const topScores = await promisify(client.zrevrange).bind(client)('scores', 0, 1, 'WITHSCORES');
console.log('top 2 scores:', topScores);
}
// 执行操作
async function run() {
try {
await stringOperations();
await hashOperations();
await listOperations();
await setOperations();
await sortedSetOperations();
} catch (err) {
console.error('Redis操作失败:', err);
} finally {
client.quit();
}
}
run();
事务操作
async function transactionExample() {
try {
// 开始事务
client.multi();
// 执行事务操作
client.incr('counter');
client.set('last_updated', new Date().toISOString());
client.hincrby('user:1', 'visits', 1);
// 提交事务
const results = await promisify(client.exec).bind(client)();
console.log('事务执行结果:', results);
} catch (err) {
console.error('事务执行失败:', err);
} finally {
client.quit();
}
}
发布订阅
// 订阅者
function subscriber() {
const subscriber = redis.createClient();
subscriber.on('message', (channel, message) => {
console.log(`收到来自频道 ${channel} 的消息: ${message}`);
});
subscriber.subscribe('news', 'updates');
}
// 发布者
function publisher() {
const publisher = redis.createClient();
setTimeout(() => {
publisher.publish('news', 'Redis发布订阅示例');
publisher.publish('updates', 'Redis更新通知');
publisher.quit();
}, 1000);
}
// 启动订阅者和发布者
subscriber();
publisher();
缓存示例
// 使用Redis缓存数据库查询结果
async function cacheExample() {
try {
const userId = 1;
const cacheKey = `user:${userId}`;
// 先从缓存获取
const cachedUser = await getAsync(cacheKey);
if (cachedUser) {
console.log('从缓存获取用户数据:', JSON.parse(cachedUser));
return;
}
// 缓存未命中,从数据库查询
console.log('缓存未命中,从数据库查询');
// 模拟数据库查询
const userFromDb = {
id: userId,
name: '张三',
age: 30,
email: 'zhangsan@example.com'
};
// 存入缓存,设置过期时间为1小时
await setAsync(cacheKey, JSON.stringify(userFromDb), 'EX', 3600);
console.log('从数据库获取用户数据并缓存:', userFromDb);
} catch (err) {
console.error('缓存操作失败:', err);
}
}
专业解决方案
缓存策略
- 缓存穿透:查询不存在的数据,解决方案:缓存空值、布隆过滤器
- 缓存击穿:热点key过期,解决方案:设置永不过期、互斥锁
- 缓存雪崩:大量key同时过期,解决方案:过期时间随机化、多级缓存
- 缓存一致性:数据库和缓存数据不一致,解决方案:双写模式、失效模式
- 缓存预热:系统启动时加载热点数据到缓存
- 缓存降级:当缓存系统故障时,降级到备用方案
数据结构应用场景
- 字符串:缓存简单值、计数器、分布式锁
- 哈希:缓存对象、用户信息、商品详情
- 列表:消息队列、最新列表、排行榜
- 集合:标签系统、好友关系、共同好友
- 有序集合:排行榜、计分系统、范围查询
- 位图:用户在线状态、活跃用户统计
- HyperLogLog:UV统计、搜索记录分析
- 地理空间:附近的人、位置服务
高可用
- 主从复制:配置主从复制,实现数据冗余
- 哨兵模式:监控主节点状态,自动故障转移
- 集群模式:Redis Cluster,数据分片存储
- 故障恢复:当节点故障时,快速恢复服务
- 数据迁移:在线迁移数据,不影响服务
性能优化
- 内存管理:设置最大内存限制,配置内存淘汰策略
- 持久化优化:根据业务需求选择合适的持久化方式
- 网络优化:使用连接池,减少连接开销
- 命令优化:避免使用慢命令,如KEYS、HGETALL等
- 批处理:使用管道(Pipeline)批量执行命令
- 分区:数据分区,减少单节点数据量
安全
- 访问控制:设置密码,限制IP访问
- 命令限制:禁用危险命令,如FLUSHALL、DEL等
- 数据加密:启用传输加密(TLS/SSL)
- 隔离:运行在独立环境,避免权限提升
- 监控:监控异常访问和命令执行
持久化策略
- RDB:适合大规模数据恢复,性能好,但可能丢失数据
- AOF:数据更安全,但性能开销较大
- 混合持久化:结合RDB和AOF的优点,Redis 4.0及以上版本支持
- 持久化优化:调整AOF重写策略,减少磁盘IO
工具推荐
- Redis CLI:官方命令行工具
- Redis Desktop Manager:跨平台GUI工具
- Redis Insight:Redis Labs提供的GUI工具
- Redis Commander:基于Web的管理工具
- Prometheus + Grafana:监控Redis性能
- Redis Exporter:Prometheus导出器
- redis-cli:官方命令行客户端
- iredis:增强版Redis命令行工具
- Redis Benchmark:性能测试工具