代码之家  ›  专栏  ›  技术社区  ›  Denis Biondic

数据库中用于生成主键的表?

  •  7
  • Denis Biondic  · 技术社区  · 16 年前

    您是否曾经使用单独的表为DB“生成”人工主键(以及为什么)?我的意思是,有一个表有两列,表名和当前ID——通过它,您可以通过简单地用表名锁定行、获取键的当前值、增加一个值以及解锁行来为某些表获取新的“ID”。为什么您更喜欢这个而不是标准的整数标识列?

    P.S.“理念”来源于Fowlers的企业应用架构模式。

    6 回复  |  直到 14 年前
        1
  •  10
  •   Will Marcouiller    16 年前

    这叫做高/低分配。

    您可以这样做,要么在表上插入触发器,从该表中获取ID,然后在获取ID之前或之后递增,具体取决于您的选择。

    这通常在必须处理多个数据库引擎时使用。Oracle中的自动增量标识符是通过一个序列来实现的,该序列使用sequence.nextvalue从数据表的before insert触发器中递增。

    相反,SQL Server有标识列,自动增量是本机的,这是由DBE本身管理的。

    为了让您的软件在这两个DBE上工作,您必须达到某种标准,然后最常用的“标准”就是对主键的hi/lo分配。

    这是其中一种方法。现在,有了诸如nhibernate这样的ORM映射工具,它可以通过配置来提供,这样您就不需要太关心应用程序和数据库方面了。

    编辑第1页

    因为这种maneuvre不能用于全局范围,所以必须为每个数据库或数据库模式提供这样的表。这样,每个模式都独立于其他模式。但是,一个模式中的数据不能隐式地移动到具有相同键的另一个模式中,因为它可能与已经存在的行冲突。

    对于安全模式,它与另一个模式或用户访问同一个数据库,因此不应该为特定的安全模式存在其他表。

        2
  •  5
  •   Joel Coehoorn    16 年前

    只要您可以使用SQL Server的标识或guid功能,就应该这样做。然而,在一些情况下,这可能是不可能的。

    一个例子是,SQL Server只允许每个表有一个标识列。很少情况下,一个表将有同时需要私有ID和公共ID的记录,并且一个标识列的限制意味着将两者生成为整数可能很麻烦。您可以始终使用一个guid作为一个guid,但是为了提高速度,您希望private id上的整数,并且您可能还希望public id比guid更易于读取。

    在这种情况下,生成ID的额外表是有意义的。不过,我会做得有点不同。表中仍然有两列,但是为每个实表创建一个“shadow”或“id mapping”表。其中一列将是您的私有ID(唯一约束),另一列将是您的公共ID(增量值可能为“7”或“13”或其他不明显于“1”的数字)。

    这里的关键区别在于你不想自己锁起来。让SQL Server处理它。

        3
  •  3
  •   kemiller2002    16 年前

    我唯一一次使用它是在btrieve中有一个应用程序,它没有标识列。我还应该说,当他们试图使用这个表时,由于所有额外的读和写,当他们试图导入数据时,它导致了巨大的速度减慢。我的朋友看了看,并重新写了他们是如何加快速度的,但这个故事的寓意是,如果你不正确地做这样的事情,可能会有残酷的后果。

    就我个人而言,我不想这样做。错误的可能性太大了。两个人试图使用同一把钥匙,因为他们在抓取ID之前忘记了锁定表。这看起来像是应该留给RDBMS的东西,如果可能的话。正如威尔所说,很容易将这种情况降到最低,但如果你不知道自己在做什么,可能会发生这种情况。

        4
  •  2
  •   gbn    16 年前

    你一点也不喜欢。

    无论您使用该模式获得什么,或者成为数据库不可知论者,您都会在头痛、支持和性能方面损失。

        5
  •  2
  •   Remus Rusanu    16 年前

    用该表名锁定行, 获取密钥的当前值, 将其递增一,然后解锁 行

    这听起来很简单,不是吗?

       UPDATE TableOfId
         SET Id += 1
       OUTPUT Inserted.Id
       WHERE Name = @Name;
    

    实际上,这是一场灾难。应用程序中没有作为独立操作发生的活动:所有操作都是事务的一部分。不能简单地“解锁”行,因为“解锁”实际上只在提交时发生。这意味着所有需要表上ID的事务都将被序列化,并且在任何时候只有一个事务可以继续。它还意味着访问多个表的事务在更新ID表时可能会死锁,因为在实践中很难执行“获取下一个ID”的更新顺序。

    为了避免完整的序列化,需要获取独立事务(通常是更新本身的隐式自动提交事务)上的ID。但这使应用程序逻辑极其复杂。每个操作都需要维护到数据库的两个独立连接,一个用于执行正常事务逻辑,另一个用于获取所需的ID。即便如此,ID的更新也会成为一个热点,它仍然会导致可见的争用和阻塞(类似于在Web应用程序中流行的可怕的“更新页面命中数+1”)。

    简而言之:使用身份。身份生成针对高并发性进行了优化。

        6
  •  1
  •   Jennifer Zouak    16 年前

    我已经看到了在一个数据库中创建的数据需要迁移、备份、集群或转移到另一个数据库时使用的这种模式。在这种情况下,首先要确保主键不需要更改。其次是外挂钥匙。第三,外部暴露的钥匙或持久的参考。