如果你平时用 macOS 做开发,大概率会遇到过这样的场景:项目里写着“先启动 Redis”,你敲下redis-server,一切正常;等到第二天电脑重启,服务没了,缓存直接失效,登录状态也跟被格式化了一样。我第一次碰到这个问题时,还真以为是 Redis 装坏了,后来才明白是启动方式太“裸”——既没有后台化,也没有注册进系统服务。这篇文章是我在 macOS 上从零安装 Redis,并把它配置成后台运行、开机自启的完整过程,包含我踩过的坑和最终的稳定方案。适合刚接触 Redis 的开发者照着一路操作,也适合已经会手动启动 Redis、但受够了每次开机都要重新敲命令的人。
1. 先理清楚:你说的“后台启动”到底是哪一种
在动手之前,我建议先把“后台启动”这四个字拆开。很多人一开始想得很简单:让 Redis 别占着当前终端窗口、别一关窗口就死掉、顺便开机自己跑起来。但这三个需求在 macOS 上其实对应三种不同的实现层次,搞混了后面排错会非常难受。
1.1 前台、后台、开机自启,三种状态别混淆
Redis 最原始的启动方式是直接在终端跑redis-server。这时候 Redis 是在前台运行,终端会被占住,按Ctrl+C或者直接关掉终端窗口,进程基本就结束了。这种模式适合调试配置文件、观察启动日志,但不适合当常驻服务用。
第二种是“后台运行”。最简单的是redis-server &,把进程放到 shell 的后台;更专业一点是让 Redis 自己 daemon 化,在配置文件里把daemonize改成yes,然后 Redis 会 fork 一个子进程出来,主进程退出,终端可以自由继续使用。这里有个容易踩的坑:redis-server &这种后台方式在终端退出时未必活得住,因为它继承的终端会话被关闭后,可能收到挂断信号直接退出。想要它扛住,一般还要配合nohup或者别的方式接管。
第三种才是“开机自启”。macOS 上负责开机启动任务的系统组件叫 launchd,它通过加载一个个 plist 配置文件来决定哪个服务在什么时候启动、要不要崩溃后自动拉起。把 Redis 注册进 launchd,才算是真正解决了“重启电脑后 Redis 不在了”的问题。
这三种状态并不是递进关系,后台运行不代表开机自启,开机自启的进程也不一定适合手动调试。我见过有人用nohup把 Redis 挂在后台跑了好几天,看着没问题,但电脑一重启照样失效。所以最终目标应该锁定为“用 launchd 管起来”。
1.2 为什么我最终选了 brew services 这条路径
在 macOS 上管理 Redis 常驻服务,方案大致有三类:裸命令行加nohup、手动写 launchd 的 plist 文件、用 Homebrew 自带的brew services命令。三者的关系有点像“手动关窗、普通遥控器、智能家居”的区别。
nohup redis-server &属于应急方案,优点是快,缺点是 OS 层面完全不知道这个进程的存在,崩溃了没人拉,日志散落在各处,也不会开机自启。手动写 launchd 配置属于进阶方案,控制力最强,但初看 plist 语法和一堆 key 时容易懵,而且 macOS 更新后部分launchctl命令行也变了,新手容易卡住。brew services本质上还是帮你生成 launchd 配置、调用launchctl加载服务,相当于把最麻烦的封装掉了,而且日志路径、配置文件位置都有约定俗成的习惯,出了问题也好查。
我的建议是:日常开发机直接用brew services,如果以后要把 Redis 部署到一台无 Homebrew 的服务器上,再回头学手动 launchd 写法不迟。下面整个安装流程也是按这个思路来的。
2. 在 MacOs 上安装 Redis:方案与实操
2.1 用 Homebrew 安装 Redis,最省事
macOS 上安装 Redis 的常见方式有 Homebrew、直接下载官方 tar 包编译、Docker 跑容器。这里我不推荐后两种作为本机常驻服务的首选:源码编译需要自己处理依赖和路径,Docker 容器虽然隔离方便,但会带来端口映射、数据卷、开机自启容器等一系列额外问题,对只想在开发机上跑一个本地 Redis 的人来说有点重。
Homebrew 的方案最顺手。先确认机器上有没有 Homebrew:
brew --version如果还没有,先去官网装好,然后执行:
brew update brew install redis安装完成后可以用两个命令验证一下:
redis-server --version redis-cli --version能看到版本号就说明二进制文件已经放进 PATH 了。我这边安装完成后,redis-server是在/opt/homebrew/bin/redis-server,因为当前电脑是 Apple Silicon 芯片的 Mac;如果你的机器是 Intel 芯片,目录通常是/usr/local/bin/redis-server。这个路径差异后面会反复遇到,先有个印象。
2.2 安装后的关键配置项
Homebrew 安装 Redis 时会附带一份默认配置,Apple Silicon 机器上路径是/opt/homebrew/etc/redis.conf,Intel 机器上是/usr/local/etc/redis.conf。后面所有修改都围绕这个文件操作。
打开配置文件,我一般会先检查这几个关键项:
bind 127.0.0.1 -::1 port 6379 daemonize yes logfile "/opt/homebrew/var/log/redis.log" dir /opt/homebrew/var/db/redis/bind:默认只监听本机回环地址,安全性不错。开发机不建议改成0.0.0.0,除非你有明确的局域网访问需求。port:默认 6379,不冲突就不用动。daemonize:这里要注意,如果你打算用brew services管理 Redis,我反而建议把它保持为no。原因是brew services依赖 launchd 来追踪进程,Redis 自己 fork 成守护进程后,主进程会正常退出,launchd 可能认为服务异常退出而反复拉起,造成端口冲突或日志混乱。logfile:指定日志文件路径,方便后面排查问题。dir:Redis 持久化文件存放目录,默认存在/opt/homebrew/var/db/redis/下面。
第一次上手时不要急着改太多,先把bind、daemonize、logfile这几项确认好就够了。密码和持久化相关的内容,后面单独讲。
2.3 手动跑一次 Redis,确认配置没毛病
改完配置后,先不要直接注册服务,手动启动一次 Redis,确保配置能正常加载。前台启动:
redis-server /opt/homebrew/etc/redis.conf看到 Redis 版本号、端口 6379 这些日志,就说明配置没问题。此时打开另一个终端,输入:
redis-cli ping返回PONG就代表服务可以正常响应。
如果不想占用当前终端,也可以在确认配置没问题后,临时用 daemon 方式启动一次:
redis-server /opt/homebrew/etc/redis.conf --daemonize yes然后再用redis-cli shutdown把进程关闭。这一步只是为了验证配置文件能被正常读取、没有语法错误,后面真正交给brew services管理时,还是要遵守上一节说的 daemonize 配置原则。
提示:如果 Redis 启动时提示
Can't open the log file之类的错误,多半是/opt/homebrew/var/log/目录不存在或没有写权限。可以先手动创建目录,再重试。
3. 设置 Redis 开机自启:两种路线都讲清楚
3.1 懒人方案:brew services start redis
安装好 Redis 之后,设置开机自启最直接的一行命令是:
brew services start redis执行完再看服务状态:
brew services list如果列表里 Redis 那行显示的是started,就说明它已经被 launchd 接管了。这个命令背后其实做了三件事:检查 Redis 的配置文件、生成或更新一个 launchd 的 plist 文件、把 plist 加载进当前用户的 launchd。以后每次登录系统,launchd 都会按照这个 plist 把 Redis 启动起来。
如果想临时跑一次但不设置开机自启,可以用:
brew services run redis注意run和start的差异:run只负责这次把服务跑起来,不保证下次登录自动启动;start则会注册进开机启动项。我调试环境变量、临时测配置时就用run,稳定下来再切回start。
启动完成后,用 Redis 自带命令做一次冒烟测试:
redis-cli ping看到PONG基本就稳了。此时如果你看一眼~/Library/LaunchAgents/目录,会发现里面多了个homebrew.mxcl.redis.plist,这就是brew services帮你生成的 launchd 配置文件。
3.2 进阶方案:手动写 launchd plist 文件
如果哪天你用了一台没有 Homebrew 的机器,或者想彻底搞懂 launchd 的原理,可以手动创建 plist。按惯例放在~/Library/LaunchAgents/下,文件名一般是com.redis.server.plist,内容是:
<?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>WorkingDirectory</key> <string>/opt/homebrew/var/db/redis</string> <key>StandardOutPath</key> <string>/opt/homebrew/var/log/redis.log</string> <key>StandardErrorPath</key> <string>/opt/homebrew/var/log/redis.log</string> </dict> </plist>几个核心 key 的含义:
Label:launchd 里的唯一标识符,不能和已有服务重复。ProgramArguments:真正要执行的命令和参数,这里指定了 redis-server 二进制和配置文件路径。RunAtLoad:加载 plist 时就启动服务,相当于登录后默认跑起来。KeepAlive:进程异常退出时是否由 launchd 自动拉起。设为true后,Redis 不小心挂了也会被重新拉起。StandardOutPath和StandardErrorPath:把 Redis 标准输出和错误输出重定向到日志文件,方便排错。
保存后,加载这个 plist 有两种方式。较新的 macOS 推荐用:
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.redis.server.plist老一点的写法是:
launchctl load ~/Library/LaunchAgents/com.redis.server.plist想要取消自启,分别用launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.redis.server.plist或launchctl unload。
手动写 plist 的优点是你可以完全控制启动参数,比如通过EnvironmentVariables字段给 Redis 注入环境变量,或者通过ThrottleInterval控制异常退出后的重启间隔。但这些在brew services下已经帮你处理好了,日常开发不必重复造轮子。
3.3 验证开机自启是否真的生效
设置完不等于万事大吉,我建议花半分钟做一次验证。最简单粗暴的方式是直接重启电脑,重启后打开终端执行:
redis-cli ping如果直接返回PONG,说明自启成功了。
不想重启的话,可以用两条命令辅助确认:
launchctl list | grep redis brew services listlaunchctl list能看到当前用户加载的服务,brew services list能看到 Homebrew 视角下的运行状态。如果两条命令都显示 Redis 在运行,那自启基本稳了。
我想特别提醒一点:如果你是用brew services start redis设置的自启,但brew services list显示的是started,而launchctl list | grep redis里又找不到对应 label,大概率是 plist 没被正确加载到当前用户的 launchd 会话里。这时候先看~/Library/LaunchAgents/下有没有 plist 文件,没有就重新brew services start redis;有的话可以用launchctl bootstrap手动加载一次。
4. 后台启动与日常运维:把 Redis 管起来
4.1 服务启停与重启的正确姿势
服务正常运行后,日常维护命令其实没几个。停止、启动、重启分别对应:
brew services stop redis brew services start redis brew services restart redis如果你的环境是手动写的 plist,也可以用launchctl bootout和launchctl bootstrap代替。这里有个容易困惑的点:redis-cli shutdown也能把 Redis 关掉,那它和brew services stop有什么区别?
区别在管理层面。redis-cli shutdown只是让 Redis 进程优雅退出,如果你已经用brew services或 launchd 的KeepAlive把它保护起来,launchd 会觉得“服务挂了”,然后自动把它重新拉起。结果就是你明明执行了 shutdown,几秒后 Redis 又活了。想真正停掉服务,应该用brew services stop redis,把 launchd 的任务也停掉。
另外还有一种重启方式叫kickstart,用于 launchd 已经接管服务的情况:
launchctl kickstart -k gui/$(id -u)/com.redis.server-k参数表示先杀掉进程再重新启动。修改配置文件后,我用kickstart比直接brew services restart更能确认是 launchd 完成了重启。
4.2 日志在哪里看、怎么看
后台服务最怕的就是“进程活着但业务异常”,这时候日志就是唯一突破口。Homebrew 管理的 Redis,日志位置通常和配置文件里的logfile一致。Apple Silicon 机器常见路径:
tail -f /opt/homebrew/var/log/redis.logIntel 机器则是:
tail -f /usr/local/var/log/redis.log手动写 plist 时,日志路径由StandardOutPath和StandardErrorPath指定,我一般也会指向同一个文件。如果日志文件里什么都没有,先检查 Redis 配置里的logfile是不是被设置成空字符串了,空值会让 Redis 默认把日志输出到/dev/null,导致什么都看不到。
另外一个实用命令是redis-cli --stat,它会持续打印 Redis 的实时状态,包括客户端连接数、内存占用、命中率等,比翻日志直观得多。开发机上排查性能问题,我都是先开这个,再决定要不要去看慢日志。
4.3 数据持久化:别让 Redis 重启后“失忆”
很多人以为 Redis 只是缓存,重启丢数据无所谓。但在本机开发环境里,如果 Redis 里存了临时登录态、队列任务或一些统计计数,服务一重启数据就没了,会非常耽误事。所以建议顺手把持久化打开。
Redis 持久化有两条路:RDB 快照和 AOF 日志。RDB 是在指定时间点把内存数据整个写到磁盘,占空间小、恢复快,但可能丢失最近一次快照之后的数据;AOF 是把每次写命令追加到日志文件,数据更完整,但文件会不断膨胀,需要定期重写。生产环境通常两种配合用,开发机可以先用 AOF 保数据。
在/opt/homebrew/etc/redis.conf里,我至少会确认这几个配置:
save 900 1 save 300 10 save 60 10000 appendonly yes appendfsync everysec dir /opt/homebrew/var/db/redis/ dbfilename dump.rdbsave三项表示触发 RDB 快照的条件:900 秒内至少有 1 次写操作就保存一次,300 秒内至少有 10 次写操作保存一次,60 秒内至少有 10000 次写操作保存一次。appendonly yes开启 AOF,appendfsync everysec表示每秒同步一次,是在性能和安全性之间比较平衡的选择。
改完配置后重启服务,然后验证一下持久化是否生效:
brew services restart redis redis-cli set test_key hello redis-cli save ls -l /opt/homebrew/var/db/redis/目录下能看到dump.rdb或者appendonly.aof就说明持久化在正常工作。
4.4 给 Redis 加个密码再使用
本机开发环境常常默认不设密码,因为默认bind 127.0.0.1只监听回环地址,外部访问不到。但如果你为了某些测试把 bind 放开到局域网,或者本机有其他服务可能把 Redis 端口暴露到公网,那就必须加密码。
配置文件里找到这样一行:
# requirepass foobared改成:
requirepass your-strong-password重启 Redis 后,再用redis-cli连接会发现普通命令已经不可用:
redis-cli 127.0.0.1:6379> keys *此时应该报NOAUTH Authentication required。带密码连接有两种方式:
redis-cli -a your-strong-password或者在交互式终端里:
redis-cli 127.0.0.1:6379> AUTH your-strong-password-a方式方便但密码会出现在 shell 历史里,不建议在共享机器上这样操作。如果只是开发机自己用,AUTH手动输入更稳妥。
这里再补一个细节:很多连接工具或代码框架支持在 URL 里带密码,比如redis://:password@127.0.0.1:6379/0,不过我不建议把密码明文写在代码仓库里,开发环境可以放.env,生产环境应该走密钥管理服务。
5. 排错实录:这些坑我基本都踩过
5.1 6379 端口被占用
启动 Redis 时提示Address already in use,说明 6379 端口已经有一个 Redis 或者其他程序占用了。先查一下占用情况:
lsof -i :6379看到具体进程后,可以确认是不是之前手动启动的 Redis 没关干净。如果是,优雅关掉:
redis-cli shutdown如果进程不是 Redis 而是别的服务,那就改 Redis 端口,在配置文件里调整port为 6380 之类,或者处理掉冲突进程。日常开发经常出现“手动启动了一次、又用 brew services 拉起来了另一个”的情况,两个进程抢同一个端口,这时候建议先把所有 redis 进程停掉,再统一用brew services start redis拉起一个。
5.2 系统里存在多个 Redis 导致版本错乱
安装过源码包、Docker、Homebrew 的机器上,有可能存在多个 redis-server。表现就是redis-server --version和redis-cli --version版本对不上,或者改完配置后发现进程用的不是你改的那份。
排查命令:
which -a redis-server which -a redis-cli这两个命令会列出 PATH 下所有匹配项,按先后顺序看当前用的是哪个。如果发现 Homebrew 版本没有优先生效,可以用:
brew unlink redis && brew link redis把 Homebrew 版本重新链接到 PATH 前位。手动源码安装的 Redis 通常会在/usr/local/bin/redis-server或/usr/local/sbin/redis-server附近,建议用brew services管理后,把其他手动装的进程全部停掉,避免相互干扰。
5.3 Apple Silicon 和 Intel 的路径不一致
这是 macOS 上 Redis 排错最容易忽略的点。Apple Silicon 的 Homebrew 前缀是/opt/homebrew,Intel 的是/usr/local。配置目录、日志目录、plist 路径全都不一样。
我见过最典型的错误是:在一台 Apple Silicon 机器上照抄网上的/usr/local/etc/redis.conf,结果brew services start redis时根本读不到配置。处理方式很简单:确认芯片架构后,把配置文件和日志路径都统一到对应前缀下。不清楚的话,直接执行:
brew --prefix这个命令会告诉你 Homebrew 真实前缀目录,所有路径都以它为准。
5.4 brew services 显示 started 却连不上
brew services list显示started,但redis-cli ping超时或者拒绝连接,这种情况一般是两类原因。
第一类是 Redis 进程被 launchd 拉起来后又立刻崩了,处于“反复重启”状态。查看日志,如果提示配置文件里的dir目录不存在或没有写权限,Redis 启动时会直接退出。创建对应目录并修正权限就能解决。
第二类是daemonize yes造成的。很多人在配置文件里把daemonize改成yes,还手动启动过一次,之后再brew services start redis时,launchd 以为 Redis 变成守护进程后主进程退出了,于是判定任务失败。解决办法是把daemonize改回no,让 launchd 保持追踪 Redis 主进程,再brew services restart redis。
5.5 改了配置却不生效
这个坑我反复踩过:配置文件改了半天,redis-cli config get一查,端口还是旧的、密码还是旧的。原因往往是 redis-cli 连上的是一个旧进程,或者brew services restart之后启动的其实是另一个 redis-server。
排查思路是先确认实际运行的进程读取了哪个配置文件:
redis-cli config get dir redis-cli config get logfile如果得到的路径和你改的路径不一致,基本可以判断进程不是由你那个配置文件启动的。此时停掉所有 Redis 进程,再用brew services start redis统一拉起。改完配置文件后,也务必执行一次brew services restart redis,不要只通过redis-cli shutdown再重启,否则可能没走 launchd 的启动参数。
5.6 持久化文件目录权限和磁盘空间
一旦把持久化打开,目录权限问题就会暴露。Redis 写入 RDB 或 AOF 文件时如果提示Permission denied,检查一下:
ls -l /opt/homebrew/var/db/redis/当前用户需要对该目录有写权限。Homebrew 安装时一般已经创建好,但如果手动调整过目录或换过数据目录,就要保证权限正确。磁盘空间也要留个心眼,AOF 开启后文件会增长,特别是开发机磁盘剩余空间不足时,Redis 会通过日志警告Background save failed。用df -h /opt/homebrew看一下剩余空间,再决定是否使用appendfsync always这种更费空间的配置。
注意:如果 Redis 输出
maxmemory相关警告,说明内存使用接近上限。开发机可以适当调大maxmemory,但不能设成超过物理内存的数值,否则会触发系统内存压力,连带着让整台机器变得卡顿。
写在最后的几点体会
我后来在好几台 macOS 开发机上重复过这套安装流程,个人体会是:不要过早迷恋源码编译和高级配置,先用brew services把服务稳定跑起来,再逐渐加深对 launchd 的理解。把 Redis 绑定到 launchd 之后,最大的好处不是省了那条启动命令,而是让本机的服务状态变得可预期——什么时候启动、日志在哪里、崩溃了会不会自动拉起,都有据可查。
最后分享一个小技巧:如果你经常在终端里操作 Redis,可以把常用命令收进 shell 配置里,比如给带密码连接加个别名。我自己就在.zshrc里加了一行:
alias redis-local='redis-cli -h 127.0.0.1 -p 6379 -a your-password'每次执行redis-local就能直接进入本地 Redis 交互界面,省去反复敲参数的麻烦。开发机上的服务管理,本质上是把“一次性跑起来”变成“长期稳定可维护”,希望这篇记录能帮你少走几步弯路。