Redis 中文教程

1. 核心概念

Redis(Remote Dictionary Server)是一个开源的内存数据结构存储,用作数据库、缓存和消息代理。Redis 支持多种数据结构,如字符串、哈希表、列表、集合、有序集合、位图、超日志等,使其成为一个功能强大的工具。

1.1 主要特点

  • 内存存储:数据存储在内存中,提供极高的读写速度
  • 持久化:支持 RDB 和 AOF 两种持久化方式
  • 多种数据结构:支持字符串、哈希表、列表、集合、有序集合等
  • 发布/订阅:支持消息发布和订阅功能
  • 事务:支持原子性事务操作
  • Lua 脚本:支持 Lua 脚本执行
  • 主从复制:支持主从复制,实现高可用性
  • 集群:支持 Redis Cluster 集群模式

1.2 技术栈特点

  • 内存存储:数据存储在内存中,读写速度极快
  • 持久化:支持 RDB 和 AOF 两种持久化方式
  • 数据结构丰富:支持多种数据结构,适应不同场景
  • 高可用性:支持主从复制和集群模式
  • 可扩展性:支持水平扩展

2. 安装配置

2.1 安装

2.1.1 Windows 安装

  1. Redis 官方网站 下载 Windows 版本的 Redis
  2. 解压到指定目录
  3. 运行 redis-server.exe 启动服务器
  4. 运行 redis-cli.exe 启动客户端

2.1.2 Linux 安装

使用包管理器安装:

# Ubuntu/Debian
apt-get update
apt-get install redis-server

# CentOS/RHEL
yum install redis

# 启动服务
systemctl start redis

# 设置开机自启
systemctl enable redis

2.1.3 macOS 安装

使用 Homebrew 安装:

brew install redis

# 启动服务
brew services start redis

2.2 基本配置

Redis 的配置文件通常位于 /etc/redis/redis.conf(Linux)或 Redis 安装目录下的 redis.windows.conf(Windows)。

主要配置项:

# 绑定地址
bind 127.0.0.1

# 端口号
port 6379

# 守护进程模式
daemonize yes

# 密码
requirepass your_password

# 持久化配置
# RDB 持久化
save 900 1
save 300 10
save 60 10000

# AOF 持久化
appendonly yes
appendfsync everysec

2.3 启动 Redis

2.3.1 启动服务器

# Linux/macOS
redis-server /etc/redis/redis.conf

# Windows
redis-server.exe redis.windows.conf

2.3.2 启动客户端

# 基本连接
redis-cli

# 指定主机和端口
redis-cli -h host -p port

# 使用密码连接
redis-cli -a password

3. 基本使用

3.1 字符串操作

3.1.1 设置和获取值

# 设置键值对
SET key value

# 获取值
GET key

# 设置键值对并指定过期时间(秒)
SETEX key seconds value

# 设置键值对并指定过期时间(毫秒)
PSETEX key milliseconds value

# 仅当键不存在时设置
SETNX key value

# 同时设置多个键值对
MSET key1 value1 key2 value2

# 同时获取多个值
MGET key1 key2

3.1.2 字符串操作

# 获取字符串长度
STRLEN key

# 追加字符串
APPEND key value

# 自增
INCR key

# 自增指定值
INCRBY key increment

# 自减
DECR key

# 自减指定值
DECRBY key decrement

# 自增浮点数
INCRBYFLOAT key increment

3.2 哈希表操作

3.2.1 设置和获取哈希字段

# 设置哈希字段
HSET key field value

# 获取哈希字段
HGET key field

# 仅当字段不存在时设置
HSETNX key field value

# 同时设置多个哈希字段
HMSET key field1 value1 field2 value2

# 同时获取多个哈希字段
HMGET key field1 field2

# 获取所有哈希字段和值
HGETALL key

# 获取所有哈希字段
HKEYS key

# 获取所有哈希值
HVALS key

# 获取哈希字段数量
HLEN key

# 检查字段是否存在
HEXISTS key field

# 删除哈希字段
HDEL key field1 field2

3.2.2 哈希表数值操作

# 自增哈希字段值
HINCRBY key field increment

# 自增哈希字段浮点数
HINCRBYFLOAT key field increment

3.3 列表操作

3.3.1 基本操作

# 从左侧插入元素
LPUSH key value1 value2

# 从右侧插入元素
RPUSH key value1 value2

# 从左侧弹出元素
LPOP key

# 从右侧弹出元素
RPOP key

# 获取列表长度
LLEN key

# 获取列表元素(索引从0开始)
LRANGE key start stop

# 获取指定索引的元素
LINDEX key index

