Redis 概述

什么是 Redis

Redis 是一个开源的基于内存的数据结构存储系统可以用作数据库缓存和消息中间件它支持多种数据结构具有高性能高可用性等特点

  • 核心功能
    • 数据存储支持多种数据结构
    • 缓存高速读写减轻数据库压力
    • 消息队列发布订阅流处理
    • 会话管理分布式 Session
  • 主要优势
    • 高性能基于内存操作读写速度极快
    • 丰富的数据结构StringListSetHashZset 等
    • 持久化支持 RDB 和 AOF 两种持久化方式
    • 高可用支持主从复制哨兵模式集群模式

Redis 的核心概念

数据类型

  • String字符串
    • 最基本的数据类型
    • 可以存储字符串整数浮点数
  • List列表
    • 有序的字符串列表
    • 支持从两端插入和弹出
  • Set集合
    • 无序且不重复的字符串集合
    • 支持交集并集差集运算
  • Hash哈希
    • 键值对集合
    • 适合存储对象
  • Zset有序集合
    • 带分数的有序集合
    • 支持范围查询和排序
  • 其他类型
    • Bitmaps位图
    • HyperLogLog基数统计
    • Geospatial地理位置
    • Streams流数据

架构组件

  • Server服务器
    • Redis 服务端进程
    • 处理客户端请求
  • Client客户端
    • 连接 Redis 服务器的程序
    • 发送命令并接收响应
  • Persistence持久化
    • RDB快照方式
    • AOF追加日志方式
  • Replication复制
    • 主从复制
    • 数据同步

Redis 的工作原理

单线程模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Redis 采用单线程模型处理命令
1. 客户端发送命令到服务器
2. 服务器将命令放入队列
3. 单线程依次执行命令
4. 返回结果给客户端

优势
- 避免上下文切换和竞争消耗
- 不会出现并发问题
- 代码简单易于维护

注意
- Redis 6.0 引入了多线程处理网络 I/O
- 但命令执行仍然是单线程

持久化机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
RDBRedis Database
1. 定期生成数据快照
2. 保存到 dump.rdb 文件
3. 恢复时加载快照文件

AOFAppend Only File
1. 记录每个写命令
2. 追加到 aof 文件
3. 恢复时重放命令

混合持久化Redis 4.0+
- 结合 RDB 和 AOF 的优点
- RDB 做全量备份
- AOF 做增量备份

环境搭建

安装 Redis

Docker 安装推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 拉取镜像
docker pull redis:7.0

# 运行容器
docker run -d \
--name redis \
-p 6379:6379 \
redis:7.0

# 带配置文件运行
docker run -d \
--name redis \
-p 6379:6379 \
-v /path/to/redis.conf:/usr/local/etc/redis/redis.conf \
redis:7.0 redis-server /usr/local/etc/redis/redis.conf

# 进入容器
docker exec -it redis redis-cli

Linux 安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Ubuntu/Debian
sudo apt-get install redis-server

# CentOS/RHEL
sudo yum install redis

# 源码编译安装
wget https://download.redis.io/releases/redis-7.0.0.tar.gz
tar -xzf redis-7.0.0.tar.gz
cd redis-7.0.0
make
make install

# 启动 Redis
redis-server

# 后台启动
redis-server --daemonize yes

Windows 安装

下载地址https://github.com/microsoftarchive/redis/releases

步骤

  1. 下载 MSI 安装包
  2. 运行安装程序
  3. 启动 Redis 服务

macOS 安装

1
2
3
4
5
6
7
8
9
10
11
# 使用 Homebrew
brew install redis

# 启动
brew services start redis

# 停止
brew services stop redis

# 命令行启动
redis-server

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# redis.conf

# 绑定地址
bind 127.0.0.1

# 端口
port 6379

# 后台运行
daemonize yes

# 日志文件
logfile "/var/log/redis/redis.log"

# 数据库数量
databases 16

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

# AOF 持久化
appendonly yes
appendfilename "appendonly.aof"

