
1. 从一次线上事故聊聊为什么需要 ACLRedis 6.0 的 ACL 机制我是等到线上真出了事才回头认真啃的。在那之前我们那套缓存集群里所有业务线共用一个requirepass密码写在配置中心的明文条目里谁都能KEYS *谁都能FLUSHALL。某天一个同学在排查问题时复制错了库名一条FLUSHDB把生产缓存清了个干净下游数据库瞬间被打穿服务雪崩了四十分钟。事后复盘最扎心的一句话是我们其实根本没有谁能在哪些键上执行哪些命令这个概念所有人都是超级管理员。Redis 的 ACL 全称 Access Control List中文一般叫访问控制列表从 6.0 版本开始正式支持。它的核心能力就三件事把一条连接绑定到一个具名用户上给这个用户限定能执行哪些命令再限定这个用户能碰哪些键和哪些频道。听起来简单但它把 Redis 从一个密码全家桶变成了可以按业务线、按角色精细切分的权限系统。你如果是运维、后端开发或者负责缓存治理的架构同学这套东西值得花两个小时彻底搞明白因为它是 Redis 认证体系里唯一一个能真正做到最小权限的工具。我先说清楚这篇文章适合谁看。第一种线上还在用共享密码、想逐步切到多账号体系的人我会给一套能直接抄的落地流程第二种已经在用 ACL 但被NOPERM报错折磨过的人我会把报错的每一种成因和排查路径都列出来第三种只是听说过 ACL 想知道它到底比requirepass强在哪的人前两章讲完你就有判断了。全文基于 Redis 6.0 到 7.x 的稳定行为涉及版本差异的地方我会明确标出来。1.1 Redis 6.0 之前的权限困境在 6.0 之前Redis 的认证体系可以用一句话概括requirepass一个密码REDIS 一个命令重命名完了。requirepass解决的是你能不能连上来只要密码对接下来你就是神所有命令都能跑所有键都能动。官方当时给出的权限控制建议是rename-command把FLUSHALL、CONFIG、KEYS这类危险命令改成一个别人猜不到的名字比如把FLUSHALL改成b840fc02d524045429941cc15f59e41cb7be6c52。我第一次看到这个方案时的反应是这不就是把钥匙藏在门垫底下吗。它的致命问题有三个。第一改名是全局的一旦改了所有客户端都得跟着改运维脚本、监控探针、业务代码全部要同步任何一处漏改就是线上故障。第二改名之后没有办法按用户区分A 业务改完名字还是能用B 业务改名之后就用不了了权限粒度精确不到用户。第三也是最要命的改名需要重启生效而生产环境的 Redis 重启一次要评估半天。所以现实中绝大多数团队的最终选择就是——不改了所有业务共用一个密码出了问题靠日志倒查是谁干的。这套模式在单业务、小规模的时候勉强能用一旦缓存被多个团队共享隐患就全部暴露出来了。你不能禁止某个业务跑KEYS *因为那会把整个实例阻塞住你不能禁止某个业务写错库因为它有全部权限你甚至没法在出事后快速定位是谁操作的因为MONITOR只显示命令不显示用户。ACL 的出现本质上就是把认证和授权这两件事拆开了requirepass只解决认证ACL 负责授权。1.2 别把 Redis ACL 和交换机 ACL 搞混这一点必须单独拎出来说因为我见过太多人在搜索 ACL 的时候把两类完全不同的东西混在一起。网络设备领域的 ACL比如在交换机上配置基于源 IP、目的 IP、端口的访问控制规则控制的是网络层的数据包能不能通过它的执行者是交换机的转发芯片规则匹配的对象是 IP 报文头。而 Redis 的 ACL 控制的是应用层的命令能不能执行执行者是 Redis 服务端进程匹配的对象是客户端发来的命令和键名。两者除了名字一样没有任何关系。还有一种容易混淆的情况有人会问我在交换机上做了端口 ACL 隔离是不是 Redis 就安全了。答案是否定的。交换机 ACL 只能控制哪些机器能连到 Redis 的 6379 端口一旦连上了你照样能执行任何命令。它能做的是网络边界做不了应用权限。真正的防护应该是两层叠加交换机或者安全组控制连接来源Redis ACL 控制连上之后能干什么。这两层缺一不可但千万不要用一层去替代另一层。顺便提一下Redis ACL 里的通配符和交换机 ACL 里的通配符掩码也是两回事。交换机的通配符掩码是反掩码0.0.0.255代表匹配任意最后一段的那种数学运算逻辑。Redis 的通配符是 glob 风格的模式匹配cache:*里的*表示任意长度的任意字符。这个区别我在第三章讲键模式的时候还会展开因为它直接关系到你写的规则到底能不能命中。2. ACL 核心模型拆解用户、规则与匹配逻辑搞明白 ACL 要先把它的数据模型在脑子里建起来不然你写规则就是靠试错。ACL 的模型其实只有三层用户是主体规则是属性匹配是执行时的判定动作。一条客户端连接在通过AUTH之后会被绑定到某个用户对象上之后每执行一条命令Redis 都会拿这条命令去这个用户的规则集里过一遍通过了才执行不通过直接返回NOPERM错误。整个过程是逐命令实时判定的没有缓存这也是为什么改了 ACL 规则之后不需要重连就立刻生效。2.1 用户对象与认证流程每个 ACL 用户对象上挂着这么几类属性开关状态、密码列表、命令权限集合、键模式集合、频道模式集合。开关状态就是on和offoff的用户即使密码对也认证不了等于是软删除。密码列表可以有多个认证时任意一个匹配即可这就给了密码轮换的空间——你可以先加一个新密码等所有客户端都切过去之后再把旧密码删掉实现零停机换密码。这里有个默认用户的概念必须讲清楚很多人第一次用 ACL 就栽在这。Redis 里始终存在一个名字叫default的用户当你没有配置任何 ACL 相关设置时default用户是on nopass ~* * all的状态翻译过来就是开启、不需要密码、能访问所有键、所有频道、所有命令。这也是为什么很多人在 6.0 上跑原来的代码一点问题都没有——因为默认行为向后兼容了。但只要你做了任意一件引入认证的事情default就会变。比如你在配置文件里写了requirepass foobared等价于执行了ACL SETUSER default on foobared注意它只加了密码权限还是全开的。如果你在 6.0 之前习惯用requirepass升级之后它依然能用但你的权限模型其实一点都没变。真正要收紧权限得显式地去改default用户的规则。再强调一个细节default用户是删不掉的ACL DELUSER default会直接报错。所以如果你的安全策略是不允许使用默认用户正确做法是把它设为off而不是删掉。2.2 命令权限规则的语法与执行顺序命令权限的规则语法是和-开头的字符串可以叠加。get表示允许 GET-get表示禁止read表示允许所有 READ 类别的命令all表示允许全部。这里的是类别前缀Redis 内置了几十个命令类别。常用的类别我列一下这些是我实际配权限时最常打交道的read是只读命令write是写入命令keyspace是键空间相关string、hash、list、set、sortedset、stream是按数据类型分的pubsub是发布订阅admin是管理类dangerous是危险命令KEYS、FLUSHALL、FLUSHDB、DEBUG这些都在里面fast和slow是按时间复杂度分的blocking是阻塞类命令。想知道某个类别下到底有哪些命令直接跑ACL CAT read就能列出来不用背。规则是从左到右依次应用的这一点是很多人踩坑的根源。-all read和read -all的结果完全不同前者是先全禁再开只读最终只读后者是先开只读再全禁最终什么都没有。所以写规则的时候一定要养成习惯先把基线打到底再往上加。我个人的固定套路是每条规则都以reset开头把用户的所有属性清干净这样不管之前配过什么结果都是可预期的。# 推荐写法先 reset 再叠加结果完全可预期 ACL SETUSER app_cache_read reset on StrongPass_2024 ~cache:* read ping select # 不推荐依赖当前状态做增量修改出问题时很难排查 ACL SETUSER app_cache_read get mget命令类别之外还可以精确到子命令。比如CONFIG有很多子命令你想让某个账号只能查配置不能改配置可以写成config|get这个竖线语法是 6.0 就支持的。同理还有CLIENT|LIST、CLIENT|KILL、ACL|WHOAMI等等。这个能力在做监控账号的时候特别有用——监控只需要INFO、CONFIG GET maxmemory、CLIENT LIST用子命令粒度授权比给admin安全得多。2.3 键模式与频道模式的通配符匹配细节键模式用~开头比如~cache:*。可以写多个多个之间是或的关系只要命中任意一个就通过。allkeys是~*的别名resetkeys是清空所有键模式。频道模式用开头语法一样allchannels是*的别名resetchannels清空。现在说那个被问得最多的问题为什么我的通配符匹配不上。这里的关键是Redis 用的是 glob 模式不是前缀匹配*能匹配任意字符包括冒号。所以~cache:*能匹配cache:user:1也能匹配cache:1这没问题。但有两个坑第一个坑*虽然能匹配空字符串但模式里其他的字符是字面量一个都不能少。~app:*匹配app:匹配app:1但不匹配app因为没有那个冒号。同理~user-*不匹配user-能匹配但你需要意识到中间的连字符必须存在。我见过有人写~session:*然后传键名session一直报NOPERM查了半小时。第二个坑多个模式之间是或但不同用户之间的模式不会合并。如果 A 用户有~cache:*B 用户有~user:*你不能指望 A 用户能访问user:1即使 B 用户能。这个说起来像废话但真有人在排查时搞混过。第三个坑是针对 Redis 7.0 的增强从 7.0 开始键模式支持读写分离标记%R~key:*表示只读权限%W~key:*表示只写权限%RW~key:*是读写。这个在实际用的时候非常讲究比如你配%R~cache:* all用户能GET但也可能不行——因为GET只需要读权限但如果键模式只给了%R那命令本身又要求写权限的话还是会失败。这个特性我建议先用默认的~就行等确有读写分离需求再上因为它的组合复杂度很容易让人头晕。# 多个键模式叠加或关系 ACL SETUSER svc_a reset on pass123 ~order:* ~cart:* read write # 7.0 的读写分离模式慎用 ACL SETUSER svc_b reset on pass456 %R~report:* %W~log:* all还有一个极其重要的边界键模式只在命令真正访问键的时候才检查。像PING、INFO、AUTH、SELECT这些不涉及具体键的命令键模式对它们完全不生效。所以如果你写~cache:* all用户依然可以执行PING、INFO、COMMAND。反过来说如果你想让用户连INFO都不能跑得从命令维度去禁不能指望键模式。3. 手把手搭建一套生产可用的 ACL 权限体系前面讲的是原理这一章讲怎么落地。我会按真实的上线顺序走一遍先确认版本再用命令行的方式快速验证一套权限矩阵验证没问题之后固化成 aclfile最后在客户端侧对接并做回归验证。整个过程不需要重启 Redis这也是 ACL 相比rename-command最爽的地方。3.1 环境确认与最小验证路径第一步先确认版本INFO server里的redis_version必须大于等于 6.0。如果你用的是某个云厂商的托管 Redis也得确认它有没有开放 ACL 命令——有些托管服务会禁用ACL系列命令这个坑我在第四章会细说。确认版本之后先跑一条ACL WHOAMI它会告诉你当前连接是以哪个用户身份在操作。默认没有认证的时候返回的就是default。紧接着跑ACL LIST你能看到当前所有用户和它们的规则。这个输出格式一开始看会有点懵我解释一下127.0.0.1:6379 ACL LIST 1) user default on nopass ~* * all 2) user app_read on #64位hash ~cache:* * read ping select每行以user开头接着是用户名然后是on/off然后是密码部分nopass表示不需要密码#开头的是密码的 SHA-256 哈希开头的是明文密码——ACL LIST输出的是哈希原始密码不会显示再往后是键模式、频道模式、命令规则。这里有个容易误判的点你执行ACL SETUSER alice on mypassword的时候用的是明文但ACL LIST里显示的是哈希值所以别以为密码没生效。想看密码部分的原始信息可以用ACL GETUSER alice它会返回一个结构化的数组里面有passwords字段和commands、keys、channels字段排查权限问题的时候强烈建议用这个而不是ACL LIST信息更全。127.0.0.1:6379 ACL GETUSER app_read 1) flags 2) 1) on 3) passwords 4) 1) c5b1e2f3... # SHA-256 哈希 5) commands 6) -all read ping select 7) keys 8) ~cache:* 9) channels 10) 3.2 用 ACL SETUSER 定义读写分离的账号矩阵我一般会按业务角色来划分账号而不是按人来划分。原因是按人划分账号数量会爆炸而且人员流动之后权限清理很麻烦。按角色划分的话常见的就是这么几类只读缓存账号给查询密集的服务用只能读不能写键范围限定在业务前缀内。这种账号的规则是reset on pwd ~cache:* read ping select。注意这里必须加ping和select因为很多客户端连接池在建立连接之后会先发PING探活还会发SELECT选库这两个命令不在read类别里漏了就会连接失败。读写业务账号业务主账号需要读写但必须禁掉危险命令。规则是reset on pwd ~cache:* read write string hash -dangerous ping select。这里的-dangerous放在后面是为了覆盖前面可能带进来的危险命令。有些类别的命令会重叠比如all里包含dangerous所以顺序很重要。管理账号只给运维用能跑INFO、CONFIG GET、CLIENT LIST、CLIENT KILL、ACL LOG。规则写成reset on pwd ~* * admin connection read -dangerous -flushall -flushdb -keys -config|set config|get client|list client|kill acl|log acl|list acl|whoami。这个规则看着长但每一项都有用尤其是-keys它虽然在dangerous里已经被禁了但显式再写一遍是为了防止未来类别定义发生变化。# 三类账号一次性配好 ACL SETUSER svc_read reset on ReadPwd_2024! ~cache:* read ping select ACL SETUSER svc_write reset on WritePwd_2024! ~cache:* read write string hash -dangerous ping select ACL SETUSER ops_admin reset on OpsPwd_2024! ~* * admin connection read -dangerous -keys config|get client|list client|kill acl|log # 确认结果 ACL LIST这里必须强调一个操作禁忌在改default用户之前一定要先用新账号验证一遍能连上、能干活。我见过有人直接ACL SETUSER default off然后发现所有客户端都连不上了因为他们的客户端根本没改成新账号。改default应该是整个迁移的最后一步而不是第一步。3.3 用 aclfile 把权限配置固化下来用ACL SETUSER配的规则只存在于内存里Redis 一重启就全没了。要持久化有两条路写进redis.conf或者用独立的 aclfile。写进redis.conf的语法是user username rules...和ACL SETUSER后面的部分一样。这种方式的缺点很明显改一次权限就得改配置文件改完还得重启或者重载失去了 ACL 热更新的最大优势。所以我推荐用 aclfile。先创建一个文件比如/etc/redis/users.acl然后在redis.conf里加上aclfile /etc/redis/users.acl。文件内容就是ACL LIST的格式每行一个用户user default off user svc_read on #hash1 ~cache:* * read ping select user svc_write on #hash2 ~cache:* * read write string hash -dangerous ping select user ops_admin on #hash3 ~* * admin connection read -dangerous -keys config|get client|list client|kill acl|log这里有个硬性约束必须说清楚一旦配置了aclfile就不能再在redis.conf里用user指令定义用户了两个来源同时存在会导致 Redis 启动失败。所以你要么全用 redis.conf要么全用 aclfile二选一。密码哈希怎么来两个办法。一是在运行中的实例上用ACL SETUSER配好之后执行ACL SAVE它会直接把当前所有用户的规则含哈希写到 aclfile 里。二是用ACL GENPASS生成一个随机密码再用ACL SETUSER写进去。我个人推荐第一种因为ACL SAVE是幂等的能保证内存状态和文件状态一致不容易手抖写错。# 内存里配好之后直接落盘 ACL SAVE # 结果/etc/redis/users.acl 被重写内容与当前内存状态一致 # 改了文件之后重新加载 ACL LOADACL LOAD这个命令在做权限变更的时候特别有用。你可以在本地把规则改好、测试通过然后 scp 到服务器上执行ACL LOAD全量生效不用重启也不用担心ACL SETUSER一条条执行中途出错留下半成品状态。不过要注意ACL LOAD是全量替换语义文件中没写的用户会被删掉所以千万不要在文件里漏掉某个账号。3.4 客户端侧对接与验证方法服务端配好了客户端不改还是白搭。不同语言的客户端对接方式不太一样我挑几个常见的说一下。Java 的 Jedis 和 Lettuce 都支持在连接配置里指定用户名和密码。Jedis 是new Jedis(host, port, user, password)或者用JedisClientConfigLettuce 是RedisURI.Builder.redis(host, port).withAuthentication(user, password)。注意这里有个大坑Redis 6.0 之前的老版本客户端只支持 AUTH password 这种单参数写法6.0 之后要认证到具体用户需要 AUTH username password 双参数写法。如果你的客户端库版本太老它发的是单参数 AUTH那default用户被关掉之后你就彻底连不上了。升级客户端库是必须的别在这上面省事。Python 的 redis-py 用redis.Redis(host..., port..., usernamesvc_read, password...)这个username参数在 redis-py 3.4 之后就支持了。Node.js 的 ioredis 用new Redis({ username, password })。配好之后一定要做回归验证我一般会跑这么几组# 1. 用只读账号验证读能过 redis-cli -u redis://svc_read:ReadPwd_2024!127.0.0.1:6379 GET cache:test # 预期返回 nil 或值不报错 # 2. 用只读账号验证写要失败 redis-cli -u redis://svc_read:ReadPwd_2024!127.0.0.1:6379 SET cache:test 1 # 预期NOPERM this user has no permissions to run the set command # 3. 用只读账号验证越界键要失败 redis-cli -u redis://svc_read:ReadPwd_2024!127.0.0.1:6379 GET other:test # 预期NOPERM this user has no permissions to access one of the keys used as arguments # 4. 用只读账号验证危险命令要失败 redis-cli -u redis://svc_read:ReadPwd_2024!127.0.0.1:6379 FLUSHALL # 预期NOPERM this user has no permissions to run the flushall command这四组测试覆盖了三个维度命令权限、键权限、危险命令。如果这四组都通过了说明你的 ACL 配置基本是对的。如果某一组不符合预期别急着改配置先去看ACL LOG第四章细讲。4. 上线之后最容易遇到的坑与排查实录ACL 的坑基本分三类权限配错了、配对了但客户端不支持、环境本身有限制。第三类最烦人因为报错信息看不出根因。这一章我把踩过的和见过的都整理出来。4.1 权限报错速查表先把最常见的报错和成因列成表遇到问题先对号入座报错信息成因解决方向NOAUTH Authentication required没发 AUTH或者default已被关闭但你还在用它客户端补上用户名密码WRONGPASS invalid username-password pair用户名或密码错了用ACL GETUSER确认用户存在和密码哈希NOPERM this user has no permissions to run the xxx command命令权限不足看ACL LOG的reasoncommand条目NOPERM ... access one of the keys used as arguments键模式没覆盖到看ACL LOG的reasonkey对照键名和模式NOPERM ... channels发布订阅频道模式没覆盖看ACL LOG的reasonchannelNOPERM this user has no permissions to run the select command客户端连上后自动 SELECT 了 DB规则里补selectNOPERM this user has no permissions to run the ping command连接池探活失败规则里补pingERR unknown command ACL使用的 Redis 版本低于 6.0或托管服务禁用了 ACL升级版本或确认服务商是否开放ERR This Redis instance is not configured to use an ACL file想执行ACL SAVE/LOAD但没配 aclfile加上aclfile配置这张表里我特别想强调两条。第一条是ping和select这两个是隐藏杀手。很多连接池在建立连接之后会自动PING一下还有一些老代码连上就SELECT两个命令都不在read或write里。我第一次配只读账号的时候就被这两个卡了一个多小时一直以为是键模式写错了。第二条是ACL SAVE需要 aclfile如果你只用内存方式配了规则ACL L Save会直接报错你以为规则存下来了其实没有重启之后全丢。键模式报错还有个小技巧报错信息里的键名会原样回显你可以直接拿这个键名去和你的规则做比对。但要注意MGET、MSET、DEL这种多键命令报错时只会告诉你其中一个键没权限不会告诉你是哪个。这时候只能靠ACL LOG。4.2 ACL LOG 的正确读法与审计思路ACL LOG是排查 ACL 问题的第一现场它记录的是所有权限校验失败的条目包括认证失败和命令/键/频道拒绝。默认保留 128 条可以ACL LOG count查指定条数ACL LOG RESET清空。127.0.0.1:6379 ACL LOG 2 1) 1) count 2) (integer) 3 3) reason 4) command 5) context 6) toplevel 7) object 8) set 9) username 10) svc_read 11) age-seconds 12) 12.345 13) client-info 14) id15 addr10.0.0.5:52341 fd8 name age120 idle0 flagsN db0 sub0 psub0 multi-1 qbuf26 qbuf-free32738 argv-mem10 obl0 oll0 omem0 tot-mem61456 eventsr cmdset usersvc_read redir-1这个结构里最关键的是reason、object、username三个字段。reason有四种值auth是认证失败command是命令被拒key是键被拒channel是频道被拒。object是触发拒绝的具体对象命令拒绝就是命令名键拒绝就是键名认证失败就是用户名。username是发起操作的用户——注意认证失败的时候这个字段显示的是试图登录的那个用户名哪怕它根本不存在。count字段是聚合计数器同一条拒绝在短时间内重复出现会累加而不是新增条目避免日志被刷爆。age-seconds是第一次出现到现在的时间client-info是完整的客户端连接信息包含来源 IP、连接 ID、当前用的用户。定位是哪个服务在乱调命令的时候看这个字段的addr就够了。我实际的做法是每次权限变更之后先不急着收工观察ACL LOG十分钟。如果出现了预期外的拒绝条目说明有客户端在用我没想到的命令这时候再决定是补权限还是去找服务方改代码。这个方法帮我抓到过好几次某个冷门定时任务在用KEYS扫键的情况。注意ACL LOG的内容是内存态不持久化重启就没了。如果要做长期审计得靠定时轮询把日志捞出来落库。另外ACL LOG本身也是需要权限的普通业务账号看不到只有管理账号或者default才能查。4.3 主从、集群与哨兵环境下的权限同步问题这是 ACL 落地时最容易被忽略的一环ACL 规则不会自动同步。主从复制同步的是数据不是用户配置集群模式的每个节点各自维护自己的 ACL哨兵互相之间也不共享 ACL。这意味着你必须自己在每一台机器上维护一份一致的权限配置。我推荐的做法是用配置管理工具Ansible、SaltStack 之类的统一下发 aclfile然后每台机器执行ACL LOAD。如果规模很小手写一个脚本遍历节点执行也行。关键是要有一份唯一的权限定义源不能这台机器手工改一点那台机器手工改一点否则迟早会出现同一个账号在不同节点行为不一样的诡异问题。主从复制还有一个细节要注意从节点默认replica-read-only yes从节点上的写入本来就会失败这个和 ACL 无关。但如果你给从节点配了只读账号报错信息可能同时包含READONLY和NOPERM别被搞混。集群模式下还有个更麻烦的问题集群命令的重定向。客户端连到节点 A 想访问节点 B 上的键会收到MOVED重定向然后客户端重新连到节点 B 执行命令。这个过程中认证信息会被带上所以每个节点的 ACL 都必须一致否则就会出现第一次访问成功、重定向后失败的间歇性问题。这种问题最难排查因为它不是必现的取决于键分布在哪个槽上。# 集群模式下逐节点验证 ACL 一致性 for node in 10.0.0.1 10.0.0.2 10.0.0.3; do echo $node redis-cli -h $node -p 6379 -a adminpass ACL LIST | sort done # 三个节点的输出排序后应该完全一致5. 几个踩过之后才想明白的经验前面讲的都是应该怎么做这一章讲几个我在实战中交过学费的细节属于文档里不会写、但踩一次痛很久的东西。5.1 密码、文件权限与热更新aclfile 里如果用了明文密码开头这个文件就是一个明文密码仓库。默认权限通常是0644意味着服务器上任何用户都能读到所有业务的 Redis 密码。我在第一次部署的时候就犯了这个错后来是用chmod 600加上chown redis:redis才补上。如果你不想在文件里存明文可以在ACL SAVE之前确认所有密码都是#哈希格式——ACL SAVE写文件的时候本来就写哈希但你手工编辑文件的时候很容易写成明文。ACL SAVE还有个坑它会整个重写文件。如果你手工在文件里加了注释或者按业务分组做了排版ACL SAVE之后全没了。我现在的做法是准备两份一份是手工维护的、带注释的users.acl.tpl一份是 Redis 实际读的users.acl改权限时改模板然后用脚本转换成目标文件最后ACL LOAD。这样既保留了可读性又不担心被覆盖。热更新还有最后一个细节ACL LOAD和ACL SAVE在高并发下会不会阻塞。这两个命令的复杂度是 O(用户数)实际测试下来几十个用户的规模下耗时可忽略不计但理论上是一个原子操作期间不会有命令被执行。如果用户数量特别大几千个建议在低峰期操作。权限什么时候生效这件事我实测下来是这样的ACL SETUSER执行之后立即对所有现有连接生效不需要重连。所以如果你不小心把一个用户的权限删多了正在跑的业务会立刻开始报错而且没有任何缓冲期。这也是为什么我一直坚持在改权限之前先做变更评审并且准备好回滚脚本——回滚脚本其实就是把上一条ACL SETUSER的反向操作或者上一版 aclfile 准备好。5.2 权限粒度设计别一上来就 allkeys我见过不少团队的 ACL 配置是~* * all除了用户名和密码不一样其他和default没区别。这种配置的意义几乎为零出现问题的时候你至少知道是哪个账号干的但除此之外什么防护都没有。真正有价值的 ACL 一定是限制到键前缀的。关于粒度我的经验是分三步走。第一步先按业务模块划分键前缀比如订单用order:*、用户用user:*、会话用sess:*这样每个模块一个账号。第二步在每个模块内部再按读写分读账号和写账号分开。第三步只给确实需要管理能力的账号开管理权限。三步走完之后你会发现 90% 的账号其实只用到read、write和几个数据类型类别admin和dangerous基本没人需要。键前缀的设计有个原则前缀要足够独特避免误伤。~user:*这种太宽泛了如果另一个业务也用user:开头就串了。我一般会要求带上环境或者应用标识比如~prod:order:*、~svc-a:session:*。多一级前缀隔离性就多一层。还有个经常被忽略的点DEL、EXPIRE、TTL这些命令的权限归属。DEL是写操作属于keyspace和writeEXPIRE也是写TTL是读。如果你给某个账号只配了read它连TTL能用但EXPIRE不能用实际业务里经常需要成对出现。我遇到过业务方反馈缓存读得到但续期失败查下来就是EXPIRE没在read里。这种细节只能靠实测别靠猜。最后再分享一个我在做迁移时用的技巧用双账号并行过渡。也就是先给业务加一个新账号 A权限严格同时保留旧账号 B把业务代码改成用 A跑一周观察ACL LOG里 A 账号的拒绝条目如果某一类拒绝是合理的业务确实需要那个命令就补上权限继续观察等一周下来拒绝条目归零再把 B 关掉。整个过程对业务无感也不需要一次改到位出问题随时可以切回 B。这个节奏比一刀切稳得多代价只是多维护一个账号一段时间。至于default用户我的建议是关掉但别删——它删不掉。关掉之后保留一个应急密码写在只有运维能访问的保险箱里平时不用出事故的时候可以临时ACL SETUSER default on 应急密码救场用完立刻关回去。这个应急通道在大规模权限体系里是必须保留的因为你永远不知道哪天会出现所有业务账号都连不上但你必须立刻进 Redis 排查的情况。我自己就遇到过一次原因是 aclfile 被配置管理工具覆盖成了空文件所有用户被ACL LOAD清空全靠 default 的应急密码才进去恢复的。那次之后我把 aclfile 的变更也纳入了审批流程并且在每台机器上备份了上一版文件ACL LOAD之前先做一次备份出问题三秒回滚。