我给 Redis 7 设了 maxmemory 2gb,跑压测期望内存到上限后自动 LRU 淘汰老 key。
10 分钟后 docker stats:
erlang
CONTAINER MEM USAGE LIMIT
redis 5.2GiB / 8GiB 64.79%
更离谱的是 CONFIG REWRITE 明明返回 OK,重启容器后配置全丢。查了三小时,发现不是 Redis 的 bug,是三个设计默认值叠在一起把我骗了。
坑 1:CONFIG SET 只改运行时,不写文件
我以为 5 分钟能搞定的事:
sql
docker exec -it redis redis-cli
CONFIG SET maxmemory 2gb
CONFIG SET maxmemory-policy allkeys-lru
CONFIG REWRITE
回车,看着没毛病。重启容器后查:
ruby
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "0"
0?我刚明明设了 2gb,而且也 REWRITE 了。
盯着这行看了一会儿才反应过来:CONFIG SET 改的是运行时内存中的配置 ,不持久化到文件。要持久化得靠 CONFIG REWRITE 或者手动改 redis.conf。
但 REWRITE 我也执行了啊。
坑 2:CONFIG REWRITE 返回 OK,但静默失败
进容器看 conf 文件:
bash
docker exec -it redis cat /usr/local/etc/redis/redis.conf | grep maxmemory
maxmemory 2gb
maxmemory-policy allkeys-lru
文件里明明写着 2gb,但 CONFIG GET 返回 0。绕了一圈才想通:这个 redis.conf 是从宿主机 bind mount 进容器的,容器对它有读权限,但 REWRITE 不一定能写回去。
宿主机上的文件 owner 是我自己,容器里的 redis 进程是 UID 999,跨过 mount 直接写 host 路径,权限不对。
但 CONFIG REWRITE 命令也没报错。Redis 文档里说:
If REWRITE fails (e.g. cannot write to the file), Redis returns an error.
我手动跑了一遍:
ruby
127.0.0.1:6379> CONFIG REWRITE
OK
返回 OK,文件实际没变。翻了一圈 issue,发现 Redis 在某些 mount 场景下会把"找不到原 conf 路径"当成"没用 conf file 启动",直接默默吞掉 REWRITE。具体什么场景我没完全摸清,反正这次它确实没写成,但也确实不报错。
OK 不等于成功。这是排查时最耽误时间的地方。
坑 3:maxmemory-policy 默认是 noeviction(不是 LRU)
第二天我把 conf 文件直接在 host 上改对,docker compose restart 后 CONFIG GET 也对了:
ruby
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "2147483648"
看着 OK。跑压测,内存涨到 2gb 后 Redis 没驱逐 key,直接抛 OOM:
bash
OOM command not allowed when used memory > 'maxmemory'.
ruby
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
我之前 CONFIG SET 设的 allkeys-lru,容器重启之后也丢了。redis.conf 文件里我没加 maxmemory-policy allkeys-lru,默认就是 noeviction ------ 达到内存上限直接拒绝写,不驱逐任何 key。
我以为"设了 maxmemory 就自动 LRU"。错。maxmemory 限制内存,maxmemory-policy 决定到达上限怎么办。两个独立配置,默认值是"啥也不淘汰,直接拒绝"。
三个坑叠加:排查时互相掩盖
每一个单独都好查。三个堆在一起,debug 路径变成:先怀疑压测脚本 → 再怀疑 Docker 内存 limit → 最后才回到 Redis 自己。
表格
| 你看到的 | 你以为的 | 实际发生的 |
|---|---|---|
CONFIG SET maxmemory 2gb |
配置已生效 | 只改了运行时,重启就丢 |
CONFIG REWRITE 返回 OK |
已写回文件 | mount 权限问题,静默失败 |
| 内存涨到 2gb | 开始 LRU 淘汰 | noeviction 默认拒绝写入,直接 OOM |
单看任何一个都好查。三个叠在一起,你会先怀疑压测脚本、再怀疑 Docker、最后才回到 Redis。
现在 conf 长啥样
就四行,跟容器一起初始化,运行时不再用 CONFIG SET 改:
perl
# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
appendonly no
save ""
appendonly no + save "" 是把 AOF 和 RDB 都关掉,纯当缓存用。学习环境不需要持久化,关掉省 IO 也省排查路径。生产环境另说,但单机搞开发没必要扛着持久化跑。
原则:配置文件 single source of truth,运行时命令只用来临时调试,不信任 CONFIG REWRITE。
每加一个 Redis 实例,conf 复制这四行,改 maxmemory 大小,完事。
下次起 Redis 直接复制
单机学习环境的最小可用配置:
perl
maxmemory 2gb
maxmemory-policy allkeys-lru
appendonly no
save ""
下次配 Redis,先把 maxmemory-policy 显式写进 conf,别相信默认值。这是单字符串就能搞定的事,但默认值挺害人。
CONFIG REWRITE 我现在直接当不存在。