Redis(Remote Dictionary Server)是一个开源的高性能内存数据存储系统,最初由 Salvatore Sanfilippo(antirez)于 2009 年创建。它不仅仅是简单的 Key-Value 存储,还支持 String、List、Hash、Set、Sorted Set、Stream、HyperLogLog、Bitmap 等丰富的数据结构。
Redis 以极致的性能著称 — 纯内存操作、单线程事件循环(避免锁开销)、I/O 多路复用,使其在单节点上即可达到 10 万+ QPS。它被广泛用于缓存、会话管理、排行榜、消息队列、分布式锁、实时分析等场景,是现代互联网架构中不可或缺的基础组件。
| 项目信息 | 详情 |
|---|---|
| 开发团队 | Redis Ltd(前 Redis Labs) |
| GitHub Stars | 68,000+ |
| 官方网站 | redis.io |
| 开源协议 | BSD-3-Clause(Redis 7.4+ 改为 RSALv2 / SSPLv1 双协议) |
| 核心技术 | C 语言(ANSI C99) |
| 存储模式 | 纯内存 + 可选持久化(RDB / AOF) |
| 部署方式 | 源码编译 / 包管理器 / Docker / Redis Stack |
| 对比维度 | Redis | Memcached | KeyDB | Dragonfly |
|---|---|---|---|---|
| 数据结构 | String/List/Hash/Set/ZSet/Stream 等 10+ | 仅 Key-Value(字符串) | 兼容 Redis 全部数据结构 | 兼容 Redis 全部数据结构 |
| 持久化 | RDB + AOF + 混合模式 | 不支持 | RDB + AOF | RDB 快照 |
| 多线程 | I/O 多线程(6.0+),命令仍单线程 | 多线程 | 多线程执行 | 多线程无共享架构 |
| 集群 | Redis Cluster(原生支持) | 客户端分片 | 兼容 Redis Cluster + Active Replication | 单节点(集群开发中) |
| 内存效率 | 中等(jemalloc) | slab 分配器 | 与 Redis 类似 | 显著更优(Dash 结构) |
| 生态与社区 | 最成熟,客户端覆盖全语言 | 成熟但功能停滞 | 兼容 Redis 客户端 | 兼容 Redis 客户端,社区较新 |
| Lua 脚本 | 支持(Lua + Functions) | 不支持 | 支持 | 支持 |
| 适用场景 | 通用缓存 / 数据结构服务器 | 纯缓存(简单 KV) | 需要多线程的 Redis 兼容场景 | 需要极致内存效率的 Redis 兼容场景 |
# 安装依赖 sudo apt update && sudo apt install -y build-essential tcl # 下载并编译 wget https://download.redis.io/redis-stable.tar.gz tar xzf redis-stable.tar.gz cd redis-stable make make test # 可选,运行测试 sudo make install # 启动 Redis redis-server # 验证 redis-cli ping # 返回 PONG 表示成功
/usr/local/bin/。可以通过 make PREFIX=/your/path install 自定义安装路径。# 添加官方 APT 仓库 curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list sudo apt update sudo apt install redisCentOS / RHEL:
sudo yum install epel-release sudo yum install redis sudo systemctl enable --now redismacOS:
brew install redis brew services start redis
# 快速启动 docker run -d --name redis \ -p 6379:6379 \ -v redis_data:/data \ redis:7-alpine # 连接测试 docker exec -it redis redis-cli pingDocker Compose 部署(生产环境推荐):
requirepass 密码、禁用 CONFIG 命令、绑定内网 IP,避免 Redis 未授权访问漏洞。# Docker 安装 Redis Stack docker run -d --name redis-stack \ -p 6379:6379 \ -p 8001:8001 \ redis/redis-stack:latest # 6379 → Redis 服务端口 # 8001 → RedisInsight Web UI
# 基本操作 SET user:1:name "张三" GET user:1:name # "张三" # 原子计数器 SET page:views 0 INCR page:views # 1 INCRBY page:views 100 # 101 # 设置过期时间 SET session:abc123 "user_data" EX 3600 # 1 小时后过期 # 分布式锁 SET lock:order:123 "owner_id" NX EX 30 # 仅当不存在时设置,30 秒过期
# 从左侧推入 LPUSH queue:tasks "task1" "task2" "task3" # 从右侧弹出(队列 FIFO) RPOP queue:tasks # "task1" # 阻塞弹出(消费者等待) BRPOP queue:tasks 30 # 等待最多 30 秒 # 范围查询 LRANGE queue:tasks 0 -1 # 获取所有元素 # 获取长度 LLEN queue:tasks
# 设置多个字段 HSET user:1 name "张三" age 28 email "zhangsan@example.com" # 获取单个字段 HGET user:1 name # "张三" # 获取所有字段 HGETALL user:1 # 字段原子计数 HINCRBY user:1 age 1 # 29 # 仅当字段不存在时设置 HSETNX user:1 created_at "2024-01-01"
hash-max-listpack-entries(默认 128)和 hash-max-listpack-value(默认 64 字节)控制。# 添加成员 SADD tags:article:1 "redis" "database" "cache" SADD tags:article:2 "redis" "nosql" "performance" # 判断成员是否存在 SISMEMBER tags:article:1 "redis" # 1 # 交集 — 两篇文章共同的标签 SINTER tags:article:1 tags:article:2 # "redis" # 随机返回成员(抽奖场景) SRANDMEMBER lottery:pool 3 # 弹出随机成员 SPOP lottery:pool
# 添加成员及分数 ZADD leaderboard 1500 "player:alice" ZADD leaderboard 2300 "player:bob" ZADD leaderboard 1800 "player:charlie" # 排行榜 Top 3(从高到低) ZREVRANGE leaderboard 0 2 WITHSCORES # 查询排名 ZREVRANK leaderboard "player:bob" # 0(第一名) # 增加分数 ZINCRBY leaderboard 500 "player:alice" # 范围查询 ZRANGEBYSCORE leaderboard 1000 2000 WITHSCORES
# 追加消息 XADD orders * user_id 1001 product "iPhone" amount 1 # 读取消息 XRANGE orders - + COUNT 10 # 创建消费者组 XGROUP CREATE orders group1 0 # 消费者读取(阻塞模式) XREADGROUP GROUP group1 consumer1 COUNT 1 BLOCK 5000 STREAMS orders > # 确认消息已处理 XACK orders group1 "1234567890-0"
# 添加元素 PFADD page:uv:2024-01-01 "user:1" "user:2" "user:3" "user:1" # 估算基数 PFCOUNT page:uv:2024-01-01 # 3 # 合并多天的 UV PFMERGE page:uv:week page:uv:2024-01-01 page:uv:2024-01-02
# 用户签到(第 0 天 = 1 月 1 日) SETBIT sign:user:1:2024 0 1 # 1月1日签到 SETBIT sign:user:1:2024 4 1 # 1月5日签到 # 查询某天是否签到 GETBIT sign:user:1:2024 0 # 1 # 统计本月签到天数 BITCOUNT sign:user:1:2024 0 3 # 前 4 个字节中 1 的个数 # 查找第一个签到日 BITPOS sign:user:1:2024 1 # 0
# 存储会话(Hash 结构) HSET session:abc123 user_id 1001 username "张三" role "admin" login_time "2024-01-01T10:00:00" EXPIRE session:abc123 1800 # 30 分钟过期 # 读取会话 HGETALL session:abc123 # 续期(每次请求时) EXPIRE session:abc123 1800 # 销毁会话(退出登录) DEL session:abc123
# 滑动窗口限流(Lua 脚本 — 原子操作)
# 限制每个 IP 每分钟最多 100 次请求
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
local count = redis.call('ZCARD', key)
if count < limit then
redis.call('ZADD', key, now, now .. math.random())
redis.call('EXPIRE', key, window / 1000)
return 1 -- 允许
else
return 0 -- 拒绝
end
# 更新玩家分数 ZADD game:leaderboard 15000 "player:alice" ZADD game:leaderboard 23000 "player:bob" ZADD game:leaderboard 18500 "player:charlie" # Top 10 排行榜 ZREVRANGE game:leaderboard 0 9 WITHSCORES # 查询某玩家排名(0-based) ZREVRANK game:leaderboard "player:alice" # 2 # 查询某玩家分数 ZSCORE game:leaderboard "player:alice" # 15000 # 查询某分数段的玩家 ZREVRANGEBYSCORE game:leaderboard 20000 10000 WITHSCORES LIMIT 0 10 # 增加分数(游戏得分累加) ZINCRBY game:leaderboard 2000 "player:alice"
# 终端 1 — 订阅频道 SUBSCRIBE chat:room:1 # 终端 2 — 发布消息 PUBLISH chat:room:1 "Hello, everyone!" # 模式订阅(通配符) PSUBSCRIBE chat:room:* # 查看活跃频道 PUBSUB CHANNELS chat:*
# 加锁(SET NX + 过期时间 — 原子操作)
SET lock:order:pay:123 "owner:server1:thread42" NX EX 30
# 释放锁(Lua 脚本确保只释放自己的锁)
-- KEYS[1] = lock key, ARGV[1] = owner id
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
# === 方案 A:List 实现简单队列 ===
# 生产者
LPUSH queue:emails '{"to":"a@b.com","subject":"Welcome"}'
# 消费者(阻塞式)
BRPOP queue:emails 0
# === 方案 B:Stream 实现可靠队列(推荐) ===
# 创建消费者组
XGROUP CREATE queue:orders $ MKSTREAM
# 生产者
XADD queue:orders * action "create" order_id 12345
# 消费者(消费者组模式)
XREADGROUP GROUP workers worker1 COUNT 10 BLOCK 5000 STREAMS queue:orders >
# 确认处理完成
XACK queue:orders workers "1234567890-0"
# 查看待确认消息(处理失败的)
XPENDING queue:orders workers
# PV 统计(String 计数器) INCR stats:page:/home:pv:2024-01-01 # UV 统计(HyperLogLog) PFADD stats:page:/home:uv:2024-01-01 "user:1001" PFCOUNT stats:page:/home:uv:2024-01-01 # 实时在线用户数(Bitmap) SETBIT online:users 1001 1 # 用户 1001 上线 SETBIT online:users 1001 0 # 用户 1001 下线 BITCOUNT online:users # 在线用户总数 # 热点商品排行(Sorted Set) ZINCRBY hot:products:hourly 1 "product:iphone15" ZREVRANGE hot:products:hourly 0 9 WITHSCORES
# 添加位置 GEOADD restaurants 116.403963 39.915119 "全聚德" GEOADD restaurants 116.407526 39.904030 "东来顺" GEOADD restaurants 121.473701 31.230416 "南翔馒头店" # 查询两点距离 GEODIST restaurants "全聚德" "东来顺" km # ≈1.2 km # 附近搜索(5 km 范围内的餐厅) GEOSEARCH restaurants FROMLONLAT 116.405 39.910 BYRADIUS 5 km ASC COUNT 10 WITHCOORD WITHDIST # 获取位置坐标 GEOPOS restaurants "全聚德"
# 创建索引(需要 Redis Stack)
FT.CREATE idx:products ON HASH PREFIX 1 product:
SCHEMA
name TEXT WEIGHT 5.0
description TEXT
price NUMERIC SORTABLE
category TAG
# 添加数据
HSET product:1 name "iPhone 15 Pro" description "Apple 最新旗舰手机" price 8999 category "手机"
HSET product:2 name "MacBook Pro M3" description "Apple 高性能笔记本" price 14999 category "笔记本"
# 全文搜索
FT.SEARCH idx:products "Apple 手机"
# 带过滤和排序
FT.SEARCH idx:products "@category:{手机}" SORTBY price ASC LIMIT 0 10
# 聚合查询
FT.AGGREGATE idx:products "*" GROUPBY 1 @category REDUCE COUNT 0 AS count
# 创建向量索引
FT.CREATE idx:docs ON HASH PREFIX 1 doc:
SCHEMA
content TEXT
embedding VECTOR FLAT 6
TYPE FLOAT32
DIM 768
DISTANCE_METRIC COSINE
# 存储文档和向量(向量为二进制格式)
HSET doc:1 content "Redis 是高性能内存数据库" embedding "\x00\x00..."
# KNN 向量搜索(查找最相似的 5 个文档)
FT.SEARCH idx:docs "*=>[KNN 5 @embedding $query_vec AS score]"
PARAMS 2 query_vec "\x00\x00..."
SORTBY score ASC
RETURN 2 content score
DIALECT 2
appendfsync everysec,在大多数场景下是性能与数据安全的最佳平衡点。同时定期将 RDB 快照备份到远程存储(S3 / OSS)。# 创建 6 节点集群(3 主 3 从) redis-cli --cluster create \ 192.168.1.1:6379 192.168.1.2:6379 192.168.1.3:6379 \ 192.168.1.4:6379 192.168.1.5:6379 192.168.1.6:6379 \ --cluster-replicas 1 # 查看集群状态 redis-cli -c cluster info redis-cli -c cluster nodes # 添加新节点 redis-cli --cluster add-node 192.168.1.7:6379 192.168.1.1:6379 # 迁移槽(重新分片) redis-cli --cluster reshard 192.168.1.1:6379
{user:1}.name)强制相关 key 落在同一个槽。WAIT 命令或使用 Redis Raft 等强一致方案。# Ubuntu / Debian sudo apt update && sudo apt install -y build-essential # CentOS / RHEL sudo yum groupinstall "Development Tools" # 清理后重新编译 cd redis-stable make distclean && make
# 查找占用进程 lsof -i :6379 # 或 ss -tlnp | grep 6379 # 终止占用进程 kill -9 <PID> # 或者修改 Redis 端口 redis-server --port 6380
# 移除旧容器 docker rm -f redis # 重新启动 docker run -d --name redis -p 6379:6379 redis:7-alpine # 如果是其他原因退出,查看日志 docker logs redis
# 临时生效 echo 1 > /proc/sys/vm/overcommit_memory # 永久生效 echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf sysctl -p
# 临时禁用 echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag # 永久禁用(添加到 rc.local 或 systemd service) # /etc/rc.local echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag
# 临时调整 echo 1024 > /proc/sys/net/core/somaxconn # 永久生效 echo "net.core.somaxconn = 1024" >> /etc/sysctl.conf sysctl -p
find / -name "redis.conf" 2>/dev/null
redis-stable/redis.conf
sudo cp redis.conf /etc/redis/redis.conf
redis-server /path/to/redis.conf
# 创建 redis 用户 sudo useradd -r -s /sbin/nologin redis # 设置目录权限 sudo chown -R redis:redis /var/lib/redis /var/log/redis /etc/redis # 使用 systemd 服务管理(已内置用户切换) sudo systemctl start redis
# 方案 1:清理后重新编译 make distclean make # 方案 2:使用 libc 分配器(不推荐生产环境) make MALLOC=libc
# 检查并修复权限 sudo mkdir -p /var/log/redis /var/lib/redis sudo chown redis:redis /var/log/redis /var/lib/redis # 查看详细错误 sudo journalctl -u redis -n 50 # 确认 redis.conf 中的路径配置正确 # logfile "/var/log/redis/redis-server.log" # dir "/var/lib/redis"
# 修改 redis.conf bind 0.0.0.0 # 或指定内网 IP:bind 192.168.1.100 # 同时关闭保护模式(需配合密码使用) protected-mode no # 重启 Redis sudo systemctl restart redis
requirepass),否则任何人都能访问你的 Redis!历史上大量 Redis 被黑事件都是因为未设置密码+绑定公网 IP。# 命令行连接时指定密码 redis-cli -a your-password # 或连接后认证 redis-cli AUTH your-password # 使用 URL 格式 redis-cli -u redis://:your-password@127.0.0.1:6379
redis.conf 中的 requirepass 设置
ACL LIST
AUTH username password
# 查看当前连接数 redis-cli info clients # 查看当前连接详情 redis-cli client list # 增大最大连接数(redis.conf) maxclients 50000 # 动态修改(无需重启) redis-cli CONFIG SET maxclients 50000 # 排查连接泄漏(未关闭的空闲连接) redis-cli client list | grep "idle=\|age="
ulimit -n(文件描述符限制)大于 maxclients + 32。telnet redis-host 6379
iptables -L -n | grep 6379
redis-cli info stats 查看 total_connections_received
redis-cli SLOWLOG GET 10
# 推荐方案:设置密码 requirepass your-strong-password # 或者同时做两件事(redis.conf) bind 192.168.1.100 # 绑定内网 IP requirepass your-password # 设置密码 protected-mode yes # 保持保护模式开启
# redis.conf 中配置 TLS tls-port 6380 tls-cert-file /path/to/redis.crt tls-key-file /path/to/redis.key tls-ca-cert-file /path/to/ca.crt # 客户端连接 redis-cli --tls --cert /path/to/client.crt --key /path/to/client.key --cacert /path/to/ca.crt -p 6380
# 查看当前用户权限 ACL WHOAMI ACL GETUSER <username> # 授予权限(管理员操作) ACL SETUSER myuser +config on ACL SAVE # 或修改 ACL 配置文件 # aclfile /etc/redis/users.acl
# Java (Jedis) 连接池配置建议 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(50); // 最大连接数 config.setMaxIdle(20); // 最大空闲连接 config.setMinIdle(5); // 最小空闲连接 config.setMaxWaitMillis(3000); // 等待超时 config.setTestOnBorrow(true); // 借用时测试连接排查方向:确保每次使用后归还连接(try-with-resources);适当增大连接池大小;检查是否有慢查询占用连接。
READONLY 命令
SLOWLOG GET 10
maxmemory 限制,且淘汰策略为 noeviction(默认):
# 查看内存使用情况 redis-cli info memory # 设置最大内存和淘汰策略 maxmemory 4gb maxmemory-policy allkeys-lru # 在所有 key 中 LRU 淘汰 # 常用淘汰策略: # volatile-lru — 仅对设置了过期时间的 key 进行 LRU 淘汰 # allkeys-lru — 对所有 key 进行 LRU 淘汰(推荐缓存场景) # volatile-ttl — 淘汰 TTL 最小的 key # allkeys-lfu — 对所有 key 进行 LFU 淘汰(Redis 4.0+,推荐) # noeviction — 不淘汰,内存满时拒绝写入(默认)
# 查看碎片率 redis-cli info memory | grep mem_fragmentation_ratio # Redis 4.0+ 开启自动碎片整理 CONFIG SET activedefrag yes # 调整碎片整理参数 CONFIG SET active-defrag-threshold-lower 10 # 碎片率 > 10% 时开始整理 CONFIG SET active-defrag-threshold-upper 100 # 碎片率 > 100% 时全力整理 CONFIG SET active-defrag-cycle-min 1 # 最小 CPU 占比 1% CONFIG SET active-defrag-cycle-max 25 # 最大 CPU 占比 25%
# 扫描大 Key redis-cli --bigkeys # 更精确的分析(Redis 4.0+) redis-cli --memkeys # 查看指定 key 的内存使用 redis-cli MEMORY USAGE mykey # 解决方案:拆分大 Key # 例:将一个 100 万元素的 Hash 拆分为 100 个子 Hash # user:data → user:data:0, user:data:1, ..., user:data:99 # 分桶算法:bucket = CRC32(field) % 100
DEL 命令!删除数百万元素的 Key 会阻塞 Redis 数秒。请使用 UNLINK(异步删除,Redis 4.0+)。# 确保 overcommit_memory = 1 echo 1 > /proc/sys/vm/overcommit_memory # 预留足够内存(建议 maxmemory 不超过物理内存的 50-60%) # 例:8GB 服务器,maxmemory 设为 4gb # 减少 fork 期间的写放大 # 禁用 THP echo never > /sys/kernel/mm/transparent_hugepage/enabled # 监控 COW 内存 redis-cli info persistence | grep rdb_last_cow_size
# 手动触发扫描(生产环境慎用) redis-cli SCAN 0 COUNT 10000 # 调优定期删除频率(redis.conf) hz 25 # 默认 10,增大可加快过期 key 回收(但增加 CPU) # 避免大量 key 同时过期 — 给 TTL 加随机偏移 # TTL = base_ttl + random(0, 300)
MEMORY USAGE key 查看单个 key 的实际内存
OBJECT ENCODING key 查看编码类型
u:1:n 比 user:1:name 省内存)
OBJECT HELP 了解编码切换条件
# 检查 Redis 进程是否使用 Swap cat /proc/$(pidof redis-server)/smaps | grep Swap # 检查系统 Swap 使用 free -h swapon --show # 解决方案 # 1. 增加物理内存或降低 maxmemory # 2. 降低 swappiness echo 1 > /proc/sys/vm/swappiness # 3. 生产环境建议完全禁用 Swap(确保内存充足) swapoff -a
# 查看是否被 OOM Kill dmesg | grep -i "oom\|kill" # 调低 Redis 进程的 OOM 分数(降低被杀优先级) echo -17 > /proc/$(pidof redis-server)/oom_adj # 根本解决方案 # 1. 设置合理的 maxmemory(留出操作系统和 fork 所需内存) # 2. 增加服务器内存 # 3. 使用淘汰策略而非 noeviction
# 使用 UNLINK 代替 DEL(异步删除) UNLINK large_key # 在 redis.conf 中开启 lazy-free lazyfree-lazy-eviction yes # 淘汰时异步释放 lazyfree-lazy-expire yes # 过期时异步释放 lazyfree-lazy-server-del yes # 内部删除时异步释放 lazyfree-lazy-user-del yes # DEL 命令也异步(Redis 6.0+) lazyfree-lazy-user-flush yes # FLUSHDB/FLUSHALL 异步(Redis 6.2+)
# 查看内存诊断信息 redis-cli MEMORY DOCTOR # 查看内存统计 redis-cli MEMORY STATS # 重置峰值统计 redis-cli CONFIG RESETSTAT # 如果碎片率高,参考 #22 开启碎片整理 # 如果是临时大数据操作导致,评估是否需要优化业务逻辑
# 临时允许写入(不推荐长期使用) CONFIG SET stop-writes-on-bgsave-error no # 排查 RDB 失败原因 # 1. 磁盘空间不足 df -h # 2. 权限问题 ls -la /data/dump.rdb chown redis:redis /data # 3. 内存不足无法 fork # 参考 #24 解决 fork 内存问题 # 手动触发 RDB 快照测试 redis-cli BGSAVE
# 1. 先备份 cp appendonly.aof appendonly.aof.bak # 2. 使用修复工具 redis-check-aof --fix appendonly.aof # 3. 对于 Redis 7+ 的 Multi-Part AOF redis-check-aof --fix appendonlydir/appendonly.aof.1.incr.aof # 4. 确认修复结果后重启 Redis redis-server /etc/redis/redis.conf
overcommit_memory = 1
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 200
BGREWRITEAOF
# 手动触发 AOF 重写 redis-cli BGREWRITEAOF # 检查 AOF 重写状态 redis-cli info persistence | grep aof # 调整自动重写阈值 CONFIG SET auto-aof-rewrite-percentage 100 CONFIG SET auto-aof-rewrite-min-size 64mb # 检查是否有大量写操作导致快速增长 redis-cli info stats | grep instantaneous_ops_per_sec
appendonly yes),优先加载 AOF
# 1. 临时关闭 AOF CONFIG SET appendonly no # 2. 重启 Redis(此时会加载 RDB) redis-cli SHUTDOWN redis-server /etc/redis/redis.conf # 3. 重新开启 AOF CONFIG SET appendonly yes BGREWRITEAOF
# 检查 RDB 文件完整性 redis-check-rdb /data/dump.rdb # 如果有备份,使用备份恢复 cp /backup/dump.rdb /data/dump.rdb # 如果没有备份但有 AOF,切换到 AOF 加载 # 修改 redis.conf:appendonly yes # 重启 Redis # 预防措施:开启 RDB 校验 rdbchecksum yes
# 排查磁盘 I/O iostat -x 1 # 解决方案 # 1. 使用 SSD 替换 HDD # 2. AOF 重写期间跳过 fsync(牺牲少量安全性) no-appendfsync-on-rewrite yes # 3. 降低 fsync 频率(如果可以接受更多数据丢失风险) appendfsync everysec # 不要用 always # 4. 将 AOF 目录放在高速磁盘上
# 查看详细原因 redis-cli info persistence | grep rdb_last_bgsave dmesg | tail -20 # 常见原因和解决 # 1. OOM Killer 杀掉子进程 → 增加内存或降低 maxmemory # 2. 磁盘空间不足 → 清理磁盘 # 3. /data 目录无写权限 → chown redis:redis /data # 重新触发 redis-cli BGSAVE
# 确认配置 CONFIG GET aof-use-rdb-preamble # 应返回 "yes" # 手动触发 AOF 重写使配置生效 BGREWRITEAOF # 验证:重写后的 AOF 文件开头是二进制 RDB 格式 # 可以用 file 命令检查 file appendonlydir/appendonly.aof.1.base.rdb
KEYS 命令遍历所有 key,时间复杂度 O(N),key 数量多时会阻塞 Redis:
# 禁止在生产环境使用 KEYS! # 使用 SCAN 替代(游标式迭代,不阻塞) SCAN 0 MATCH "user:*" COUNT 100 # 通过 ACL 禁用 KEYS 命令 ACL SETUSER default -keys # 或通过 rename-command 禁用 rename-command KEYS ""
KEYS *!同理,FLUSHALL、FLUSHDB、DEBUG 等危险命令也应禁用或重命名。# 配置慢查询阈值(微秒) CONFIG SET slowlog-log-slower-than 10000 # 10ms CONFIG SET slowlog-max-len 128 # 查看慢查询日志 SLOWLOG GET 10 # 查看慢查询数量 SLOWLOG LEN # 重置 SLOWLOG RESET # 常见慢查询原因: # 1. O(N) 命令操作大集合(HGETALL、SMEMBERS、LRANGE 0 -1) # 2. 大 Key 的 DEL 操作 # 3. Lua 脚本执行时间过长 # 4. SORT 命令排序大量数据
# 延迟基准测试 redis-cli --latency redis-cli --latency-history redis-cli --latency-dist # Redis 内置延迟监控(Redis 2.8.13+) CONFIG SET latency-monitor-threshold 5 # 记录 > 5ms 的事件 LATENCY LATEST LATENCY HISTORY event-name # 检查事件循环延迟 redis-cli DEBUG SLEEP 0 # 触发一次延迟检测 # 常见非命令延迟来源: # 1. fork(BGSAVE/BGREWRITEAOF) # 2. AOF fsync # 3. 操作系统 Swap # 4. 网络问题 # 5. 过期 key 的集中删除
# redis-cli 管道模式
cat commands.txt | redis-cli --pipe
# Python 示例
import redis
r = redis.Redis()
pipe = r.pipeline()
for i in range(10000):
pipe.set(f"key:{i}", f"value:{i}")
pipe.execute() # 一次性发送所有命令
# 注意事项:
# 1. Pipeline 不是原子操作(如需原子性用 MULTI/EXEC 或 Lua)
# 2. 单次 Pipeline 不要太大(建议 1000~10000 条)
# 3. Pipeline 中的命令结果在 execute() 后统一返回
# 发现热点 Key redis-cli --hotkeys # 需要开启 LFU 淘汰策略 redis-cli MONITOR # 实时监控(短时间使用,影响性能) # 解决方案 # 1. 本地缓存:在应用层增加 L1 缓存(如 Caffeine/Guava Cache) # 2. 读写分离:将读请求分发到多个从节点 # 3. Key 拆分:将热点 Key 拆分为多个子 Key(如 hotkey:0 ~ hotkey:9) # 读取时随机选择一个子 Key # 4. 使用 Redis Cluster 的 READONLY 命令让从节点分担读请求
# 方案 1:缓存空值 # 查询数据库为空时,在 Redis 中缓存空值 SET user:999999 "" EX 300 # 缓存空值 5 分钟 # 方案 2:布隆过滤器(Redis Stack / RedisBloom) # 将所有合法 ID 加入布隆过滤器 BF.ADD valid_users "user:1" BF.ADD valid_users "user:2" # 请求前先检查 BF.EXISTS valid_users "user:999999" # 0 → 不存在,直接拒绝 # 方案 3:请求参数校验 # 在接入层拦截非法请求(如负数 ID、超长字符串等)
# 基础 TTL + 随机偏移(0~300 秒)
import random
ttl = 3600 + random.randint(0, 300)
r.setex(f"cache:{key}", ttl, value)
2. 永不过期 + 异步更新:缓存不设 TTL,后台线程定期刷新
# 方案 1:互斥锁(分布式锁) # 缓存未命中时,先获取锁,再查数据库,再写缓存 SET lock:cache:hotkey "1" NX EX 10 # 获取锁成功 → 查询 DB → 写入缓存 → 释放锁 # 获取锁失败 → sleep 后重试读缓存 # 方案 2:逻辑过期 # 缓存中存储过期时间字段,实际不设 Redis TTL HSET cache:hotkey data "..." expire_at 1704067200 # 读取时判断 expire_at,过期则异步更新,返回旧数据 # 方案 3:预热 # 热点 key 过期前主动刷新
# 使用集群模式连接(自动处理重定向) redis-cli -c -h 192.168.1.1 -p 6379 # 客户端库需启用集群模式 # Python: redis.RedisCluster(...) # Java: JedisCluster / Lettuce cluster mode # 查看槽分配 redis-cli -c cluster slots # 如果频繁出现,检查集群是否正在 rebalance redis-cli -c cluster info
appendonlydir 目录
redis.conf 中部分配置项名称变更