在数据库中使用位掩码的优缺点


22

不久前,我与同事交谈,他绝对反对使用位掩码,因为很难理解存储在数据库中的所有值。我认为使用它们(例如确定当前用户的角色)并不总是一个坏主意。否则,您需要将其存储在单独的表中,这将导致另一个JOIN。如果我错了,你能告诉我吗?使用位掩码还有其他副作用,优点/缺点吗?


2
让数据库在内部创建位掩码并将位显示为单独的列会更有意义。您的要求可能会发生变化。
西蒙·里希特

1
如果不使用联接,则不会按照预期的方式使用关系数据库。
Pieter B

Answers:


38

我使用一个使用位掩码存储用户角色分配的应用程序。这是一个痛苦的屁股。如果这使我产生偏见,则被判有罪。

如果您已经在使用关系数据库,则它是一种违反大多数关系理论和所有规范化规则的反模式。当您构建自己的数据存储时,这可能不是一个坏主意。

有太多的表被连接,但是建立了关系数据库来处理这个问题。如果性能成为问题,许多文件将具有附加功能:索引,索引视图等。即使您查找的值不会经常更改,这对Bitmask来说是一个优点,但管理索引的开销是在数据库上非常容易。

尽管数据库在汇总数据方面做得很好,但是当您开始将复杂公式或标量函数之类的内容引入数据集时,它们可能会变得缓慢。您可以在应用程序中按位进行操作,但是如果您所做的只是获取相关数据(查找用户的角色),那么您就无法利用数据存储的最佳性能。

我最后反对这一观点的理由是,对于其他开发人员而言,这很简单。您有用户,角色和分配。这是一个非常常见的多对多关系集(因为存在多个关系),因此应该易于管理。这只是CRUD的东西。


8
关系数据库是位掩码最差的地方。存储成本不再那么糟糕了,只需几次联接和一张额外的桌子就可以打败您。当然,这使得所有事情都难以推理。将权限以位(1/0)的形式存储在数据库中自己的表中,并用带有but标志的代码表示它们。似乎相当合适且可行。开发人员可以得到简单的标志,而dbas具有标准化的表。大家都开心。
Mike McMahon

3
同意,我曾经支持过一个使用位掩码在其数据库中充当用户角色和特权的应用程序。那是一场噩梦。使用32位int,我们用完了位,因此有人有个好主意,添加了更多的位掩码,然后有重叠,因此,一列中的位4表示另一列中的位8,这样他们就失去了同步。是啊 索引很困难,因为索引存储的是离散的列值,而不是其中的各个位,因此如果没有where some_bit_mask & 12 > 0逐行扫描,就无法搜索行。
布兰登

一天结束时,多对多user_role_mapuser_priv_map桌子就足够了。
布兰登

@MikeMcMahon,能否请您更深入地研究表设计,我应该如何在代码中映射它以获得您所讨论的结果?
Alex Ovechkin

2
@usr-永不言败。当然可以使用位掩码,但是我不会在使用关系数据库的应用程序中使用它们。处理旧数据或对速度的超级需求时,可能会遇到一些极端情况。
JeffO

24

您已经命名了相关的利弊:

  • 位字段可节省空间。
  • 它们将数据存储在记录本身中,因此您不需要JOIN即可找到它们。(但是记录中的各个标志字段将执行相同的操作。)
  • 如果您想高效地处理原始SQL输出,则它们的可读性很差。

决定做什么需要更多信息:

  • 您的用例的磁盘空间到底有多少?
  • 您实际上是否经常阅读用户角色,以至于无法及时加入这些角色?
  • 是否要读取SQL输出并据此做出决定-还是不可读的数据库记录不重要,就像您的系统机器代码不可读一样?

因此,您要做的就是收集风险因素,然后对它们进行加权,以查看利弊是否超过了利弊。


谢谢您的回答,完全同意您的想法,但总的来说,这是不是反模式?您在项目中使用遮罩吗?
Alex Ovechkin

12
@Alex 没有“最佳实践”之类的东西可以决定您的情况。如果空间非常短缺,则最好使用位字段。如果要在向CEO提交的报告中使用SQL输出,则最好使用口头表达。但是是唯一了解这些情况的人,因此社区无法为您提供永远有效的处方。
凯莉安·佛斯

将space参数视为“ gimme”。是否使用位掩码的问题是存在还是落在其是否会在此之上带来任何好处。
罗比·迪

您还需要处理数据库中的信息吗?还是总是在使用它之前将其读入应用程序。
伊恩