# 设置指定索引的元素
LSET key index value

# 在指定元素前插入元素
LINSERT key BEFORE pivot value

# 在指定元素后插入元素
LINSERT key AFTER pivot value

# 删除指定数量的元素
LREM key count value

# 保留指定范围的元素
LTRIM key start stop

# 阻塞弹出元素
BLPOP key1 key2 timeout
BRPOP key1 key2 timeout

3.4 集合操作

3.4.1 基本操作

# 添加元素
SADD key member1 member2

# 移除元素
SREM key member1 member2

# 获取所有元素
SMEMBERS key

# 检查元素是否存在
SISMEMBER key member

# 获取集合大小
SCARD key

# 随机获取元素
SRANDMEMBER key [count]

# 随机弹出元素
SPOP key [count]

3.4.2 集合运算

# 交集
SINTER key1 key2

# 并集
SUNION key1 key2

# 差集
SDIFF key1 key2

# 交集存储到新集合
SINTERSTORE destination key1 key2

# 并集存储到新集合
SUNIONSTORE destination key1 key2

# 差集存储到新集合
SDIFFSTORE destination key1 key2

3.5 有序集合操作

3.5.1 基本操作

# 添加元素(带分数)
ZADD key score1 member1 score2 member2

# 移除元素
ZREM key member1 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]

# 获取指定分数范围的元素(从大到小)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

# 获取集合大小
ZCARD key

# 获取指定分数范围的元素数量
ZCOUNT key min max

# 增加元素分数
ZINCRBY key increment member

# 删除指定排名范围的元素
ZREMRANGEBYRANK key start stop

# 删除指定分数范围的元素
ZREMRANGEBYSCORE key min max

3.6 通用操作

# 删除键
DEL key1 key2

# 检查键是否存在
EXISTS key

# 设置键过期时间(秒)
EXPIRE key seconds

# 设置键过期时间(毫秒)
PEXPIRE key milliseconds

# 设置键的过期时间戳(秒)
EXPIREAT key timestamp

# 设置键的过期时间戳(毫秒)
PEXPIREAT key milliseconds-timestamp

# 移除键的过期时间
PERSIST key

# 获取键的剩余过期时间(秒)
TTL key

# 获取键的剩余过期时间(毫秒)
PTTL key

# 重命名键
RENAME key newkey

# 仅当新键不存在时重命名
RENAMENX key newkey

# 查看键的类型
TYPE key

# 查找匹配的键
KEYS pattern

# 随机获取一个键
RANDOMKEY

# 清空当前数据库
FLUSHDB

# 清空所有数据库
FLUSHALL

4. 高级功能

4.1 持久化

4.1.1 RDB 持久化

RDB(Redis Database)是一种快照持久化方式,将某个时间点的数据库状态保存到磁盘。

配置:

# 900秒内有1个键变化时保存
save 900 1

# 300秒内有10个键变化时保存
save 300 10

# 60秒内有10000个键变化时保存
save 60 10000

# RDB 文件路径
dir /var/lib/redis

# RDB 文件名
dbfilename dump.rdb

# 压缩 RDB 文件
rdbcompression yes

手动触发 RDB 持久化:

SAVE  # 同步保存
BGSAVE  # 异步保存

4.1.2 AOF 持久化

AOF(Append Only File)是一种追加式持久化方式,将所有写操作命令追加到文件中。

配置:

# 启用 AOF
appendonly yes

# AOF 文件名
appendfilename "appendonly.aof"

# 同步策略:always(每次操作)、everysec(每秒)、no(由操作系统决定)
appendfsync everysec

# 重写时是否同步
no-appendfsync-on-rewrite no

# 重写触发条件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

手动触发 AOF 重写:

BGREWRITEAOF

4.2 主从复制

主从复制允许将一个 Redis 服务器(主服务器)的数据复制到多个 Redis 服务器(从服务器)。

4.2.1 配置主从复制

主服务器配置

# 绑定地址
bind 0.0.0.0

# 端口
port 6379

# 密码
requirepass your_password

从服务器配置

# 绑定地址
bind 0.0.0.0

# 端口
port 6380

# 主服务器地址和端口
replicaof 127.0.0.1 6379

# 主服务器密码
masterauth your_password

4.2.2 查看复制状态

# 主服务器查看从服务器信息
INFO replication

# 从服务器切换为主服务器
REPLICAOF NO ONE

4.3 事务

Redis 事务允许将多个命令打包执行,保证原子性。

# 开始事务
MULTI

# 执行命令(入队)
SET key1 value1
SET key2 value2
GET key1

# 执行事务
EXEC

# 取消事务
DISCARD

