2

我们正在创建一个包含 20 多个表的新数据库,我们的数据库支持:

  • 序列。
  • 身份列(始终作为身份/序列生成)。

所以,问题是:我们应该使用序列还是身份?哪一个更好?团队似乎在这一点上存在分歧,所以我想听听利弊以帮助决定。

添加数据库详细信息:

  • 我们正在 IBM DB2 上创建新数据库,但我们需要确保它与未来迁移到 PostgreSQL 的计划兼容。
4

5 回答 5

2

您的问题是关于使用序列与身份(可能是“始终作为身份生成”列)。在 Postgres 中,这些将被声明为serial. 这些总是单列中的某种数字。

从数据库性能的角度来看,两者并没有太大的区别。一个重要的区别是一些数据库将缓存标识列,这可以加快插入速度但会导致间隙。缓存序列的规则可能不同。在高事务环境中,缓存不足可能成为性能瓶颈。跨多个表共享一个序列会使这个问题变得更糟。

从数据管理的角度来看,存在更大的差异。一个序列需要管理两个对象(表和序列)。表中内置了一个identityserial列。

对于单个表,我只考虑在不支持内置自动增量/序列/标识列(咳咳,“Oracle”)的数据库中使用序列。否则,我会使用设计用于处理表格的机制。

我确实想指出,使用自动递增代理键还有其他好处。如果数据库中存在这样的概念,这也应该是用于对数据进行聚类的键。然后,新插入始终位于“末尾”(尽管如果您要删除数据,则页面可能仅被部分使用)。主键也应该是用于外键引用的唯一键,即使其他列(单独或一起)是唯一的候选主键。

于 2018-04-06T17:43:45.530 回答
1

最好的答案是让你回到你的情况。

首先,许多人更喜欢序列,因为它们易于生成并提供单一数据类型来导航您的连接。此外,许多商店需要单列主键来帮助进一步提高代码复杂性。

让我们谈谈缺点:

序列:当使用 b-tree 索引时,序列通常以升序插入,这可能会导致“不平衡树”,并随着时间的推移导致性能不完美(在 b-tree 索引上)。有时,人们会生成散列或 GUID 以生成更平衡的树。

使用“查找表”时,序列可能会导致“难以阅读”的代码,尤其是在数据库中对值进行硬编码时。示例:“where status_seq=1”比“where status_id='ACTIVE'”更难阅读。

使用 ID 的缺点:混合数据类型会导致混淆。有时它们是数字,有时它们是 varchar 或 char。许多 ORM 会混淆这些并省略导致结果错误的前导零。IE 01234 != 1234,但您的 ORM 可能会返回 1234 而不是 01234。

许多人以人类可读的形式存储 ID,例如“VALID”或州缩写。从长远来看,这可能会让人头疼,因此即使您确实在桌子上使用了 ID,您也可能希望避免将这些 ID 直接显示给您的客户。

与序列相比,ID 字段在未来“需要更改”的可能性更大。示例:假设您有一个国家/地区代码表,并且发生了革命并且国家/地区代码发生​​了变化。你真的想通过主表和引用它的所有外键,输入新的国家代码 - 还是使用旧的国家代码,因为这是你的选择。如果在这种情况下使用序列,只需更新基表中的其他非键列,就可以了。

好处:

序列的好处:序列本质上是自动生成的。ID 并不总是如此。添加记录时,您真的希望程序员或用户命名一个不易更改的 ID 吗?当您使用序列时,很少需要重新编号,并且如果出现错误,可以轻松更改底层的人类可读数据。

如上所述,它们始终是数字数据类型,如果使用得当,可以帮助“导航”您的应用程序(IE,通常只需“传递”一个数字即可导航您的表结构)

在 DB 和您的编程语言之间使用通信时,您可以指望能够将整数转换为整数,而不会出现任何奇怪的数据转换问题。

ID:主要好处是代码更易于阅读,我们已经在上面解释过。

总而言之,我认为这取决于具体情况,具体取决于表和列的使用情况。如果您打算使用 ID,请避免向用户显示值的诱惑。如果表不会改变并且只是保存标志或“枚举”类型的数据,那么 ID 肯定有助于提高代码的可读性。否则,序列通常是数据可维护性的更好选择。

有些人选择 GUID 或 ID 来帮助提高索引性能,但就个人而言,如果代码可读性下降或代码变得更复杂,我会在编写更复杂的代码之前花一些钱购买更好的硬件——因为好处是微乎其微的。

资料来源:Oracle 认证 DBA(针对该主题进行培训),以及 20 多年与开发人员和企业数据库合作的经验。

于 2018-04-06T16:58:47.183 回答
0

我是序列的粉丝。如果所有的 ID 都是相同的类型,并且所有的 ID 都来自相同的序列,我会喜欢它。这不是必需的,只是可以让您梳理出事情发生的顺序……这通常不是技术要求,而是调试帮助。我倾向于喜欢 bigint 作为我的关键类型,所以我几乎可以保证永远不会用完 ID。如果您使用 int 键(或更小),您可能希望每个表使用一个序列。

