在数据库中共享一个主键序列?


14

将单个序列用作所有表的主键是否可以接受(不是主键对于给定表是唯一的,而是所有表都是唯一的)?如果是这样,从客观上讲,它比在表中使用单个主键序列更好。

我是一名初级软件开发人员,而不是DBA,因此我仍在学习良好数据库设计的许多基础知识。

编辑:如果有人想知道的话,我最近读了我们公司的一名DBA对数据库设计的评论,他提到了一个问题,即设计没有在整个数据库中使用单个主键,这听起来与事实不同。到目前为止,我已经学到了。

Edit2:要回答注释中的问题,这是针对Oracle 11g的,但是我想知道的是非数据库特定级别。如果这个问题确实取决于数据库,我很想知道为什么,但是在这种情况下,我将寻找针对Oracle的答案。


2
由于性能原因,这通常是一个糟糕的主意。
菲尔(Philᵀᴹ)2013年

1
实际上,使每个表具有其自己的独立主键范围会带来更大的好处。但是只有当您查看一堆ID时,您才可以说,这是Accounts,那个是PurchaseHeader,等等。执行此操作需要进行一些设置,并且(如任何特殊用途的东西)需要进行一些日常护理和喂养。(是的,很多年前我就使用过这样的系统。)
RLF 2013年

您正在使用哪个DBMS?甲骨文?Postgres?DB2?
a_horse_with_no_name

1
你可能会曲解他的意思吗?也许他不是那样的人吗?
JamesRyan 2013年

公司DBA实际上是否意味着任何表中都没有主键字段?
Max Vernon

Answers:


13

可以接受吗 当然。共同?不,有益吗?疑。

在我以前的工作中,我们继承了一个具有中央序列生成器的系统(这是SQL Server系统,早SEQUENCE在SQL Server 2012中就已引入)。这并不是真正的性能瓶颈,除非您每秒产生数十万个值,否则不应该成为性能瓶颈。但这没有理由就使所有代码都比原来复杂得多。设计的目的是要确保如果系统中某个对象的ID值分配为12,则系统中只有一个对象才能具有ID12。这对我来说似乎很晦涩,我从不理解。如果我有一个CustomerID = 12的客户,为什么这会阻止我订购OrderID = 12的订单?

如果您有多个系统,并且正在从这些多个系统中为特定类型的实体(例如,客户或订单)生成ID,我确实会看到中央序列生成器的有用性。中央序列可以将新值分配给多个系统而不会成为瓶颈(仅是单点故障),而不必担心两个系统会生成相同的ID。


如果您必须在这样的选择之间进行选择,而仅使用uniqueidentifiers作为主键,您是否会有偏好(尽管答案很可能“取决于”)?看起来GUID将以相同的方式解决该问题,除了您将获得标准的实现方式而不必滚动自己的集中式主键生成器。显然,在SQL 2012中使用序列可以完成两项工作,但是假设有人使用的是旧版本?
SqlRyan

2
@SqlRyan我需要了解为什么OrderID必须与CustomerID完全不同。我几乎可以肯定不会使用GUID。设置IDENTITY范围可能会更好(客户从1开始,订单从1000000开始,依此类推),并且当您快要耗尽该范围时会发出警报。
亚伦·伯特兰

1
@SqlRyan-使用执行不佳的GUID作为群集主键可能会导致各种问题。正如亚伦(Aaron)所说,“身份”更符合目标。
Max Vernon

在以前的系统中,我看到在整个数据库中使用单个序列,这样做是为了使外键指向多个不同的表而不是单个表,因此当您说两个不同行的外键时12岁时,您知道他们指向同一件事,而无需检查他们指向的表。同一列中的13可能是不同表上的主键。我个人对该设计风格感到非常不舒服。
Lawtonfogle 2013年

@AaronBertrand或使用简单的整数标识符,并在面向客户时将一些代码附加到开头。例如。I1337,C1337显然是发票或客户
JamesRyan

7

这个想法在一个非常复杂的数据库中很有用,因为人们可能会因为INT ID相同而意外地使用错误的列将其加入到表中并获得无效的行。

我们选择将顺序的GUID作为我们的主键,以避免GUID的某些索引碎片陷阱。可悲的是它们很大。

SQL Server可以通过默认调用newSequentialID()函数的方式生成顺序GUID,因此没有要维护的已发布密钥表,也没有阻塞瓶颈。

实际上,这给了我们整个数据库,整个企业中唯一的ID,因为它们确实是唯一的。

当您尝试将数据传送到数据仓库/多维数据集时,价格和空间是成问题的,而速度/大小取决于使用较小的整数键。

我坚信,由于使用了它们,我们避免了应用程序中的许多错误。


4

我无法想象所有表中的单个序列背后的原因是什么。它所做的只是在生成新值时创建瓶颈。

无论生成顺序键值的开销有多小,生成器都是单个资源,对其访问必须进行同步。它收到的请求越多,某些请求者不得不等待水龙头轮到的机会就越高。显然,与多个生成器中的任何一个相比,更多客户端将更频繁地访问所有表之间共享的单个序列生成器,从而产生更多的竞争。如果业务规则对生成的值施加约束(例如,不存在间隙或严格排序)或在集群数据库中,则争用可能会变得更加明显。

即使使用最高效的序列生成器,也会有导致无法忍受的争用的工作量。


2
您可能想添加有关如何创建瓶颈以及为什么这是一个坏主意的详细信息。
Max Vernon13年

2

Database表中PrimaryKey的目的主要是为了增强假定唯一的数据的唯一性,因为所有工作流都无法覆盖并确保不会导致数据重复。第二个原因是,很多时候PK还是表上聚集索引的主要候选者,因此,当在选择查询中正确使用这些列时/在哪里使用PK,它也促进了数据检索。

使用序列号作为主键与每个表的“标识”列相同,并且在主键中仅使用该列。在数据库中具有单个序列号必须具有某些特定用法,但是从PrimaryKey的角度来看,我不理解原因。例如,在我从事的Datawarehouse项目之一中,我们有一个称为LoadBatchID的列,从ETL到报告所有表的50%都有此列,但在某些地方它具有不同的含义。我们使用唯一的proc作为数字生成器,以确保我们不会发现冲突,还可以帮助我们追溯到原始文件,即数据的来源以及ETL每个不同阶段发生的情况。


2

我想这样做的原因之一是,如果所有实体都继承自某个父实体。假设您想对任何类型的实体发表评论:

create table god_entity (
  id bigserial primary key
);

create table some_table (
  id bigint primary key references god_entity(id),
  ...
);

create table some_other_table (
  id bigint primary key references god_entity(id),
  ...
);

create table comment (
  id bigint primary key references god_entity(id),
  ...
);

create table entity_comment (
  entity_id bigint not null references god_entity(id),
  comment_id bigint not null references god_entity(id),

  primary key (entity_id, comment_id)
);

通常这不会完成。。

不了解性能特征。

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.