# 最大内存
maxmemory 2gb

# 内存淘汰策略
maxmemory-policy allkeys-lru

# 密码认证
requirepass your_password

验证安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 连接 Redis
redis-cli

# 测试连通性
127.0.0.1:6379> ping
PONG

# 设置键值对
127.0.0.1:6379> set name "Redis"
OK

# 获取值
127.0.0.1:6379> get name
"Redis"

# 查看信息
127.0.0.1:6379> info

基本操作

String 操作

基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 设置值
SET key value
SET name "Redis"

# 获取值
GET key
GET name

# 批量设置
MSET key1 value1 key2 value2
MSET name "Redis" version "7.0"

# 批量获取
MGET key1 key2
MGET name version

# 设置过期时间
SETEX key seconds value
SETEX code 300 "123456"

# 设置过期时间毫秒
PSETEX key milliseconds value

# 不存在时设置
SETNX key value
SETNX lock "locked"

# 递增
INCR key
INCR counter

# 递减
DECR key
DECR counter

# 增加指定值
INCRBY key increment
INCRBY counter 10

# 减少指定值
DECRBY key decrement
DECRBY counter 5

应用场景

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 计数器
INCR page_views

# 2. 分布式锁
SET lock_key unique_value NX EX 10

# 3. 验证码
SETEX verify_code 300 "123456"

# 4. 限流
INCR rate_limit:user_id
EXPIRE rate_limit:user_id 60

List 操作

基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 左侧插入
LPUSH key value
LPUSH tasks "task1" "task2"

# 右侧插入
RPUSH key value
RPUSH tasks "task3"

# 获取列表长度
LLEN key
LLEN tasks

# 获取元素
LRANGE key start stop
LRANGE tasks 0 -1

# 左侧弹出
LPOP key
LPOP tasks

# 右侧弹出
RPOP key
RPOP tasks

# 阻塞弹出
BLPOP key timeout
BRPOP key timeout

# 获取指定位置元素
LINDEX key index
LINDEX tasks 0

# 设置指定位置元素
LSET key index value
LSET tasks 0 "new_task"

# 修剪列表
LTRIM key start stop
LTRIM tasks 0 99

# 插入元素
LINSERT key BEFORE|AFTER pivot value
LINSERT tasks BEFORE "task2" "task1.5"

应用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 消息队列
LPUSH queue message
BRPOP queue 0

# 2. 最新列表如最新文章
LPUSH articles article_id
LTRIM articles 0 99

# 3. 栈
LPUSH stack item
LPOP stack

# 4. 队列
RPUSH queue item
LPOP queue

Set 操作

基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 添加元素
SADD key member
SADD tags "java" "redis" "cache"

# 获取所有元素
SMEMBERS key
SMEMBERS tags

# 判断元素是否存在
SISMEMBER key member
SISMEMBER tags "java"

# 获取集合大小
SCARD key
SCARD tags

# 删除元素
SREM key member
SREM tags "java"

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

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

# 移动元素
SMOVE source destination member
SMOVE tags old_tags "java"

集合运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 交集
SINTER key1 key2
SINTER set1 set2

# 并集
SUNION key1 key2
SUNION set1 set2

# 差集
SDIFF key1 key2
SDIFF set1 set2

# 交集并存储
SINTERSTORE destination key1 key2

# 并集并存储
SUNIONSTORE destination key1 key2

# 差集并存储
SDIFFSTORE destination key1 key2

应用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 标签系统
SADD article:1:tags "java" "redis"
SADD article:2:tags "java" "mysql"
SINTER article:1:tags article:2:tags # 共同标签

# 2. 好友关系
SADD user:1:friends 2 3 4
SADD user:2:friends 1 3 5
SINTER user:1:friends user:2:friends # 共同好友

# 3. 抽奖
SADD lottery user1 user2 user3
SPOP lottery 1 # 随机抽取

Hash 操作

