权限系统两种数据库表设计方案:哪种实现更高效?
两种权限设计方案的效率对比
作为在权限系统上踩过不少坑的开发者,我来聊聊这两种方案在效率层面的差异——其实没有绝对的优劣,得看你的业务场景。先明确下两种模式的叫法,方便后面讨论:
- 宽表权限模式:就是你最开始用的,每个权限对应表的一列,用0/1标识是否开启
id|name|can_do_this|can_do_that 1|admin|1|1 2|manager|1|0 3|user|0|0 - EAV(实体-属性-值)权限模式:教授推荐的,每条记录对应一个用户的单个权限状态
id|user_id|privilege|active 1|1|can_do_this|1 2|1|can_do_that|1 3|2|can_do_this|1 4|2|can_do_that|0
查询效率:宽表完胜常规场景
如果你的业务经常需要一次性获取某个用户的所有权限,宽表的效率要高得多:
- 只需要一次
SELECT * FROM roles WHERE id = ?就能拿到所有权限列,直接返回结果,应用层不用做额外处理。 - 而EAV模式得先查
SELECT privilege, active FROM user_privileges WHERE user_id = ?,然后还要在应用层把零散的行转换成键值对(比如把can_do_this:1这种记录拼成一个权限对象),多了一层处理开销;要是同时检查多个权限,还得写复杂的条件或者聚合查询,性能肯定不如宽表直接取列判断。
当然,如果你的业务大部分场景只需要检查单个权限,那两者的查询效率差距不大,但现实中多数系统都是需要批量获取权限的。
写入/更新效率:各有胜负
- 批量设置权限:宽表更高效。比如给一个用户配置10个权限,宽表只需要一次
UPDATE语句修改10列;EAV模式则得执行10条INSERT/UPDATE(就算用批量插入,也比单条UPDATE繁琐)。 - 单个权限更新:两者差距极小。EAV模式改单条记录,宽表改单列,在现代数据库里这点差异几乎可以忽略,除非你的权限数量多到宽表的行数据特别大,才会有轻微的IO差距。
存储效率:宽表更紧凑
- 宽表的每条记录把所有权限存在一起,没有冗余字段(比如EAV里重复出现的
user_id和privilege字符串),权限数量固定的话,存储占用更小。 - EAV模式每条记录都要重复存储用户ID和权限名称,当用户数、权限数变多的时候,存储量会显著膨胀,而且索引开销也更大(比如
user_id+privilege的联合索引,数据量比宽表的主键索引大很多)。
扩展效率:EAV模式碾压
这也是你教授强调的核心优势:
- 宽表新增权限必须执行
ALTER TABLE ADD COLUMN,这个操作在大表上特别耗时(甚至会锁表导致业务中断),而且权限越多表结构越臃肿,后续维护成本极高。 - EAV模式新增权限只需要插入新的记录,完全不用改表结构,对于快速迭代的项目来说,灵活性拉满。
总结:根据业务场景选
- 如果你的权限体系非常稳定,几乎不会新增权限,而且需要频繁批量查询权限,那宽表模式在效率上更优。
- 如果你的权限体系经常变动,需要快速新增权限,哪怕牺牲一点查询和存储效率,EAV模式(或者它的变种,比如加个权限组表做关联)会更适合长期维护。
另外给你个折中方案参考:可以用权限组+用户权限覆盖的模式——先建一个权限组表(用宽表模式),再建一个用户权限表(用EAV模式),用户的最终权限优先取用户权限表的设置,没有的话继承组权限。这样既兼顾了查询效率(大部分用户用组权限,一次查询搞定),又保留了扩展灵活性(新增权限改组表,或者用户单独设置权限用EAV)。
内容的提问来源于stack exchange,提问作者Tarek Sawah
相关产品推荐
相关产品推荐

