Redis配置文件,也就是redis.conf,是每个用Redis的人都绕不开、但又常常没仔细钻研的文件。很多人第一次接触Redis,是apt install redis-server或者Docker一把梭,拿到手就能跑,默认配置用了一年也没出事。直到某天内存被打满、数据莫名其妙丢了一部分、或者Redis被公网扫描进来写了个定时任务,才回过头来研究这个几百行的配置文件。我在帮团队排查线上Redis问题时发现,十次事故里至少有七次,根因都能追溯到某个配置项没设置对,或者是配置改了但没真正生效。这篇文章我就把redis.conf里最常踩坑、最影响业务的核心配置项全部拆开讲一遍,从加载机制到内存、持久化、安全、慢查询,再到一份可以直接抄作业的配置模板,希望能帮你少走半年弯路。
1. 配置加载机制:你的Redis到底读的是哪一份配置文件
1.1 不带任何参数启动时,默认配置从哪来
很多人有这样一个误区:以为Redis启动时一定会去读redis.conf,其实不一定。你用redis-server直接启动,Redis用的是编译时写死的默认配置,根本不会自动找某个配置文件来读。这个默认配置的值是多少,可以通过CONFIG GET *查看,比如默认maxmemory 0、databases 16、save 3600 1这些都是内置默认值,跟磁盘上的任何文件都没有关系。
如果你用apt或yum安装的Redis,包管理器会在/etc/redis/redis.conf放一份配置文件,并且注册为systemd服务。服务启动命令通常会带上配置文件路径,比如redis-server /etc/redis/redis.conf。如果你是自己编译安装的Redis,默认配置文件就在源码目录下的redis.conf,需要你手动复制到/usr/local/etc/或者其他目录再指定加载。Docker方式运行时要特别注意:官方镜像里默认是没有redis.conf的,如果你不把宿主机上的配置文件挂载进去,容器内完全是默认配置启动。
判断当前进程到底有没有读配置文件,最简单的办法就是看启动命令。执行ps -ef | grep redis,如果redis-server后面跟着文件路径,说明是通过配置文件启动的;如果只看到一个redis-server *:6379,那很可能就是默认配置硬跑的。
1.2 启动参数、配置文件、CONFIG SET 三者的优先级
Redis的配置来源其实有三个层级:启动命令行参数、配置文件、运行时CONFIG SET命令。这三者的生效优先级是:命令行参数 > 配置文件 > 内置默认值,而CONFIG SET属于运行时动态修改,不重启的话优先级最高,一旦重启就丢失,回到启动时加载的值。
有个非常实用的技巧:命令行参数可以临时覆盖配置文件里的值,适合快速调试。比如配置文件里设置了port 6379,但你临时想用6380跑一个实例,不需要改配置文件,直接执行:
redis-server /etc/redis/redis.conf --port 6380启动后端口就会是6380。这时你如果在客户端执行CONFIG GET port,看到的结果也是6380,但磁盘上的redis.conf并没有被修改。这种机制方便是方便,但也很容易坑人:你改完配置文件忘了重启,或者启动脚本里带了某个--xxx参数,重启后跟你预期不一致,完全找不到原因。我曾经排查过一台机器上Redis端口到了6380但配置文件里明明是6379的情况,最后发现是systemd的ExecStart那行末尾多了个参数。
1.3 验证当前生效配置:CONFIG GET 的正确用法
不管你是通过什么方式改的配置,最终以运行进程为准。查真实生效配置的方式是进入redis-cli,执行:
redis-cli CONFIG GET maxmemory CONFIG GET *CONFIG GET *会把当前所有生效配置全打出来,大概有两百多项。不要用cat redis.conf来判断实际生效值,这个文件只是启动时的输入,不能反映运行状态。我在实战中经常碰到同事拿着配置文件跟我说"我明明改成2GB了啊",结果CONFIG GET maxmemory显示0,再一看,改的文件跟进程加载的文件压根不是同一个路径。
提示:排查配置问题时,第一步永远是
CONFIG GET查看进程内的真实值,第二步才是打开配置文件对比,不要跳步。
2. 内存管理配置:maxmemory和淘汰策略决定Redis的下限
2.1 maxmemory用默认值等于裸奔
Redis在64位系统上,maxmemory的默认值是0,意思是不限制内存使用。这个默认值设计的初衷是保证Redis能存尽量多的数据,但放到生产环境就是一个定时炸弹。Redis是纯内存数据库,数据全在内存里,如果不设置上限,写入量上来之后内存会被吃满,操作系统开始使用Swap,性能急剧下降,甚至触发OOM Killer直接把Redis进程杀掉。更麻烦的是,如果部署在同一台机器上的还有别的进程,Redis会把整台机器的内存都吃干。
设置maxmemory之前,先要算清楚机器内存怎么分配。假设机器内存16GB,Redis数据占10GB,RDB持久化或者AOF重写时会有子进程利用Copy-On-Write机制,可能额外占用数据量一定比例的内存,通常建议预留数据量的20%到30%。再加上操作系统自身的开销,比较稳妥的做法是maxmemory设为物理内存的60%到70%,比如16GB机器设maxmemory 10gb。
内存压力可以这样检查:
redis-cli INFO memory重点关注used_memory、used_memory_rss和mem_fragmentation_ratio。used_memory是Redis实际使用的内存,used_memory_rss是操作系统视角下进程占用的内存。如果mem_fragmentation_ratio大于1.5,说明内存碎片率偏高,考虑重启或者开启activedefrag yes。
2.2 maxmemory-policy六种策略怎么选
内存达到maxmemory之后,Redis会根据maxmemory-policy决定怎么处理新写入。这个参数特别重要,默认值是noeviction,意思是不淘汰任何数据,新写入直接报错。对于缓存场景来说,默认策略几乎是错误选项:缓存不淘汰数据,反而让业务写入全部失败。
六个策略的适用场景我用实际经验总结如下:
| 策略 | 淘汰范围 | 适用场景 | 风险点 |
|---|---|---|---|
noeviction | 不淘汰 | 持久化存储语义的少量数据 | 写满后写入失败 |
allkeys-lru | 所有key | 纯缓存,所有数据都可牺牲 | 未设置过期时间的key也会被淘汰 |
volatile-lru | 仅设了过期时间的key | 缓存+持久化混合 | 如果没设过期时间的key多,可能淘汰不掉导致写入失败 |
allkeys-lfu | 所有key,按访问频率 | 访问热点分明的缓存 | 冷门key会被很快淘汰 |
volatile-lfu | 设了过期时间的key,按访问频率 | 热点数据设了expire | 访问频率低但重要的key可能被淘汰 |
volatile-random | 随机淘汰设了过期时间的key | 对淘汰谁无所谓,只求不报错 | 随机性会导致不可预期 |
最常用的是allkeys-lru和volatile-lru。缓存场景直接用allkeys-lru;业务数据中只有一部分key适合过期、其余需要长期保留,用volatile-lru。这里有个很多人没注意的细节:volatile-lru只会淘汰设了TTL的key,如果所有key都没设过期时间,内存写满后Redis依然会拒绝写入。所以volatile-*系列策略要求业务方规范地设置过期时间。
有人会问,allkeys-lru是不是会把原本不该淘汰的key淘汰掉?会。如果缓存里混着需要长期保存的数据,就别用allkeys-lru。我们线上就曾经出现过一个事故:用allkeys-lru做缓存,但延迟队列的任务key也被缓存进去了,队列积压时这些低温key全部被Redis优先淘汰,消费者拉不到数据。所以选策略之前,先把自己的数据按"可牺牲/不可牺牲"分个类。
2.3 配合内存配置的实用建议
第一个建议:缓存场景下给key都要设过期时间,让volatile-lru有淘汰的对象。第二个建议:修改淘汰策略时,不用重启Redis,运行中直接CONFIG SET maxmemory-policy allkeys-lru,临时应急非常方便。第三个建议:监控不能只看used_memory,还要关注evicted_keys这个指标,如果这个数字持续增长,说明淘汰事件很多,缓存命中率可能受影响。
redis-cli INFO stats | grep evicted_keys如果发现evicted_keys飙升,可以适当调大maxmemory,或者检查业务是否有一次性大量写入的批量任务,把缓存打爆了。数据类型的差异也在这里体现:同样存储100万条数据,如果都是Short String,可能只占100MB;如果是Hash且field很多,占用会远超预期。评估容量时最好按实际的key结构和数据长度做压测,不要拍脑袋估算。
3. 持久化配置:RDB、AOF和混合持久化的场景化选择
3.1 RDB:save参数不是写得越勤越好
RDB是Redis默认开启的持久化方式,通过快照的形式把内存数据写到磁盘。redis.conf中默认有三条触发规则:
save 3600 1 save 300 100 save 60 10000意思分别是:3600秒内至少有1个key变化就做一次快照;300秒内至少有100个key变化就做一次快照;60秒内至少有10000个key变化就做一次快照。这三个条件是或的关系,谁先满足谁触发。这里的核心思路是:变化越频繁,快照间隔越短,在数据安全性和磁盘IO之间取平衡。
但RDB有一个天然缺陷:每次快照之间的数据如果丢了,是没法恢复的。假设save 3600 1,上一小时做了快照,这一个小时写入了大量数据,Redis突然宕机,这一个小时的数据就全没了。所以RDB适合对数据丢失不敏感的场景,比如纯缓存,或者允许从上游重新拉取数据的场景。
save参数不要调得太激进。比如你改成save 60 1,看起来数据更安全了,但每60秒就fork一个子进程做全量快照,大数据量下磁盘IO和内存都会突然飙高。我一个项目就遇到过:key有二三十GB,save 300 100本来好好的,非要改成save 60 1,结果每60秒一次全量快照,磁盘被打满,主从复制也出现延迟。后来改回默认值才稳定。
3.2 AOF:appendfsync三种模式如何取舍
AOF是追加写日志,记录每次写命令,恢复时回放日志。它的可靠性取决于appendfsync策略,有三种选择:
| 参数值 | 行为 | 可靠性 | 性能损耗 |
|---|---|---|---|
always | 每个写命令都fsync到磁盘 | 最高,最多丢一个命令 | 最大,吞吐量明显下降 |
everysec | 每秒fsync一次 | 中高,最多丢1秒数据 | 小,推荐 |
no | 由操作系统决定何时刷盘 | 最低,可能丢较多数据 | 最小 |
绝大多数生产环境选everysec就够了,Redis官方文档也推荐这个值。always看起来很安全,但实测下吞吐量会下降一个量级,尤其是写并发高的场景,基本不推荐。no的性能和风险差距都不大,也不建议,因为崩溃时丢失的数据量不可控。
开启AOF的方式是在配置文件里:
appendonly yes appendfilename "appendonly.aof" appendfsync everysec这里有个常见的疑问:appendonly no时Redis还会生成RDB文件,但数据持久化只靠RDB;一旦appendonly yes,Redis重启时会优先加载AOF文件来恢复数据,AOF文件不存在时才加载RDB。
3.3 混合持久化和AOF重写条件
Redis 4.0之后引入了混合持久化,配置文件对应参数是aof-use-rdb-preamble yes。开启后,AOF文件的前半部分是RDB格式的二进制快照,后半部分是增量命令日志。这样做的收益是重启恢复速度比纯AOF快得多,因为直接加载RDB快照再回放少量增量命令即可,同时仍然保留AOF的数据安全性。建议新项目全部开启,老项目升级时也可以打开。
AOF文件会持续增长,Redis有自己的重写机制,配置文件里控制的是:
auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb含义是:当AOF文件体积比上一次重写后的体积增长了100%,并且当前文件超过64MB,才触发一次自动重写。重写会fork子进程,生成一个新的精简AOF文件。如果磁盘空间紧张,可以把min-size调小、percentage调低,但重写期间IO会有短暂波动,这个要根据业务低峰期来权衡。
AOF文件如果因为异常崩溃出现半截写入,Redis默认会尝试截断尾部不完整的命令并启动。这个行为由aof-load-truncated yes控制,保持默认即可。如果Redis启动不了,报Bad file format之类的错误,先备份原AOF文件,再用redis-check-aof --fix修复,不要直接删文件,能救一点是一点。
4. 网络与安全配置:bind、protected-mode、requirepass的黄金三角
4.1 bind和protected-mode:它们究竟在防什么
bind和protected-mode这两个参数配合使用,决定了谁能连上你的Redis。默认配置是:
bind 127.0.0.1 -::1 protected-mode yes意思很明确:只允许本机的回环地址连接。但很多人拿到Redis第一步就改成bind 0.0.0.0,让所有网卡都能访问,然后又不设置密码,这就等于把Redis裸奔在公网上。Redis本身没有设计鉴权加密层,2.8版本之后才有了requirepass,但默认是关闭的。
protected-mode yes的作用是这样的:当没有显式配置bind、也没有设置requirepass时,Redis只接受回环地址连接;如果检测到你显式配置了bind某个IP,就不再保护,所有来源都能访问。也就是说,你一旦把bind改成0.0.0.0,等于告诉Redis"我允许所有网卡监听外部连接",此时如果没有密码,公网扫描到6379端口就能直接登录操作数据。
我在安全排查时看到太多这样的案例:bind 0.0.0.0加protected-mode yes加无密码,Redis被入侵后被人写入SSH公钥或挖矿脚本。安全的做法是,明确Redis只对哪些内网IP提供服务,然后:
bind 192.168.1.100 127.0.0.1 protected-mode yes requirepass 你的强密码如果Redis只给本机应用服务,最稳妥的配置就是保持bind 127.0.0.1不变,不要碰0.0.0.0。
4.2 requirepass:设密码容易,但别用命令行传
requirepass是Redis实例级别的访问密码。配置方式:
requirepass YourStrongPass配置之后,客户端连接时要用AUTH YourStrongPass认证,否则Redis返回NOAUTH Authentication required。很多人为了方便,会用redis-cli -a YourStrongPass来连接,但这有个风险:命令行的-a参数会被进程列表记录下来,任何人都能看到密码。更安全的方式是环境变量REDISCLI_AUTH,或者在交互模式下先连接再AUTH。
高版本的Redis还有ACL机制,可以为不同用户分配不同权限和可访问的key范围。如果团队里有多个人、多个应用共用一套Redis,建议用ACL代替一把梭的requirepass,控制粒度更细。
4.3 Docker部署下的网络配置误区
Docker跑Redis踩坑频率最高的就是端口映射和bind冲突。假如你在宿主机上执行:
docker run -p 6379:6379 redis宿主机外部连接6379时,会转发到容器内的6379端口。但容器里Redis默认bind 127.0.0.1,这表示它只监听容器内部的回环地址,宿主机访问过来时数据包到达的是容器的eth0网卡,连接会被拒绝。所以Docker部署时必须显式设置:
bind 0.0.0.0 protected-mode no requirepass 强密码这里注意,Docker的端口映射不是Redis进程主动发起的连接,它是网络层的转发,所以bind 127.0.0.1会把所有映射过来的请求拒之门外。安全上,Docker容器内的Redis本来就不该让公网直连,正确做法是用Docker内部网络,只让需要访问Redis的容器通过服务名来连接,宿主机对外不暴露6379端口。
如果你用docker run时加了-v /path/redis.conf:/etc/redis/redis.conf,还要确认启动命令是通过redis-server /etc/redis/redis.conf加载的,否则挂载进去的文件也不会被读取。很多人挂载了配置文件但Redis启动时压根没指定路径,导致怎么改都不生效,这个问题我在线下帮人排查过很多次。
5. 日志、慢查询与动态调优:配置出问题后你靠什么定位
5.1 日志级别和日志文件:别等出事才想起看日志
Redis日志默认输出到标准输出,如果daemonize yes后台运行且没有配置logfile,日志会被丢弃。正确的配置是:
daemonize yes pidfile /var/run/redis.pid logfile /var/log/redis/redis.log loglevel noticeloglevel有四种:debug、verbose、notice、warning。默认notice会记录启动、关闭、持久化、主从切换等关键事件。排查问题时临时切到debug可以看到每个命令的执行细节,代价是日志量极大,用完记得改回来。
日志里常出现的几个关键信息要能看懂:Saving the final RDB snapshot说明RDB快照执行了;Background AOF rewrite finished successfully说明AOF重写完成;Can't save in background: fork: Cannot allocate memory说明fork子进程时内存不足,多半是maxmemory和系统内存设置有问题;Accepted 127.0.0.1:xxxx表示新连接接入,如果出现大量陌生IP的连接记录,就要警惕是不是被扫了。
5.2 慢查询日志:定位线上延迟的第一把尺子
慢查询日志是Redis排查性能问题最高效的入口。配置文件里:
slowlog-log-slower-than 10000 slowlog-max-len 128slowlog-log-slower-than单位是微秒,10000微秒等于10毫秒。意思是执行时间超过10毫秒的命令会记录到慢查询日志。slowlog-max-len是日志最大条数,Redis的慢查询日志存在内存里,不会写到磁盘,重启就清空。查看方式:
redis-cli SLOWLOG GET 10在实际环境里,如果发现大量慢查询是KEYS命令,那基本可以断定是业务方误用了KEYS做模糊匹配。KEYS在key数量大时会阻塞Redis单线程,阻塞期间所有命令都排队,整个实例的延迟指标都会飙升。正确的替代方案是用SCAN命令分批遍历。如果你的慢查询里出现DEL删除大key,也很危险,Redis删除一个包含几百万元素的集合时会阻塞服务,可以考虑用UNLINK异步删除。
5.3 CONFIG SET与CONFIG REWRITE:运行时改配置的正确姿势
Redis最方便的一点是几乎所有的配置都能在运行期动态修改,不需要重启。CONFIG SET改的是内存里的值,但不会自动写回配置文件。为了让修改在重启后依然生效,需要执行:
redis-cli CONFIG SET maxmemory 2gb redis-cli CONFIG REWRITECONFIG REWRITE会把当前内存中生效的配置跟配置文件比对,并自动重写配置文件。这个命令的约束是:如果配置文件里某些参数和内存值不一致,REWRITE会把内存值写进去。所以调整配置的顺序应该是:先CONFIG SET让线上立刻生效,观察一段时间确认没问题,再CONFIG REWRITE持久化到磁盘。如果先改文件再重启,也行,但要承担一个风险:如果新配置有问题,重启后服务可能直接起不来,而且在业务高峰期重启Redis是尽量避免的事。
注意:
CONFIG REWRITE要求原配置文件对Redis进程有写权限,如果配置文件是root所有且Redis以普通用户运行,REWRITE会报错,先调整文件权限再执行。
6. 一份可以直接抄的redis.conf核心配置模板
下面这份配置是我在多个项目中沉淀下来的基础模板,不是官方默认值,也不是为了跑Demo用的,而是综合了安全、性能、可维护性的通用配置。你可以根据实际机器配置调整参数,但结构可以直接用。
# 网络与访问控制 bind 127.0.0.1 protected-mode yes port 6379 timeout 0 tcp-keepalive 300 # 访问认证(强烈建议开启) requirepass your-strong-password # 守护进程与 PID daemonize yes pidfile /var/run/redis.pid supervised no # 日志 loglevel notice logfile "/var/log/redis/redis.log" # 内存上限与淘汰策略 maxmemory 10gb maxmemory-policy allkeys-lru maxmemory-samples 5 # RDB 持久化 save 3600 1 save 300 100 save 60 10000 dbfilename dump.rdb dir /var/lib/redis # AOF 持久化与混合持久化 appendonly yes appendfilename "appendonly.aof" appendfsync everysec aof-use-rdb-preamble yes auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes # 慢查询 slowlog-log-slower-than 10000 slowlog-max-len 128 # 客户端连接数 maxclients 10000这份模板有几个细节需要说明一下:
timeout 0表示连接空闲多久后关闭,0表示不关闭。如果业务方经常有闲置连接占着不释放,可以设置timeout 300,让空闲5分钟以上的连接被Redis切断。但要注意:如果客户端有连接池且没做好重连机制,服务端主动断开会造成客户端一堆异常,需要先确认客户端具备自动重连能力。maxmemory-samples 5是LRU/LFU算法的采样数。Redis的LRU不是全量精确计算,而是随机采几个key来决定淘汰谁,采样数越大计算结果越精确,但也更消耗CPU。默认5是平衡点。maxclients 10000要根据ulimit -n来定,如果系统文件描述符上限只有1024,Redis最多连接数也到不了10000。修改连接数前先执行ulimit -n查看上限。appendonly yes和混合持久化同时开启后,AOF文件会比纯日志模式更小,恢复更快。如果redis版本低于4.0,aof-use-rdb-preamble这个参数可能不存在,需要升级。
这份模板我并不是建议你直接照抄所有值,而是参考结构和考量方式。比如maxmemory设多少,取决于你的机器内存和业务数据量;比如缓存和持久化混合业务,maxmemory-policy要改成volatile-lru。配置文件的调优本质是Trade-off的决策,不存在放之四海皆准的值。
我看过很多团队在Redis配置文件上走过同一条弯路:默认配置用起来很爽,出问题之后一头雾水。实际上redis.conf的每个配置项都可以在官方文档和CONFIG GET的帮助下搞明白,真正难的是知道自己业务需要什么:数据能不能丢、内存上限多少、哪些客户端需要访问、发生故障时你希望Redis怎么表现。把这些业务决策理清了,配置文件怎么填就是顺理成章的事。所以我最后想说的是,别把配置文件当成一个"改了不报错就行"的东西,它是你Redis实例的体检报告和遗嘱——现在不重视,出问题时它决定的不是Redis的命运,而是你的业务。