基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 设置字段
HSET key field value
HSET user:1 name "张三" age 25

# 获取字段
HGET key field
HGET user:1 name

# 批量设置
HMSET key field1 value1 field2 value2
HMSET user:1 email "zhangsan@example.com" phone "13800138000"

# 批量获取
HMGET key field1 field2
HMGET user:1 name age

# 获取所有字段和值
HGETALL key
HGETALL user:1

# 获取所有字段名
HKEYS key
HKEYS user:1

# 获取所有值
HVALS key
HVALS user:1

# 删除字段
HDEL key field
HDEL user:1 phone

# 判断字段是否存在
HEXISTS key field
HEXISTS user:1 name

# 获取字段数量
HLEN key
HLEN user:1

# 字段自增
HINCRBY key field increment
HINCRBY user:1 age 1

# 字段自增浮点数
HINCRBYFLOAT key field increment
HINCRBYFLOAT user:1 score 0.5

应用场景

1
2
3
4
5
6
7
8
9
10
11
# 1. 存储对象
HSET user:1001 name "李四" age 30 email "lisi@example.com"
HGETALL user:1001

# 2. 购物车
HSET cart:user:1 product:1 2
HSET cart:user:1 product:2 1
HINCRBY cart:user:1 product:1 1

# 3. 用户信息缓存
HMSET user:info:1001 name "王五" avatar "url" level 10

Zset 操作

基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 添加成员
ZADD key score member
ZADD leaderboard 100 "player1" 200 "player2"

# 批量添加
ZADD key score1 member1 score2 member2
ZADD leaderboard 150 "player3" 180 "player4"

# 获取成员分数
ZSCORE key member
ZSCORE leaderboard "player1"

# 获取排名从小到大
ZRANK key member
ZRANK leaderboard "player1"

# 获取排名从大到小
ZREVRANK key member
ZREVRANK leaderboard "player1"

# 获取范围成员
ZRANGE key start stop [WITHSCORES]
ZRANGE leaderboard 0 9 WITHSCORES

# 获取范围成员从大到小
ZREVRANGE key start stop [WITHSCORES]
ZREVRANGE leaderboard 0 9 WITHSCORES

# 按分数范围获取
ZRANGEBYSCORE key min max [WITHSCORES]
ZRANGEBYSCORE leaderboard 100 200 WITHSCORES

# 删除成员
ZREM key member
ZREM leaderboard "player1"

# 获取集合大小
ZCARD key
ZCARD leaderboard

# 统计分数范围内的成员数
ZCOUNT key min max
ZCOUNT leaderboard 100 200

# 增加分数
ZINCRBY key increment member
ZINCRBY leaderboard 10 "player1"

应用场景

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 排行榜
ZADD game:rank 1000 player1 2000 player2
ZREVRANGE game:rank 0 9 WITHSCORES # Top 10

# 2. 延迟队列
ZADD delay_queue timestamp task_id
ZRANGEBYSCORE delay_queue 0 current_timestamp

# 3. 带权重的消息队列
ZADD priority_queue 1 low_priority_task
ZADD priority_queue 10 high_priority_task
ZPOPMIN priority_queue # 优先处理高权重

高级功能

事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 开启事务
MULTI

# 执行命令进入队列
SET name "Redis"
INCR counter

# 执行事务
EXEC

# 取消事务
DISCARD

# 监视键乐观锁
WATCH key
MULTI
SET key value
EXEC

发布订阅

1
2
3
4
5
6
7
8
9
10
11
12
# 订阅频道
SUBSCRIBE channel1 channel2

# 发布消息
PUBLISH channel1 "Hello Redis"

# 模式订阅
PSUBSCRIBE news.*

# 取消订阅
UNSUBSCRIBE channel1
PUNSUBSCRIBE news.*

Lua 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 执行 Lua 脚本
EVAL script numkeys key [key ...] arg [arg ...]

# 示例原子性操作
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 mykey myvalue