4.4 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])"
# 返回脚本 SHA1 值
# 使用脚本 SHA1 值执行
EVALSHA sha1 1 key value

# 查看脚本是否存在
SCRIPT EXISTS sha1

# 清除所有脚本缓存
SCRIPT FLUSH

# 杀死正在执行的脚本
SCRIPT KILL

4.5 发布/订阅

Redis 支持发布/订阅模式,用于消息传递。

4.5.1 发布消息

# 发布消息到频道
PUBLISH channel message

4.5.2 订阅频道

# 订阅频道
SUBSCRIBE channel1 channel2

# 订阅模式
PSUBSCRIBE pattern*

# 取消订阅
UNSUBSCRIBE channel1 channel2

# 取消模式订阅
PUNSUBSCRIBE pattern*

4.6 集群

Redis Cluster 提供了自动分片和高可用性。

4.6.1 配置集群

  1. 创建集群配置文件:
# 端口
port 7000

# 集群模式
cluster-enabled yes

# 集群配置文件
cluster-config-file nodes-7000.conf

# 集群超时时间
cluster-node-timeout 15000

# 绑定地址
bind 0.0.0.0
  1. 启动多个 Redis 实例(至少 3 个主节点)

  2. 创建集群:

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

4.6.2 集群操作

# 查看集群信息
CLUSTER INFO

# 查看集群节点
CLUSTER NODES

# 查看键所在的槽
CLUSTER KEYSLOT key

# 查看槽的节点
CLUSTER GETKEYSINSLOT slot count

5. 最佳实践

5.1 性能优化

5.1.1 内存优化

  • 合理设置内存限制:使用 maxmemory 配置限制 Redis 使用的最大内存
  • 选择合适的内存淘汰策略:根据业务场景选择 maxmemory-policy
  • 使用小数据类型:如使用整型而非字符串存储数字
  • 压缩数据:对于大型字符串,考虑使用压缩
  • 避免大键:将大型数据结构拆分为多个小键

5.1.2 命令优化

  • 使用批量命令:如 MSETHMSET
  • 避免使用阻塞命令:如 BLPOPBRPOP 等,在生产环境中谨慎使用
  • 使用管道:通过管道减少网络往返时间
  • 限制 KEYS 命令使用:在生产环境中使用 SCAN 代替 KEYS
  • 合理使用事务:避免事务中包含过多命令

5.1.3 持久化优化

  • 根据业务场景选择持久化方式:RDB 适合备份,AOF 适合数据安全
  • 调整 AOF 同步策略everysec 平衡性能和安全性
  • 定期执行 AOF 重写:减少 AOF 文件大小
  • 使用混合持久化:Redis 4.0+ 支持 RDB 和 AOF 混合持久化

5.2 安全性

5.2.1 访问控制

  • 设置密码:使用 requirepass 配置密码
  • 绑定地址:限制 Redis 只监听特定地址
  • 使用防火墙:限制 Redis 端口的访问
  • 避免使用默认端口:修改默认端口 6379

5.2.2 数据安全

  • 定期备份:定期备份 RDB 和 AOF 文件
  • 使用哨兵模式:实现高可用性
  • 使用集群模式:实现数据分片和高可用性

5.3 监控和维护

5.3.1 监控

  • 使用 INFO 命令:查看 Redis 运行状态
  • 使用 MONITOR 命令:实时监控命令执行
  • 使用 Redis 监控工具:如 Redis Exporter + Prometheus + Grafana

5.3.2 维护

  • 定期清理过期键:使用 EXPIRE 设置过期时间
  • 定期检查内存使用:避免内存溢出
  • 定期检查持久化文件:确保数据安全
  • 定期重启服务:在维护窗口重启服务,释放内存

6. 实际应用

6.1 缓存

Redis 最常见的应用场景是作为缓存,提高系统性能。

6.1.1 基本缓存

const redis = require('redis');
const client = redis.createClient({
  url: 'redis://localhost:6379'
});

// 连接客户端
client.connect();

// 缓存数据
async function cacheData(key, value, expiration = 3600) {
  await client.set(key, JSON.stringify(value), {
    EX: expiration
  });
}

// 获取缓存数据
async function getCachedData(key) {
  const data = await client.get(key);
  return data ? JSON.parse(data) : null;
}

// 示例使用
async function getUserData(userId) {
  // 尝试从缓存获取
  const cachedData = await getCachedData(`user:${userId}`);
  if (cachedData) {
    console.log('从缓存获取数据');
    return cachedData;
  }
  
  // 缓存未命中,从数据库获取
  console.log('从数据库获取数据');
  const userData = {
    id: userId,
    name: '张三',
    email: 'zhangsan@example.com'
  };
  
  // 存入缓存
  await cacheData(`user:${userId}`, userData);
  return userData;
}

