什么时候将标志存储为位掩码比使用关联表更好?



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

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

选项1

使用关联表

<>之前用户----UserId (PK)名字部门之前<>之前许可----PermissionId (PK)之前<>以前User_Permission----UserId(颗)PermissionId(颗)之前

选项2

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

<>之前用户----UserId (PK)名字部门之前的权限
[Flags]
enum Permissions {
    Read = 1,
    Create = 2,
    Download = 4,
    Print = 8,
    Approve = 16
}

问得好!

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

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

我想你是在乎速度吧。在使用计算时,位掩码可以非常快——但是在查询位掩码时,您将无法使用索引。这应该无关紧要,但是如果您想知道哪些用户具有创建权限,那么您的查询应该是类似

这样的内容。
select * from user where permsission & CREATE = TRUE

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

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

所以,除非有另一种评估"更好"的方法,否则我认为位掩码路由不如将权限存储在规范化的数据库结构中好。我不同意它会"因为必须执行连接而变慢"的说法——除非您的数据库完全不正常,否则您将无法测量这一点(而没有活动索引的好处的查询即使只有几千条记录也会变得明显变慢)。

我个人倾向于使用关联表

位掩码字段很难查询和连接。

你总是可以把它映射到你的c#标志枚举,如果性能变得和问题重构数据库。

可读性胜过过早优化;)

没有明确的答案,所以做适合你的。但我的问题是:

如果

使用选项1
  • 您希望权限增长到许多
  • 如果您可能需要在数据库存储过程本身中执行权限检查
  • 你不希望有数百万的用户,所以表中的记录不会大量增长

如果

使用选项2
  • 权限将被限制为少数
  • 你期望有数百万用户

存储规范化的权限(即不在位掩码中)。虽然这显然不是您的场景的需求(特别是如果权限不会经常更改),但它将使查询变得更容易和更明显。

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

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

根据你的查询模式,计划的功能集和数据分布,我会选择你的选项1,甚至一些简单的:

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

添加一个列是一个模式修改,但我的猜测是,添加一个特权"Purge",将需要一些代码来配合它,所以特权可能不必像你想象的那样动态。

如果你有一些病态的数据分布,比如90%的用户群没有一个权限,下面的模型也可以工作得很好(但是在进行更大的扫描时(一个5-way连接vs一个完整的表扫描)。

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)
)

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

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

当它们的结构不会改变并且总是一起使用时,它将非常有用。这样,您就可以往返于服务器之间。它们在性能方面也很好,因为你可以在一个变量的单个赋值中影响所有的权限。

我个人不喜欢他们…在一些性能要求很高的应用中,它们仍然被使用。我记得我使用这些方法执行了一款国际象棋ai,因为你可以在一次比较中评估一个棋盘。和它一起工作很痛苦。

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

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

使用flags枚举(bitmask)您的查询将运行得更快,因为您不需要为了使值有意义而包含到关联表的连接。

最新更新