# 示例计数器限流
EVAL "
local count = redis.call('INCR', KEYS[1])
if count == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return count
" 1 rate_limit:user:1 60

# 加载脚本
SCRIPT LOAD script

# 执行已加载的脚本
EVALSHA sha1 numkeys key [key ...] arg [arg ...]

管道

1
2
3
4
5
6
7
8
9
10
# 使用管道批量执行命令
redis-cli --pipe < commands.txt

# 或在客户端中
cat << EOF | redis-cli
SET key1 value1
SET key2 value2
GET key1
GET key2
EOF

Keys 命令谨慎使用

1
2
3
4
5
6
7
# 查找键生产环境慎用
KEYS pattern
KEYS user:*

# 更好的替代方案SCAN
SCAN cursor [MATCH pattern] [COUNT count]
SCAN 0 MATCH user:* COUNT 100

Spring Boot 整合

添加依赖

1
2
3
4
5
6
7
8
9
10
11
<!-- Spring Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- Redis 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# application.yml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

// 使用 String 序列化器处理 key
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());

// 使用 JSON 序列化器处理 value
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

template.afterPropertiesSet();
return template;
}
}

Service 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.example.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class RedisService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

// 设置字符串
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}

// 设置字符串带过期时间
public void set(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}

// 获取字符串
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}

// 删除键
public Boolean delete(String key) {
return redisTemplate.delete(key);
}

// 判断键是否存在
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}

// 设置过期时间
public Boolean expire(String key, long timeout, TimeUnit unit) {
return redisTemplate.expire(key, timeout, unit);
}

// 获取过期时间
public Long getExpire(String key) {
return redisTemplate.getExpire(key);
}

// 递增
public Long increment(String key) {
return redisTemplate.opsForValue().increment(key);
}

// 递减
public Long decrement(String key) {
return redisTemplate.opsForValue().decrement(key);
}

// Hash 操作
public void hSet(String key, String field, Object value) {
redisTemplate.opsForHash().put(key, field, value);
}

public Object hGet(String key, String field) {
return redisTemplate.opsForHash().get(key, field);
}

// List 操作
public void lPush(String key, Object value) {
redisTemplate.opsForList().leftPush(key, value);
}

public Object lPop(String key) {
return redisTemplate.opsForList().leftPop(key);
}

// Set 操作
public Long sAdd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}

// Zset 操作
public Boolean zAdd(String key, Object value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
}

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.example.controller;

import com.example.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/cache")
public class CacheController {

@Autowired
private RedisService redisService;

@PostMapping("/set")
public String set(@RequestParam String key, @RequestParam String value) {
redisService.set(key, value, 300, java.util.concurrent.TimeUnit.SECONDS);
return "success";
}

@GetMapping("/get")
public Object get(@RequestParam String key) {
return redisService.get(key);
}

@DeleteMapping("/delete")
public Boolean delete(@RequestParam String key) {
return redisService.delete(key);
}
}

持久化

RDB 持久化

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# redis.conf

# 触发条件
save 900 1 # 900秒内至少1个键被修改
save 300 10 # 300秒内至少10个键被修改
save 60 10000 # 60秒内至少10000个键被修改

# RDB 文件名
dbfilename dump.rdb

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

# 压缩
rdbcompression yes

# 校验和
rdbchecksum yes

手动触发

1
2
3
4
5
6
# 保存 RDB
SAVE # 阻塞式
BGSAVE # 后台异步

# 查看最后一次保存时间
LASTSAVE

优缺点

优点 缺点
文件紧凑适合备份 可能丢失最后一次快照后的数据
恢复速度快 fork 子进程时可能影响性能
适合大规模数据恢复 无法实现实时持久化

AOF 持久化

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# redis.conf

# 开启 AOF
appendonly yes

# AOF 文件名
appendfilename "appendonly.aof"

# 同步策略
appendfsync everysec # always, everysec, no

# AOF 重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 加载时截断损坏的 AOF
aof-load-truncated yes

同步策略

