什么时候将标志存储为位掩码比使用关联表更好?
我正在开发一个应用程序,其中用户具有不同的权限来使用不同的功能(例如读取、创建、下载、打印、批准等)。权限列表预计不会经常更改。我有几个选项可以选择如何将这些权限存储在数据库中。
在什么情况下选项 2 会更好?
选项 1
使用关联表。
User ---- UserId (PK) Name Department
Permission ---- PermissionId (PK) Name
User_Permission ---- UserId (FK) PermissionId (FK)
选项 2
为每个用户存储一个位掩码。
User ---- UserId (PK) Name Department Permissions
[Flags]
enum Permissions {
Read = 1,
Create = 2,
Download = 4,
Print = 8,
Approve = 16
}
I’m working on an application where users have different permissions to use different features (e.g. Read, Create, Download, Print, Approve, etc.). The list of permissions isn’t expected to change often. I have a couple of options of how to store these permissions in the database.
In what cases would Option 2 be better?
Option 1
Use an associative table.
User ---- UserId (PK) Name Department
Permission ---- PermissionId (PK) Name
User_Permission ---- UserId (FK) PermissionId (FK)
Option 2
Store a bitmask for each user.
User ---- UserId (PK) Name Department Permissions
[Flags]
enum Permissions {
Read = 1,
Create = 2,
Download = 4,
Print = 8,
Approve = 16
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
很好的问题!
首先,让我们对“更好”做一些假设。
我假设您不太关心磁盘空间 - 从空间的角度来看,位掩码是有效的,但我不确定如果您使用 SQL Server,这是否重要。
我假设您确实关心速度。使用计算时,位掩码可以非常快 - 但在查询位掩码时您将无法使用索引。这应该不那么重要,但如果您想知道哪些用户具有创建访问权限,您的查询将类似于
(今天在路上没有访问 SQL Server)。由于数学运算,该查询将无法使用索引 - 因此如果您有大量用户,这将非常痛苦。
我假设您关心可维护性。从可维护性的角度来看,位掩码不如存储显式权限那样具有底层问题域的表达能力。您几乎肯定必须跨多个组件(包括数据库)同步位掩码标志的值。不是不可能,只是后背疼痛。
因此,除非有另一种评估“更好”的方法,否则我认为位掩码路由不如将权限存储在规范化的数据库结构中好。我不同意它会“变慢,因为你必须进行连接” - 除非你有一个完全功能失调的数据库,否则你将无法测量这一点(而没有活动索引的好处的查询可能会变得明显即使有几千条记录也会更慢)。
Splendid question!
Firstly, let's make some assumptions about "better".
I'm assuming you don't much care about disk space - a bitmask is efficient from a space point of view, but I'm not sure that matters much if you're using SQL server.
I'm assuming you do care about speed. A bitmask can be very fast when using calculations - but you won't be able to use an index when querying the bitmask. This shouldn't matter all that much, but if you want to know which users have create access, your query would be something like
(haven't got access to SQL Server today, on the road). That query would not be able to use an index because of the mathematical operation - so if you have a huge number of users, this would be quite painful.
I'm assuming you care about maintainability. From a maintainability point of view, the bitmask is not as expressive as the underlying problem domain as storing explicit permissions. You'd almost certainly have to synchronize the value of the bitmask flags across multiple components - including the database. Not impossible, but pain in the backside.
So, unless there's another way of assessing "better", I'd say the bitmask route is not as good as storing the permissions in a normalized database structure. I don't agree that it would be "slower because you have to do a join" - unless you have a totally dysfunctional database, you won't be able to measure this (whereas querying without the benefit of an active index can become noticably slower with even a few thousand records).
就我个人而言,我会使用关联表。
位掩码字段很难查询和连接。
您始终可以将其映射到 C# 标志枚举,并且如果性能出现问题,则重构数据库。
过早优化的可读性;)
Personally, I would use an associative table.
A bitmask field is very difficult to query and join on.
You can always map this to your C# flags enum and if performance becomes and issue refactor the database.
Readability over premature optimization ;)
没有明确的答案,所以做对你有用的事情。但我的问题是:
请使用选项 1
使用选项 2 如果
There is no definitive answer, so do what works for you. But here is my catch:
Use option 1 if
Use option 2 if
存储标准化的权限(即不在位掩码中)。虽然这显然不是您的场景的要求(特别是如果权限不经常更改),但它将使查询变得更容易、更明显。
Store the permissions normalized (i.e. not in a bitmask). While it's obviously not a requirement for your scenario (especially if the permissions won't often change), it will make querying much easier and more obvious.
我建议不要使用位掩码,原因如下:
根据您的查询模式、计划的功能集和数据分布,我会选择您的选项 1,甚至是简单的内容,例如:
添加列是架构修改,但我的猜测是添加特权“清除”,将需要一些代码来配合它,因此权限可能不必像您想象的那样动态。
如果您有一些不良的数据分布,例如 90% 的用户群没有单一权限,则以下模型也可以正常工作(但在进行较大扫描时会崩溃(一个 5 路连接与单个全表)扫描)。
I advice against using a bitmask for the following reasons:
Depending on your query patterns, planned feature set and data distribution I would go with your option 1, or even something simple as:
Adding a column is a schema modification, but my guess is that adding a privilege "Purge", will require some code to go along with it, so the privileges may not have to be as dynamic as you think.
If you have some sick distribution of data, such as 90% of the user base doesn't have a single permission, the following model also works fine (but falls apart when doing larger scans (one 5-way join vs a single full table scan).
我唯一能想到何时使用位掩码字段来存储权限的时候,是当你真的受到你拥有的物理内存的限制时......就像在旧的移动设备上一样。事实上,您节省的内存量并不值得。即使对于数百万用户来说,硬盘空间也很便宜,并且您可以通过使用非位掩码方法更轻松地扩展权限等(这是关于报告谁拥有哪些权限等)
我遇到过的最令人头痛的问题之一与之相关的是直接在数据库中分配用户权限。我知道您应该尝试使用应用程序来管理自身,而不是一般地使用应用程序数据,但有时,这是必要的。除非位掩码实际上是一个字符字段,并且您可以轻松地看到某人拥有什么权限而不是整数,否则请尝试向分析师等解释如何通过更新字段向某人提供写访问权等......并祈祷你的算术是正确的。
The only time I can think of when I would use a bitmask field to store permissions, is when you are really really constrained in how much physical memory you have....like maybe on an old mobile device. In truth, the amount of memory you save isn't worth it. Even at millions of users hard drive space is cheap, and you can expand permissions etc. a lot easier by using the non-bitmask approach (this about reporting off of who has what permissions etc.)
One of this biggest headaches I've run into with it is assigning users permissions directly in the database. I know you should try and use the application to administer itself and not-much with application data in general, but sometimes, it's just necessary. Unless the bitmask is actually a character field, and you can easily see what permissions someone has instead of an integer, try explaining to an analyst etc. how to give write access etc. to someone by updating the field.....and pray your arithmetic is correct.
当它们的结构不会改变并且始终一起使用时,它将很有用。这样,您与服务器之间的往返次数就很少。它们在性能方面也很好,因为您可以影响变量的单个分配中的所有权限。
我个人不喜欢它们......在一些性能密集型应用程序中,它们仍然被使用。我记得使用这些来实现国际象棋人工智能,因为你可以通过一次比较来评估棋盘。使用起来很痛苦。
It'll be useful when they won't change in their structure and will always be used together. That way, you have little round trips to the server. They are also good performance-wise because you can affect all the rights in a single assignation of a variable.
I personally don't like them... In some performance intense application, they're still used. I remember implementing a chess-AI using these because you could evaluate a board in a single comparison.. It's a pain to work with.
我总是将其标准化存储除非数据库只是为您保存记录,并且除了检索和保存之外您永远不会对此执行任何操作。这种情况的一个场景是,如果在登录时获取用户的权限字符串,并在服务器代码中对其进行处理和缓存。在这种情况下,非规范化其实并不重要。
如果您将其存储在字符串中并尝试在数据库级别对其进行处理,则必须进行一些操作才能获得页面 X 的权限,这可能会很痛苦。
I would always store it normalized unless the database is merely holding the record for you, and you will never do anything with this besides retrieving and saving. A scenario for this is if upon login, your user's permission string is fetched, and in server code it is processed and cached. In that case it really doesn't matter too much that it's denormalized.
If you're storing it in a string and trying to do work on it at the DB level, you'd have to do some gymnastics to get the permissions for page X out, which can be painful.
使用标志枚举(位掩码),您的查询将运行得更快,因为您不需要包含关联表的联接来理解该值。
Your queries will run faster using a flags enumeration (bitmask), because you won't need to include a join to the associated table in order to make sense of the value.