我前阵子在一台全新的 MacBook 上从头装 Redis,目标很明确:装好之后让它安安静静在后台跑,重启电脑也不用管,开机自动起。本来以为就是brew install redis一条命令的事,结果一路趟过去,发现涉及的东西比想象中多——Homebrew 的服务管理、Redis 自身的守护进程配置、macOS 的 launchd 启动机制、plist 文件的写法,任何一环没对上,结果就是“装上了但没法用”或者“现在能用但重启就废”。
这篇文章我就把这套流程完整拆开讲,包括我一开始偷懒直接brew services start redis之后遇到的各种问题,以及最后用手写 plist 接管开机自启的完整过程。如果你也是 macOS 用户,想要一个开机自启、后台稳定运行的 Redis,这篇文章可以直接照抄。
1. 装之前的准备工作:Homebrew 与命令行环境
1.1 为什么要用 Homebrew 装 Redis,而不是源码编译
我见过不少人一上来就wgetRedis 源码包,然后make、make install一条龙。这路子不能说错,但在 macOS 上属于给自己找麻烦。源码编译的 Redis 默认只提供redis-server和redis-cli这些二进制,配置文件、日志目录、数据目录、launchd 启动脚本全都要自己手动安排。而 Homebrew 的 redis 公式把这些都处理好了,装完就有完整的目录结构、默认配置,甚至自带了homebrew.mxcl.redis.plist这个现成的自启动配置模板。
简单说,Homebrew 装 Redis 能让你把精力放在“怎么让它开机自启”上,而不是浪费在“编译报错缺依赖”“装完了连配置文件在哪都不知道”这种无关紧要的事情上。
1.2 安装 Xcode Command Line Tools 与 Homebrew
在 macOS 上装 Homebrew,前提是系统里有 Xcode Command Line Tools,它包含 git、clang 这些编译器工具链。装起来很简单,打开终端执行:
xcode-select --install系统会弹窗让你确认,等几分钟下载安装完就行。注意这一步依赖网络,而且有时候会卡在“正在下载”很久,多等一会,别频繁取消重试。
装完 Command Line Tools 之后再装 Homebrew。这里推荐用国内镜像源,因为官方源在国内环境下经常慢到怀疑人生。我这边实测中科大源比较稳定:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"如果你需要换镜像源,装完之后执行:
cd "$(brew --repo)" git remote set-url origin https://mirrors.ustc.edu.cn/brew.gitIntel 和 Apple Silicon 的 Homebrew 默认安装路径不一样,这直接影响后续 Redis 配置文件的路径。Apple Silicon 的 Mac 上 Homebrew 装在/opt/homebrew,Intel 的装在/usr/local。我这台是 M 系列芯片,所以后面出现的路径都以/opt/homebrew为准,Intel 用户把路径换一下就行。
1.3 用 brew install 安装 Redis 并验证
准备工作做完之后,安装本身反而最简单:
brew install redis这个过程会自动安装依赖,比如openssl、pkg-config这些。装完看一眼版本号确认一下:
redis-server --version redis-cli --version我这边装完显示的是 Redis 7.x,版本号不用太纠结,只要是 6.x 以上,本文的所有操作都适用。
到这里 Redis 已经装好了,但此刻它还只是个“装了但没跑”的状态。真正花时间的是接下来这部分:怎么改配置、怎么后台跑、怎么开机自启。
2. Redis 配置文件:从默认值到可后台运行
2.1 配置文件的默认位置与初次修改
Homebrew 装的 Redis,配置文件路径是:
- Apple Silicon:
/opt/homebrew/etc/redis.conf - Intel:
/usr/local/etc/redis.conf
用brew info redis也能看到这个路径。第一次打开这个文件,你可能会被里面的注释量吓到,密密麻麻全是英文注释。别慌,绝大多数配置保持默认就行,我们需要动的就那几个关键项。
在动手之前,推荐先备份一份原始配置:
cp /opt/homebrew/etc/redis.conf /opt/homebrew/etc/redis.conf.bak这个习惯我保持了很多年。改配置改到一半发现改坏了,直接cp回来就能还原,比靠记忆恢复靠谱得多。
2.2 daemonize、logfile、requirepass 等关键项
Redis 默认是前台启动的,也就是说终端窗口一关,Redis 就跟着退出。要让 Redis 自己跑到后台去,需要改daemonize这个配置项。
找到配置文件中这行:
daemonize no改成:
daemonize yes这个配置的意思很直白——让 Redis 以守护进程方式运行。改完这个之后,redis-server /opt/homebrew/etc/redis.conf启动后终端会立刻回到命令提示符,但 Redis 已经在后台跑着了。
但这只是第一步。如果你想让 Redis 在系统启动时自动运行,光靠daemonize yes是不够的,因为daemonize yes只是让 Redis 脱离终端,并没有告诉 macOS“开机时启动它”。所以很多教程里只改这一项,然后告诉你“已经后台启动了”,其实只答对了一半。后面我会专门讲 launchd 的部分,那才是开机自启的正解。
接着看日志配置。默认情况下 Redis 的日志输出到 stdout,也就是终端。但既然要后台运行,日志必须写到文件里,否则以后排查问题什么都看不到。找到:
# logfile ""改成:
logfile "/opt/homebrew/var/log/redis.log"注意这个目录是 Homebrew 装 Redis 时自动创建好的,你不需要手动mkdir。如果你用了自定义路径,必须先确保目录存在,否则 Redis 启动会报错。
密码这块,如果 Redis 只在本机用,可以暂时不开。但如果你的 Mac 在局域网内,或者你打算用可视化工具远程连接,建议设置一个强密码:
# requirepass foobared改成:
requirepass "你的强密码"设置了密码之后,用redis-cli连接时需要执行AUTH 你的密码才能操作,这个别忘。我见过好几个人配好了密码,结果过了几天自己忘了,连不上还以为 Redis 挂了,最后一看是密码问题。
2.3 持久化配置影响(RDB、AOF)
后台运行和开机自启其实还牵扯到一个隐藏问题:数据持久化。先解释一下 Redis 的两种持久化机制:
- RDB:定时把内存中的数据快照写入磁盘,默认开启,文件叫
dump.rdb - AOF:把每一条写命令追加到日志文件,重启时重放命令恢复数据,默认关闭
对于开发机来说,RDB 默认配置基本够用。但如果你的 Redis 里放了一些不想丢的临时数据,建议顺手把 AOF 打开:
appendonly yesAOF 文件的默认位置同样在/opt/homebrew/var/db/redis/下。这个配置不直接影响“后台启动”和“开机自启”这两个主题,但如果你开机自启配好了,重启之后发现 Redis 里啥都没了,那大概率是持久化没配好。所以我把这条一并列出来,避免你走了前面的路,最后栽在这个坑里。
改完配置之后,可以用redis-server /opt/homebrew/etc/redis.conf前台启动一次,看看有没有报错。没有报错再 Ctrl+C 停掉,继续下一步。如果你已经看到Ready to accept connections这行日志,说明配置没问题。
3. 后台启动的各种姿势与实测对比
3.1 前台启动与 redis-server 路径确认
先明确一个概念:macOS 上装完 Redis 之后,有两个地方可以执行redis-server:
/opt/homebrew/bin/redis-server/opt/homebrew/opt/redis/bin/redis-server
两者基本一样,/opt/homebrew/opt/redis/bin可以理解成 Homebrew 里的“内部链接”,而/opt/homebrew/bin是暴露给用户的公开命令。直接用redis-server就行,因为/opt/homebrew/bin已经在 PATH 里了。
用下面的命令做一次配置校验:
redis-server /opt/homebrew/etc/redis.conf如果配置正确,启动日志里能看到端口(默认 6379)在监听。这是最原始的启动方式,放在这里只是为了验证配置。验证完直接 Ctrl+C 停掉,因为接下来要聊的才是真正“后台化”的方案。
3.2 用&或 nohup 手动后台运行的局限
很多人第一反应是:既然前端启动会占住终端,那我加个&让它后台运行不就行了?比如这样:
redis-server /opt/homebrew/etc/redis.conf &这确实能让 Redis 在当前终端会话存活期间在后台跑,终端关闭之后进程还活着。但问题是,这种方式本质上是“手动起了一个孤儿进程”,管理起来很别扭。你想停的时候得先ps aux | grep redis找到 PID,再kill PID,完全是手工操作。
nohup稍微好一点,能让进程在终端退出后继续运行:
nohup redis-server /opt/homebrew/etc/redis.conf > /opt/homebrew/var/log/redis-nohup.log 2>&1 &但它仍然没有解决“开机自启”的问题。macOS 开机之后,不会因为你之前跑过一个nohup命令就自动拉起 Redis。你每次重启电脑,都得手动执行一遍。这显然不符合“开机、后台启动”这六个字的要求。
3.3 brew services 方式:最省事的后台方案
Homebrew 为每个装了服务的公式都封装了一个brew services命令,用起来很简单:
brew services start redis执行完之后,Homebrew 会把 Redis 注册进 launchd,然后立即启动它。查看运行状态:
brew services list输出里会看到 Redis 这一行是started。这个方案的优点是省事、干净,而且 Homebrew 自动帮你处理了 launchd 的 plist 注册,重启电脑之后 Redis 会自动起来,也算实现了开机自启。
但我实际用过一段时间之后,发现brew services方案有几个不舒服的地方:
- Redis 的日志、错误输出等都由 launchd 统一管理,排查问题时要到
/opt/homebrew/var/log/redis.log或者launchctl error里去翻,对不熟悉 launchd 的人来说,黑盒感很强。 - 一旦 Redis 启动失败,
brew services list里只显示error,但你不知道具体错误原因,得自己去看日志。 - 如果你想用非默认端口、非默认配置路径,
brew services对自定义参数的支持比较弱。
所以,brew services适合“我就想让它跑起来”的场景。但如果你跟我一样,想搞明白 macOS 到底是怎么把 Redis 拉起来的、想完全掌控启动参数和日志行为,建议自己写 plist 文件。这是下一章的内容。
3.4 为什么手动 daemonize 不推荐作为唯一方案
这里必须把daemonize yes和“真正的后台服务”区分开。前面改过daemonize yes之后,用命令:
redis-server /opt/homebrew/etc/redis.confRedis 确实会在后台运行,终端也不会被占用。但问题在于:这个进程是当前用户手动拉起的,它和“登录会话”绑定在一起。你的 Mac 重启之后,这个进程就没了,你也不会看到一个“开机自动后台运行”的 Redis。
更隐蔽的问题在于:一旦 Redis 进程崩溃退出,没有任何机制把它重新拉起来。而 launchd 的 plist 里可以配置KeepAlive,进程退出后自动重启。同样是“后台运行”,一个是裸奔,一个是带保险绳,性质完全不同。
所以我把建议放在这里:daemonize yes这个配置可以开,但开了之后不要依赖它来实现“开机自启”。开机自启要用 launchd,这是 macOS 的官方机制,也是唯一靠谱的方案。
4. 开机自启动的完整配置:launchd 与 plist 文件
4.1 launchd 的工作原理
先搞清楚 launchd 是个什么东西。简单说,它就是 macOS 的“开机管家”,系统启动时会读入一堆 plist 配置,按配置决定启动哪些服务、怎么启动、进程挂了要不要拉起。
launchd 的 plist 文件有两个存放目录:
~/Library/LaunchAgents/:当前用户登录后加载,适用于用户级服务/Library/LaunchAgents/:所有用户登录后加载,需要 sudo 权限/Library/LaunchDaemons/:系统级守护进程,不需要用户登录就会启动,适用于后端服务
对于 Redis 这种本机开发用的服务,放在用户级的~/Library/LaunchAgents/就够了。但有一个前提:你 Mac 开机后需要有用户登录进入桌面,LaunchAgent 才会被加载。如果你设置了自动登录,或者你本人就是唯一的用户,这完全没问题。如果你是开着 Mac 当服务器用、平时根本不登录图形界面,那就应该考虑 LaunchDaemon。后者的 plist 写法和运行权限完全不同,这篇文章以 LaunchAgent 为主,因为大多数开发者都是这个场景。
Homebrew 的brew services命令本质也是帮你生成一个 plist 并放进~/Library/LaunchAgents/。
4.2 创建 plist 文件:一步步写清每个字段
下面这个 plist 是我在实际使用中逐渐调整出来的,相比 Homebrew 默认生成的版本,可读性更好,参数也更明确:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.redis.server</string> <key>ProgramArguments</key> <array> <string>/opt/homebrew/bin/redis-server</string> <string>/opt/homebrew/etc/redis.conf</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/opt/homebrew/var/log/redis.stdout.log</string> <key>StandardErrorPath</key> <string>/opt/homebrew/var/log/redis.stderr.log</string> </dict> </plist>把这段内容保存到~/Library/LaunchAgents/com.redis.server.plist。
逐个字段说清楚是什么意思:
Label:服务的唯一标识,必须和文件名com.redis.server一致。不一致会导致launchctl load报错。ProgramArguments:要执行的命令和参数。第一个元素是程序路径,后面的元素是传给它的参数。这里就是让redis-server读redis.conf启动。RunAtLoad:设置为true,表示加载这个 plist 时立即执行一次启动。这是“开机后自动启动”的关键。KeepAlive:设置为true,表示如果 Redis 进程意外退出,launchd 会自动重新拉起。相当于带了一个守护进程。StandardOutPath和StandardErrorPath:把 stdout 和 stderr 重定向到日志文件,这样以后排查问题有据可查。
有几点要注意:
如果你用了
daemonize yes,Redis 会自己 fork 到后台,然后 launchd 会发现当初启动的那个“前台进程”已经退出了。此时KeepAlive的逻辑会跟 Redis 的守护进程化行为产生冲突。因此,用 launchd 管理时,推荐把daemonize改成no。因为 launchd 本身就负责管理后台生命周期,Redis 不需要再自己 daemonize。这是一个非常容易踩的坑,我折腾了快一个小时才反应过来。plist 文件对格式要求严格,标签必须配对。写完建议用下面的命令校验:
plutil -lint ~/Library/LaunchAgents/com.redis.server.plist如果输出OK,说明格式没问题。如果输出错误,会告诉你具体哪一行有问题。
4.3 launchctl 加载与验证
plist 写完之后,真正的加载命令如下:
launchctl load ~/Library/LaunchAgents/com.redis.server.plist老版 macOS 上这条命令正常,但新版 macOS(尤其是 Ventura 之后)会提示load已废弃,建议用bootstrap。保险起见我给出两种:
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.redis.server.plist其中gui/$(id -u)表示当前用户的 GUI 会话域。这条命令在 Apple Silicon 的 macOS 上实测有效。
查看服务是否加载成功:
launchctl list | grep redis如果看到类似PID Status Label的输出,并且有进程 ID,说明 Redis 已经被 launchd 管理起来了。也可以再用redis-cli ping验证一下 Redis 实际工作状态,正常会返回PONG。
如果要卸载服务,用:
launchctl bootout gui/$(id -u)/com.redis.server听说过unload的老命令也没问题,一样能用。但新系统下建议用bootout,更符合 launchd 的新式管理语义。
4.4 修改配置后的重载流程
launchd 和 Redis 的配置都存在一个“改了配置不会立即生效”的问题。实际调优的时候经常要改 redis.conf 里的某个参数,然后希望 Redis 用新配置重启。正确的流程是:
# 1. 卸载服务 launchctl bootout gui/$(id -u)/com.redis.server # 2. 重新加载服务 launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.redis.server.plist这一步做完,launchd 会杀掉旧的 redis-server 进程,然后用新配置重新拉起一个。整个过程就是两条命令的事,不用手动去killPID。
另外,launchctl命令不会自己去读 Redis 的配置文件,所以改了redis.conf之后直接launchctl bootstrap是无效的,必须先bootout再bootstrap。这个顺序搞反了,新配置不会生效,而且 Redis 会继续用旧配置跑,排查问题时容易被误导。
5. 实测中遇到的坑与排查思路
5.1 端口被占用:启动失败的头号嫌疑
我一开始装完直接启动 redis-server,结果报错:
Could not create server TCP listening socket *:6379: bind: Address already in use这个提示的原因很明确:6379 端口已经有进程占用了。最典型的场景是之前手动启动过一个 Redis,但没人记得它还在后台跑着。排查命令:
lsof -i :6379如果看到有redis-server进程占用,说明之前启动的 Redis 还在。用下面的命令停掉它:
redis-cli shutdown如果 Redis 设置了密码,需要:
redis-cli -a 你的密码 shutdown停掉之后再验证:
lsof -i :6379没有任何输出,说明端口已经释放了。然后重新走一遍launchctl bootstrap流程即可。
5.2 plist 加载不生效的排查链路
如果你执行完launchctl list | grep redis看不到任何输出,按下面的链路排查:
第一,确认文件路径和文件名。~/Library/LaunchAgents/com.redis.server.plist这个文件必须存放在 LaunchAgents 目录下,文件名必须和Label一致。不一致,launchd 静默忽略。
第二,检查 plist 格式。用plutil -lint校验,任何括号不配对、标签写错,都会导致加载失败。
第三,查看 launchd 的错误信息。比较常见的是:
launchctl error如果输出了错误码,可以launchctl print gui/$(id -u)/com.redis.server查看这个服务的详细信息。注意print命令是排查 launchd 问题的神器,里面能看到程序路径、参数、运行状态、退出原因等,比盲猜有效得多。
第四,确认 redis.conf 中daemonize是no。如果你之前改成了yes,launchd 启动 Redis 后,Redis 自己把自己 fork 到后台,初始前台进程正常退出。在 launchd 眼里,这个服务“启动完了然后立即退出了”,如果KeepAlive没开,服务就直接变 inactive 了;开了则会反复重启。这个问题在日志里看起来像“启动、退出、启动、退出”的死循环。排查时看/opt/homebrew/var/log/redis.stderr.log就能发现端倪。
5.3 开机自启生效但 redis-cli 连不上
还有一次比较有意思,重启电脑后 Redis 确实在跑,但redis-cli ping报连接拒绝,用lsof -i :6379也没看到监听。查了半天,发现是 macOS 的LocalHost网络服务在重启后稍微慢了一点,launchd 在 Redis 需要监听的网络栈就绪之前就已经把它拉起来了,导致 Redis 执行bind失败。
处理方案有两个:
- 方案一:在
redis.conf里把bind 127.0.0.1显式写出来,不要让 Redis 去监听所有网卡(*),减少网络栈依赖。 - 方案二:在 plist 里加一个延迟启动的机制。launchd 本身没有直接的 “sleep N 秒再启动” 字段,但可以在
ProgramArguments里包一层 shell:
<key>ProgramArguments</key> <array> <string>/bin/bash</string> <string>-c</string> <string>sleep 5 && /opt/homebrew/bin/redis-server /opt/homebrew/etc/redis.conf</string> </array>这样 launchd 拉起服务后会先睡 5 秒再启动 Redis,给网络栈留出时间。这种方式不够优雅,但实测能解决大部分“开机太早导致绑定失败”的问题。等 Redis 自己稳定运行之后,可以再把 sleep 去掉。
5.4 日志的查看方法与持久化验证
日常维护 Redis 最常用的日志路径:
- Redis 运行日志:
/opt/homebrew/var/log/redis.log - launchd stdout:
/opt/homebrew/var/log/redis.stdout.log - launchd stderr:
/opt/homebrew/var/log/redis.stderr.log
排查问题时按顺序看这三个文件。如果 Redis 没起来,先看 stderr;stderr 里没有有效信息,再看 redis.log。日志文件不存在,说明 Redis 很可能根本没被启动,或者启动参数有问题。
持久化方面,设置appendonly yes之后,重启机器连 Redis 验证一下:
redis-cli 127.0.0.1:6379> SET testkey "hello" OK 127.0.0.1:6379> SAVE OK 127.0.0.1:6379> SHUTDOWN然后重新启动 Redis,再GET testkey,如果返回"hello",说明持久化验证通过。这一条配置看似跟“开机自启”无关,实际是“开机自启后 Redis 数据还在不在”的关键保障。
5.5 关于可视化工具和连接测试的补充
Redis 跑起来之后,很多人喜欢用可视化工具看一眼。相关热词里也出现了 Redis Desktop Manager,目前这个工具的社区版已经更名为 RedisInsight,官方推出的版本功能比较完整。如果只是想看 key 列表、内存占用这些基础信息,用redis-cli完全够用;如果确实需要 GUI,下载 RedisInsight 即可。连接时注意填127.0.0.1:6379,如果你在 redis.conf 里设置了密码,需要填 AUTH 密码。连接不上时优先检查 Redis 是否监听、密码是否配置正确、网络栈是否正常这三个点,不要一上来就怀疑工具坏了。
如果你平时在 Linux 服务器上习惯用的systemctl命令,在 macOS 上不存在。这和本文讨论的 launchd 机制是两套东西,不要把两者的概念混在一起。
最后再分享一个我个人的习惯:我会在~/.zshrc里加一个别名,方便快速查看 Redis 状态:
alias redis-status='launchctl list | grep redis && redis-cli ping'每次开机之后敲一下redis-status,能同时确认 launchd 服务和 Redis 本身都正常,一行命令搞定。这个项目做完之后,我的 Redis 已经连续跑了一个多月没出过任何问题,中间经历了多次重启。配置一次,后面真的就省心了。