1
“您是要读取SQL输出并据此做出决定吗?还是不可读的数据库记录不重要,就像您的系统的机器代码不可读一样?” 我想我不能代表所有开发人员,但是当我进行开发时,对我来说,开始从数据库中选择数据来理解或检查某些东西是非常普遍的。因此,我认为通常,对此的答案是:“是的,有人会。”
jpmc26 2016年

18

如果你真的,要真的真的很紧张的磁盘空间,那么你可能会考虑对用户权限的位图。如果您担心性能,那么请完全不要考虑它们,因为将它们分开实际上会比较慢。您无法有意义地对位映射的字段建立索引,从而导致数据库表扫描,这几乎总是导致性能下降。

除非您是Amazon或Netflix,否则与您拥有的所有其他资源相比,与用户权限有关的数据可以忽略不计。

任何认真的DBMS都可以处理该“额外加入”而不会闪烁。


7
+1:优秀的关系数据库是由非常,非常,非常擅长做事的人开发的。任何需要使用比特字段来获取性能最后一点的人都不需要问这个问题。对数据建模,然后查找不起作用的零件。
Blrfl

拥有联接将使应用程序代码更加复杂,因此在很多地方处理角色。
伊恩

4
@Ian的加入似乎并不比需要知道如何解密位屏蔽的权限更为复杂。
布拉德(Brad)2013年

@Brad,想想一个枚举,它是C#中的一组标志,其值按“原样”存储在数据库中,因此C#Cold无法简单化。如果使用联接,则C#代码必须处理“一对多”的关系。
伊恩

我还应该补充一点,如果您在一个表中有多个布尔列,那么大多数数据库都会弄清楚如何将它们压缩到尽可能少的空间中,并将为您解决纠结。
Blrfl 2016年

8

当存储昂贵时,带位掩码的好处是节省了空间。在大数据时代,这不再是曾经的问题。

以您举的例子为例-从数据库设计的角度来看,将角色存储为位掩码将是某种代码味道,因为它违反了第一范式。从这个意义上讲,它们是一种反模式。

话虽这么说,但不一定非要一个。您可以将数据存储为位掩码,然后具有可以动态拉动用户角色的视图。然后,您还可以一目了然地检查哪些用户具有相同的角色。


2

使用位掩码的唯一好处是,如果位字段的含义不是静态的。关系表只有在您提前知道记录中每个字段的内容时才能很好地工作:CREATE TABLE毕竟,您必须在DDL语句中标识这些字段。

如果每个位字段的含义在运行时都是可配置的,或者提前未知,则将布尔值存储为位字段可能有意义。即使这样,它可以定义任意字段的表:field_1field_2,等这给你一个更清洁的关系设计,虽然仍然不理想。由于这两种解决方案都不理想,因此是否优先使用位字段在很大程度上取决于意见。

如果您知道位在开发过程中代表什么,那么请为每个位创建字段并为其赋予有意义的名称

只是要注意内部平台效果。如果您最终定义了任意但类型良好的字段,那是一回事,但是如果走得太远,您将在关系数据库内部重新创建关系数据库。


2

我对位掩码很矛盾。我发现他们的大多数批评者都不了解二进制和十六进制。为了清楚起见,请使用良好的助记符。

上面没有提到的一个优点是能够为位掩码添加新含义,而无需花费大量时间来添加新列。我们的数据库设计师(在我之前)将它们放在一个表中,现在每天可获得500万条新记录。添加新列来表示新行为将花费很长时间,而定义新位(我们使用了64中的33)不需要重建表。

不,不能对位掩码进行索引,但是建立33个索引将是荒谬的,并且会减慢对爬网的插入。表搜索使用日期和记录“所有者”索引,因此,如果可能的话,将永远不会使用此位掩码上的索引。


这是一个有趣的案例。我想您可以通过在表上定义“备用”列,然后根据需要将其投入使用,以一种简洁明了的方式实现相同目的。然后,您至少可以选择索引这些列(如果选择这样做)。
史蒂夫

1

如果目标只是节省一些磁盘空间,我认为这是个坏主意:

  • 看今天GB的成本,
  • 将其与编写报告和查询并必须弄清楚现场内容以及如何解决特定问题的人员的时间成本进行比较,成本/收益比较可能会在错误的一端结束。
  • 如果您使用的是SQL数据库,则许多查询中所需的其他位访问操作也可能会消耗不必要的计算时间

但是,在某些情况下,可以确定位字段的使用:

  • 如果您的位代表一组复杂的标志,并且您始终将它们作为一个整体进行处理,
  • 如果您需要在这些集合上应用一些模式匹配算法,甚至更多,
  • 尤其是如果该数据不在最常用的选择标准中。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.