策略 说明 性能 安全性
always 每次写入都同步
everysec 每秒同步一次推荐 中等 中等
no 由操作系统决定

AOF 重写

1
2
3
4
5
# 手动触发 AOF 重写
BGREWRITEAOF

# 查看 AOF 状态
INFO persistence

混合持久化

1
2
# Redis 4.0+ 支持
aof-use-rdb-preamble yes
1
2
3
4
优势
- 结合 RDB 和 AOF 的优点
- RDB 做全量备份恢复快
- AOF 做增量备份数据更安全

高可用

主从复制

配置从节点

1
2
3
# slave redis.conf
slaveof master_ip 6379
masterauth master_password

命令配置

1
2
3
4
5
# 在从节点执行
SLAVEOF master_ip 6379

# 查看复制状态
INFO replication

特点

  • 一个主节点可以有多个从节点
  • 从节点只读
  • 主节点故障需要手动切换

哨兵模式

配置哨兵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# sentinel.conf
port 26379

# 监控主节点
sentinel monitor mymaster 127.0.0.1 6379 2

# 主节点下线判定时间
sentinel down-after-milliseconds mymaster 5000

# 故障转移超时时间
sentinel failover-timeout mymaster 60000

# 并行同步的从节点数量
sentinel parallel-syncs mymaster 1

启动哨兵

1
redis-sentinel sentinel.conf

特点

  • 自动故障检测和转移
  • 客户端可以获取新的主节点地址
  • 至少需要 3 个哨兵节点

集群模式

创建集群

1
2
3
4
5
6
7
8
9
10
11
# 创建 6 个节点的集群3主3从
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 --cluster check 127.0.0.1:7000

# 连接集群
redis-cli -c -p 7000

集群配置

1
2
3
4
# redis.conf
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000

特点

  • 数据分片存储16384 个槽
  • 自动故障转移
  • 水平扩展能力强
  • 不支持多数据库只有 db0

性能优化

内存优化

1
2
3
4
5
# 设置最大内存
maxmemory 2gb

# 内存淘汰策略
maxmemory-policy allkeys-lru
策略 说明
volatile-lru 从设置了过期时间的键中选择 LRU
allkeys-lru 从所有键中选择 LRU推荐
volatile-lfu 从设置了过期时间的键中选择 LFU
allkeys-lfu 从所有键中选择 LFU
volatile-random 从设置了过期时间的键中随机选择
allkeys-random 从所有键中随机选择
volatile-ttl 选择即将过期的键
noeviction 不淘汰返回错误默认

命令优化

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 使用 Pipeline 批量执行
redis-cli --pipe < commands.txt

# 2. 避免使用 KEYS 命令
# 错误KEYS *
# 正确SCAN 0 MATCH pattern COUNT 100

# 3. 合理设置过期时间
SETEX key 300 value

# 4. 使用合适的数据结构
# 存储对象用 Hash 而非 String
HSET user:1 name "张三" age 25

连接优化

1
2
3
4
5
6
7
8
9
# 使用连接池
spring:
redis:
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms

最佳实践

命名规范

1
2
3
4
5
6
7
8
9
10
11
键命名
- 使用冒号分隔业务:对象:ID:字段
- 示例user:1001:nameorder:2024:items
- 避免特殊字符

前缀建议
- 用户相关user:
- 订单相关order:
- 商品相关product:
- 缓存相关cache:
- 会话相关session:

使用建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 1. 合理设置过期时间
redisTemplate.opsForValue().set(key, value, 300, TimeUnit.SECONDS);

// 2. 避免大 Key
// 错误存储超大字符串或集合
// 正确拆分或使用合适的数据结构

// 3. 使用 Hash 存储对象
redisTemplate.opsForHash().putAll("user:1", userMap);

// 4. 热点数据加随机过期时间
long expireTime = 300 + new Random().nextInt(60);
redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);