话虽如此,序列还是有一些问题需要注意。例如,可以“刻录”序列而不将它们实际放入数据中。同样,这可能是也可能不是问题。一般来说,我不必在意。

序列通常通过对表的 ID 列进行默认约束来实现。这意味着需要注意几件事。有可能在插入时实际上提供了列的值......这不会“碰撞”您的序列,并且可能与未来不提供值的插入发生冲突。对我来说,这是最重要的问题。如果您的所有 ID 都是默认提供的,那么这不是问题。

过程(和远程客户端)可以保留或“刻录”序列。这非常方便...让您的程序代码提前知道 ID 是什么,而无需将它们提交给数据。您总是可以执行以下操作:

insert someTempTable( Id, Name )
select
  next value for dbo.MySequence,
  Name
from
  dbo.SomeTable

...这会烧掉序列值,但好处是,当我将工作表中的行插入someTempTable真实表中时,我可以放心,ID 不会发生冲突。这比使用基于标识列的 ID 更简单。我可以在 temp 中构建一整套相关数据,然后将它们全部移动到永久存储中。这通常效率更高。

于 2018-04-06T16:33:18.520 回答
0

我没有使用过序列,但我可以讨论身份字段。

首先,在我过去 18 年使用 SQL Server 的所有情况下,它们都能很好地工作。这在其他数据库上很可能是正确的,这对于使用它们的数据库来说是一个关键特性。我们从来没有遇到过关于使用身份的任何问题。如果您希望拥有一个非常大的数据库,您可能希望在设置它时将标识定义为 big int。

如果您在创建表时没有设置身份,那么稍后在 SQl Server 中设置它会很痛苦,请在此处检查您的数据库以获取详细信息。但是,如果您仅将自动生成的密钥用作 PK,则应在创建表时执行此操作。

使用身份(或与此相关的序列或 GUID)时的一个关键问题是,除了自动生成的值之外,如果您有自然键,您还需要为表中的自然键创建唯一索引。这将防止数据完整性问题。

如果您遇到回滚时跳过数字的问题,则可能会出现其他问题。由于这些是占位符,它们不应该有意义,所以这可能不是问题,但我看到人们出于业务原因而不是技术原因需要此功能的情况。使用回滚测试两者以查看是否有间隙,如果您需要它们没有间隙。如果两者都有差距,那么您将需要滚动自己的系统来注意竞争条件。

既然您说您要在 DB2 中创建一个数据库以迁移到 Postgres,我将设置一个测试,其中包含几个具有 db2 中身份的表和几个具有序列的表。向其中插入大量虚假数据。然后我会测试将它们移植到 Postgres 数据库并开始添加记录的难度。这可能是关键数据,在您的特定情况下哪种方法更好。

您还可以考虑通过将大量记录插入到两个相似的测试表中来进行有关性能的测试,除了它们分配 Id 的方式之外。这可能是两种方式都可以接受的性能,也可能是一种比另一种更快。以下链接适用于 SQL Server,但测试方法可能对您做出决定很有用。http://dba-presents.com/index.php/sql-server/25-identity-vs-sequence-performance-test

如果这是一个关键问题,那么您自己确定诸如性能之类的事情是至关重要的,因为结果可能会受到您自己的特定设置的影响。

如果您想要一个基于某些文本值和递增数字(例如 CA1、CA2、CA3、TX1、TX2、TX3)的有意义的 ID,那么身份将不起作用,但我认为可以使用序列(参见这篇文章:PostgreSQL 序列基于另一列)。所以序列可以给你更多的灵活性,但如果你不需要它,那为什么还要麻烦呢?

可能我还会认为,有时使用一种有时使用另一种对维护(以及在您的情况下进行转换)来说是最令人困惑的。做事方式的一致性可能是关键。如果您有一种情况,序列为您提供了灵活性,而您必须具有身份不具备的灵活性。我会在整个过程中使用序列,以避免在进行转换时知道哪个表使用了什么不必要的复杂性。

于 2018-04-06T18:00:34.347 回答
0

Db2IDENTITY列由序列支持(支持高速缓存和无序生成以获得更高的性能)——区别纯粹是语法上的。带有标识列:

create table t1 (
  id integer not null generated always as identity cache 100,
  foobar varchar(111)
)

提供该列的值,它会自动生成并插入:

insert into blah (foobar) values ('something')

如果未定义列,则IDENTITY必须在插入行时显式创建序列并生成值

create table t2 (
  id integer not null,
  foobar varchar(111)
)

create sequence myseq cache 100

insert into t2 values (next value for myseq, 'something else')

同样,当您需要重新定义身份属性时,您可以通过ALTER TABLE语句来执行此操作;对于您使用的序列ALTER SEQUENCE

表中只能定义一列IDENTITY;如果您需要多个这样的列,则必须为它们使用序列。

LOAD当使用或实用程序将数据大量加载IMPORT到具有标识列的表中时,需要进行特殊处理——您将需要覆盖或忽略可能存在的标识值。

于 2018-04-07T18:52:25.360 回答