76

我正在开发一个应用程序,其中用户有不同的权限来使用不同的功能(例如读取、创建、下载、打印、批准等)。权限列表预计不会经常更改。对于如何将这些权限存储在数据库中,我有几个选择。

在什么情况下选项 2 会更好?

选项1

使用关联表。

用户
----
用户 ID (PK)
姓名
部门
允许
----
权限 ID (PK)
姓名
用户权限
----
用户 ID (FK)
权限 ID (FK)

选项 2

为每个用户存储一个位掩码。

用户
----
用户 ID (PK)
姓名
部门
权限
[Flags]
enum Permissions {
    Read = 1,
    Create = 2,
    Download = 4,
    Print = 8,
    Approve = 16
}
4

9 回答 9

64

绝妙的问题!

首先,让我们对“更好”做一些假设。

我假设您不太关心磁盘空间 - 从空间的角度来看,位掩码是有效的,但如果您使用 SQL Server,我不确定这很重要。

我假设您确实关心速度。使用计算时,位掩码可以非常快 - 但在查询位掩码时您将无法使用索引。这应该不是那么重要,但是如果您想知道哪些用户具有创建访问权限,您的查询将类似于

select * from user where permsission & CREATE = TRUE

(今天无法访问 SQL Server,在路上)。由于数学运算,该查询将无法使用索引 - 因此,如果您有大量用户,这将非常痛苦。

我假设您关心可维护性。从可维护性的角度来看,位掩码不像存储显式权限那样具有潜在问题域的表现力。您几乎可以肯定必须跨多个组​​件(包括数据库)同步位掩码标志的值。不是不可能,而是后背痛。

因此,除非有另一种评估“更好”的方法,否则我会说位掩码路径不如将权限存储在规范化的数据库结构中。我不同意它会“更慢,因为你必须进行连接” - 除非你有一个完全功能失调的数据库,否则你将无法衡量这一点(而没有活动索引的好处的查询可能会变得显着即使有几千条记录也更慢)。

于 2011-04-18T20:15:51.603 回答
12

就个人而言,我会使用关联表。

位掩码字段很难查询和加入。

您始终可以将其映射到您的 C# 标志枚举,如果性能变为并发布重构数据库。

过早优化的可读性;)

于 2011-04-18T20:06:11.650 回答
6

没有明确的答案,所以做适合你的。但这是我的收获:

使用选项 1 如果

  • 您希望权限增长到很多
  • 如果您可能需要对数据库存储过程本身进行权限检查
  • 您不希望有数百万用户,因此表中的记录不会大量增长

使用选项 2 如果

  • 权限将仅限于少数
  • 您期望数百万用户
于 2011-04-18T20:07:54.333 回答
5

存储规范化的权限(即不在位掩码中)。虽然这显然不是您的方案的要求(尤其是在权限不会经常更改的情况下),但它会使查询更容易和更明显。

于 2011-04-18T20:06:52.577 回答
2

我建议不要使用位掩码,原因如下:

  • 索引不能有效使用
  • 查询更难_
  • 可读性/维护受到严重影响
  • 那里的普通开发人员不知道位掩码是什么
  • 灵活性降低(上限为 nr 个位数)

根据您的查询模式、计划的功能集和数据分布,我会选择您的选项 1,甚至是简单的东西:

user_permissions(
   user_id
  ,read     
  ,create   
  ,download 
  ,print    
  ,approve  
  ,primary key(user_id)
);

添加列是一种模式修改,但我的猜测是添加特权“清除”将需要一些代码来配合它,因此特权可能不必像您想象的那样动态。

如果您的数据分布有问题,例如 90% 的用户群没有单一权限,则以下模型也可以正常工作(但在进行较大扫描时会崩溃(一个 5 路连接与单个完整表扫描)。

user_permission_read(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)

user_permission_write(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)

user_permission_etcetera(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)
于 2011-04-18T20:42:28.677 回答
1

我能想到的唯一一次我会使用位掩码字段来存储权限的情况是,当你真正受到物理内存量的限制时......就像在旧的移动设备上一样。事实上,您节省的内存量并不值得。即使在数百万用户中,硬盘空间也很便宜,并且您可以通过使用非位掩码方法更轻松地扩展权限等(这是关于报告谁拥有什么权限等)

我遇到的最大麻烦之一是直接在数据库中分配用户权限。我知道您应该尝试使用应用程序来管理自身,而不是一般地使用应用程序数据,但有时,这只是必要的。除非位掩码实际上是一个字符字段,并且您可以轻松查看某人拥有哪些权限而不是整数,否则请尝试向分析师等解释如何通过更新字段来向某人提供写访问权限等......并祈祷你的算术是正确的。

于 2011-04-18T20:08:25.753 回答
1

当它们的结构不会改变并且将始终一起使用时,它将很有用。这样,您几乎不需要往返服务器。它们在性能方面也很好,因为您可以在一个变量的单个分配中影响所有权限。

我个人不喜欢它们......在一些性能密集型应用程序中,它们仍然被使用。我记得使用这些实现国际象棋AI,因为您可以通过一次比较来评估棋盘。使用起来很痛苦。

于 2011-04-18T20:08:49.813 回答
1

除非数据库只是为您保存记录,否则我将始终将其标准化存储,并且除了检索和保存之外,您将永远不会对此做任何事情。这种情况的一种情况是,如果在登录时,获取用户的权限字符串,并在服务器代码中对其进行处理和缓存。在那种情况下,它的非规范化实际上并不重要。

如果您将它存储在一个字符串中并尝试在数据库级别对其进行处理,则您必须做一些体操才能获得页面 X 的权限,这可能会很痛苦。

于 2011-04-18T20:15:04.417 回答
-2

使用标志枚举(位掩码)您的查询将运行得更快,因为您无需包含关联表的连接即可理解该值。

于 2011-04-18T20:06:35.857 回答