// 5. 缓存穿透布隆过滤器或空值缓存
Object value = redisTemplate.opsForValue().get(key);
if (value == null) {
value = queryFromDatabase(id);
if (value != null) {
redisTemplate.opsForValue().set(key, value, 300, TimeUnit.SECONDS);
} else {
// 缓存空值防止缓存穿透
redisTemplate.opsForValue().set(key, "", 60, TimeUnit.SECONDS);
}
}

监控建议

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 监控内存使用
INFO memory

# 2. 监控命中率
INFO stats

# 3. 监控连接数
INFO clients

# 4. 监控命令执行情况
INFO commandstats

# 5. 使用 Redis Sentinel 或 Cluster 监控

常见问题

缓存穿透

1
2
3
4
5
6
问题查询不存在的数据请求直接打到数据库

解决方案
1. 缓存空值
2. 使用布隆过滤器
3. 参数校验

缓存击穿

1
2
3
4
5
6
问题热点 Key 过期大量请求同时访问数据库

解决方案
1. 热点数据永不过期
2. 互斥锁分布式锁
3. 逻辑过期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 使用分布式锁解决缓存击穿
String lockKey = "lock:" + key;
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);

if (Boolean.TRUE.equals(locked)) {
try {
// 双重检查
Object value = redisTemplate.opsForValue().get(key);
if (value != null) {
return value;
}

// 查询数据库
value = queryFromDatabase(id);
redisTemplate.opsForValue().set(key, value, 300, TimeUnit.SECONDS);
return value;
} finally {
redisTemplate.delete(lockKey);
}
} else {
// 等待后重试
Thread.sleep(50);
return getValue(key);
}

缓存雪崩

1
2
3
4
5
6
问题大量 Key 同时过期导致数据库压力骤增

解决方案
1. 设置不同的过期时间
2. 使用多级缓存
3. 限流降级
1
2
3
// 设置随机过期时间
long expireTime = baseExpireTime + new Random().nextInt(randomRange);
redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);

报错处理

💗💗 Redis 报错OOM command not allowed

1
2
3
4
5
6
7
8
9
10
11
错误信息
OOM command not allowed when used memory > 'maxmemory'

错误原因
内存达到上限

解决方案
1. 增加 maxmemory
2. 配置内存淘汰策略
3. 清理无用数据
4. 优化数据结构

💗💗 Redis 报错NOAUTH Authentication required

1
2
3
4
5
6
7
8
9
10
错误信息
NOAUTH Authentication required

错误原因
需要密码认证

解决方案
1. 配置密码requirepass your_password
2. 客户端认证AUTH your_password
3. 检查配置文件

💗💗 Redis 报错MISCONF Errors writing to the AOF file

1
2
3
4
5
6
7
8
9
10
11
错误信息
MISCONF Errors writing to the AOF file

错误原因
AOF 文件写入失败

解决方案
1. 检查磁盘空间
2. 检查文件权限
3. 关闭 AOFCONFIG SET appendonly no
4. 修复 AOFredis-check-aof --fix appendonly.aof

学习资源

  • 视频
    • 尚硅谷 - 2021 最新 Redis 6 入门到精通教程https://www.bilibili.com/video/BV1Rv41177Af
  • 官方文档
    • Redis 命令参考http://redisdoc.com/
    • Redis 官方文档https://redis.io/documentation
    • Redis GitHubhttps://github.com/redis/redis
  • 书籍
    • Redis 设计与实现黄健宏著
    • Redis 实战Josiah L. Carlson 著
  • 教程
    • Redis 入门教程https://www.runoob.com/redis/redis-tutorial.html
    • Redis 官方教程https://redis.io/docs/
  • 工具
    • Redis 在线练习https://try.redis.io/
    • Redis Desktop Manager可视化管理工具
    • Another Redis Desktop Manager免费开源工具
    • RedisInsight官方可视化工具
  • 社区
    • Redis 中文社区http://www.redis.cn/
    • Stack Overflow Redis 标签https://stackoverflow.com/questions/tagged/redis