// 使用示例
getUserData(123).then(console.log);

6.1.2 缓存预热

// 缓存预热
async function warmUpCache() {
  // 预加载热点数据
  const hotUsers = [1, 2, 3, 4, 5];
  for (const userId of hotUsers) {
    const userData = {
      id: userId,
      name: `用户${userId}`,
      email: `user${userId}@example.com`
    };
    await cacheData(`user:${userId}`, userData);
  }
  console.log('缓存预热完成');
}

// 启动时执行缓存预热
warmUpCache();

6.1.3 缓存更新

// 更新缓存
async function updateCache(key, value, expiration = 3600) {
  await client.set(key, JSON.stringify(value), {
    EX: expiration
  });
}

// 删除缓存
async function deleteCache(key) {
  await client.del(key);
}

// 示例:更新用户数据
async function updateUserData(userId, updates) {
  // 更新数据库
  console.log('更新数据库');
  
  // 更新缓存
  const updatedUserData = {
    id: userId,
    name: updates.name || '张三',
    email: updates.email || 'zhangsan@example.com'
  };
  await updateCache(`user:${userId}`, updatedUserData);
  return updatedUserData;
}

6.2 会话存储

Redis 适合存储用户会话,支持分布式部署。

const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');

const app = express();

// 创建 Redis 客户端
const redisClient = redis.createClient({
  url: 'redis://localhost:6379'
});
redisClient.connect();

// 配置会话中间件
app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: 'your-secret-key',
  resave: false,
  saveUninitialized: false,
  cookie: {
    maxAge: 24 * 60 * 60 * 1000 // 24小时
  }
}));

// 示例路由
app.get('/login', (req, res) => {
  // 模拟登录
  req.session.user = {
    id: 123,
    name: '张三'
  };
  res.send('登录成功');
});

app.get('/profile', (req, res) => {
  if (req.session.user) {
    res.send(`欢迎,${req.session.user.name}`);
  } else {
    res.send('请先登录');
  }
});

app.get('/logout', (req, res) => {
  req.session.destroy();
  res.send('登出成功');
});

app.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
});

6.3 消息队列

Redis 可以用作简单的消息队列,使用列表或发布/订阅模式。

6.3.1 基于列表的消息队列

const redis = require('redis');
const client = redis.createClient({
  url: 'redis://localhost:6379'
});

// 连接客户端
client.connect();

// 发送消息
async function sendMessage(queue, message) {
  await client.lPush(queue, JSON.stringify(message));
  console.log('消息发送成功:', message);
}

// 接收消息
async function receiveMessage(queue) {
  const message = await client.rPop(queue);
  if (message) {
    console.log('消息接收成功:', JSON.parse(message));
    return JSON.parse(message);
  }
  return null;
}

// 阻塞接收消息
async function receiveMessageBlocking(queue, timeout = 0) {
  const result = await client.brPop(queue, timeout);
  if (result) {
    console.log('消息接收成功:', JSON.parse(result.element));
    return JSON.parse(result.element);
  }
  return null;
}

// 示例使用
async function example() {
  // 发送消息
  await sendMessage('tasks', { id: 1, name: '任务1' });
  await sendMessage('tasks', { id: 2, name: '任务2' });
  
  // 接收消息
  await receiveMessage('tasks');
  
  // 阻塞接收消息
  await receiveMessageBlocking('tasks');
}

example();

6.3.2 基于发布/订阅的消息队列

const redis = require('redis');

// 创建发布客户端
const publisher = redis.createClient({
  url: 'redis://localhost:6379'
});
publisher.connect();

// 创建订阅客户端
const subscriber = redis.createClient({
  url: 'redis://localhost:6379'
});
subscriber.connect();

// 订阅频道
subscriber.subscribe('notifications', (message) => {
  console.log('收到通知:', JSON.parse(message));
});

// 发布消息
async function publishNotification(notification) {
  await publisher.publish('notifications', JSON.stringify(notification));
  console.log('通知发布成功:', notification);
}

// 示例使用
async function example() {
  await publishNotification({ id: 1, title: '通知1', content: '这是一条通知' });
  await publishNotification({ id: 2, title: '通知2', content: '这是另一条通知' });
}

example();

6.4 计数器

Redis 适合实现各种计数器,如访问量、点赞数等。

const redis = require('redis');
const client = redis.createClient({
  url: 'redis://localhost:6379'
});

// 连接客户端
client.connect();

