我正在开发一个应用程序,其中用户具有使用不同功能的不同权限(例如,读取,创建,下载,打印,批准等)。预计权限列表不会经常更改。我有几个如何在数据库中存储这些权限的选项。
选项2在哪些情况下会更好?
使用关联表。
User ---- UserId (PK) Name Department
Permission ---- PermissionId (PK) Name
User_Permission ---- UserId (FK) PermissionId (FK)
为每个用户存储一个位掩码。
User ---- UserId (PK) Name Department Permissions
[Flags]
enum Permissions {
Read = 1,
Create = 2,
Download = 4,
Print = 8,
Approve = 16
}
答案 0 :(得分:63)
精彩的问题!
首先,让我们做一些关于“更好”的假设。
我假设你不太关心磁盘空间 - 从空间的角度来看,位掩码是有效的,但是如果你使用的是SQL服务器,我不确定这很重要。
我假设你关心速度。使用计算时,位掩码可以非常快 - 但在查询位掩码时将无法使用索引。这应该不重要,但如果您想知道哪些用户具有创建访问权限,那么您的查询将类似于
select * from user where permsission & CREATE = TRUE
(今天无法访问SQL Server)。由于数学运算,该查询将无法使用索引 - 因此,如果您拥有大量用户,这将非常痛苦。
我假设你关心可维护性。从可维护性的角度来看,位掩码不像存储显式权限那样具有底层问题域的表达能力。您几乎肯定必须跨多个组件(包括数据库)同步位掩码标志的值。并非不可能,但背后的痛苦。
因此,除非有另一种评估“更好”的方法,否则我会说位掩码路由不如将权限存储在规范化数据库结构中。我不同意它会“慢,因为你必须加入” - 除非你有一个完全不正常的数据库,你将无法衡量这一点(而没有活跃索引的好处的查询可能会变得明显即使有几千条记录也会变慢)。
答案 1 :(得分:11)
就个人而言,我会使用一个关联表。
位掩码字段很难查询和加入。
您始终可以将此映射到您的C#标志枚举,如果性能变为并且重构数据库。
过早优化的可读性;)
答案 2 :(得分:5)
存储规范化的权限(即不在位掩码中)。虽然它对于您的场景显然不是要求(特别是如果权限不会经常更改),它将使查询更容易和更明显。
答案 3 :(得分:5)
没有明确的答案,所以做适合你的事情。但这是我的问题:
答案 4 :(得分:1)
我唯一能想到的是当我使用位掩码字段来存储权限时,就是你真的受限于你拥有多少物理内存....就像在旧的移动设备上一样。事实上,你节省的内存量是不值得的。即使在数百万用户中,硬盘空间也很便宜,并且您可以通过使用非位掩码方法(这关于报告谁具有哪些权限等)来更轻松地扩展权限等。
我遇到的一个最大问题是直接在数据库中分配用户权限。我知道您应该尝试使用该应用程序来管理自己,而不是通常使用应用程序数据,但有时候,这只是必要的。除非位掩码实际上是一个字符字段,并且您可以轻松地查看某人拥有的权限而不是整数,请尝试向分析师等解释如何通过更新字段给某人写入权限.....并祈祷你的算术是正确的。
答案 5 :(得分:1)
当他们不改变他们的结构并且总是一起使用时,它将是有用的。这样,您几乎没有到服务器的往返。它们在性能方面也很好,因为您可以在单个赋值变量中影响所有权限。
我个人不喜欢他们......在一些表现强烈的应用程序中,它们仍然被使用。我记得使用这些实现了一个国际象棋AI,因为你可以在一次比较中评估一个棋盘。这对你来说很痛苦。
答案 6 :(得分:1)
我总是存储它标准化的,除非数据库只是为你保存记录,除了检索和保存之外你永远不会做任何事情。这种情况是,如果登录时,将获取用户的权限字符串,并在服务器代码中对其进行处理和缓存。在那种情况下,它的非规范化确实无关紧要。
如果你将它存储在一个字符串中并尝试在数据库级别上进行操作,你必须做一些体操才能获得第X页的权限,这可能很痛苦。
答案 7 :(得分:1)
我建议不要使用位掩码,原因如下:
根据您的查询模式,计划的功能集和数据分布,我会使用您的选项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)
)
答案 8 :(得分:-2)
使用标志枚举(位掩码)可以更快地运行查询,因为您不需要在关联表中包含连接以便理解值。