// 增加计数器
async function incrementCounter(key) {
  const count = await client.incr(key);
  console.log(`${key} 计数器值: ${count}`);
  return count;
}

// 增加指定值
async function incrementCounterBy(key, value) {
  const count = await client.incrBy(key, value);
  console.log(`${key} 计数器值: ${count}`);
  return count;
}

// 获取计数器值
async function getCounter(key) {
  const count = await client.get(key);
  return count ? parseInt(count) : 0;
}

// 重置计数器
async function resetCounter(key) {
  await client.set(key, 0);
  console.log(`${key} 计数器已重置`);
}

// 示例使用
async function example() {
  // 页面访问量
  await incrementCounter('page:views:home');
  
  // 文章点赞数
  await incrementCounter('post:likes:123');
  
  // 获取计数器值
  const homeViews = await getCounter('page:views:home');
  console.log('首页访问量:', homeViews);
  
  // 重置计数器
  await resetCounter('page:views:home');
}

example();

6.5 实时排行榜

Redis 的有序集合适合实现实时排行榜。

const redis = require('redis');
const client = redis.createClient({
  url: 'redis://localhost:6379'
});

// 连接客户端
client.connect();

// 添加分数
async function addScore(leaderboard, member, score) {
  await client.zAdd(leaderboard, {
    score: score,
    value: member
  });
  console.log(`${member} 分数已更新: ${score}`);
}

// 获取排行榜(从高到低)
async function getLeaderboard(leaderboard, start = 0, stop = 9) {
  const result = await client.zRevRangeWithScores(leaderboard, start, stop);
  console.log('排行榜:');
  result.forEach((item, index) => {
    console.log(`${index + 1}. ${item.value}: ${item.score}`);
  });
  return result;
}

// 获取成员排名
async function getRank(leaderboard, member) {
  const rank = await client.zRevRank(leaderboard, member);
  if (rank !== null) {
    console.log(`${member} 排名: ${rank + 1}`);
    return rank + 1;
  }
  console.log(`${member} 不在排行榜中`);
  return null;
}

// 获取成员分数
async function getScore(leaderboard, member) {
  const score = await client.zScore(leaderboard, member);
  if (score !== null) {
    console.log(`${member} 分数: ${score}`);
    return score;
  }
  console.log(`${member} 不在排行榜中`);
  return null;
}

// 示例使用
async function example() {
  const leaderboard = 'game:leaderboard';
  
  // 添加分数
  await addScore(leaderboard, '玩家1', 1000);
  await addScore(leaderboard, '玩家2', 1500);
  await addScore(leaderboard, '玩家3', 1200);
  await addScore(leaderboard, '玩家4', 1800);
  await addScore(leaderboard, '玩家5', 900);
  
  // 获取排行榜
  await getLeaderboard(leaderboard);
  
  // 获取玩家排名
  await getRank(leaderboard, '玩家3');
  
  // 获取玩家分数
  await getScore(leaderboard, '玩家2');
}

example();

7. 总结

Redis 是一个功能强大的内存数据结构存储,具有极高的性能和丰富的功能。本文介绍了 Redis 的核心概念、安装配置、基本使用、高级功能、最佳实践和实际应用示例,希望能够帮助开发者更好地理解和使用 Redis。

7.1 主要优势

  • 高性能:内存存储,读写速度极快
  • 数据结构丰富:支持多种数据结构,适应不同场景
  • 持久化:支持 RDB 和 AOF 两种持久化方式
  • 高可用性:支持主从复制和集群模式
  • 可扩展性:支持水平扩展
  • 功能丰富:支持事务、Lua 脚本、发布/订阅等高级功能

7.2 适用场景

  • 缓存:提高系统性能,减轻数据库压力
  • 会话存储:支持分布式部署
  • 消息队列:实现简单的消息传递
  • 计数器:如访问量、点赞数等
  • 排行榜:实时排行榜
  • 实时数据:如实时监控数据
  • 分布式锁:实现分布式系统的锁机制

7.3 未来展望

Redis 作为一个成熟的开源项目,不断发展和完善。未来的 Redis 可能会:

  • 进一步提高性能:优化内存使用和命令执行速度
  • 增强数据结构:添加更多新的数据结构和功能
  • 改进集群模式:提高集群的稳定性和可靠性
  • 增强安全性:提供更多安全特性
  • 更好的云原生支持:适应云环境的需求

通过本文的学习,相信开发者已经对 Redis 有了全面的了解,可以开始在实际项目中使用 Redis 构建高性能的应用了。

« 上一篇 Primus 中文教程 下一篇 » MongoDB 中文教程