簡體   English   中英

我們應該使用序列或身份作為主鍵嗎?

[英]Should we use sequences or identities for our primary keys?

我們正在創建一個包含20多個表的新數據庫,我們的數據庫支持:

  • 序列。
  • 標識列(始終作為標識/序列生成)。

所以,問題是:我們應該使用序列還是身份? 哪一個更好? 球隊似乎在這一個進行划分的,所以我想聽到的利弊 ,幫助決定。

添加數據庫細節:

  • 我們正在IBM DB2上創建新數據庫,但我們需要確保它與將來遷移到PostgreSQL的計划兼容。

您的問題是關於使用序列與身份(“大概始終生成身份”列,大概是)。 在Postgres中,這些將被聲明為serial 這些將始終是單列中的某種數字。

從數據庫性能的角度來看,兩者之間沒有太大區別。 一個重要的區別是一些數據庫將緩存標識列,這可以加快插入速度但會造成間隙。 緩存序列的規則可能不同。 在高事務環境中,緩存不足可能是性能瓶頸。 在多個表之間共享單個序列會使這個問題變得更糟。

從數據管理的角度來看,存在更大的差異。 序列需要管理兩個對象(表和序列)。 表中內置了identityserial列。

對於單個表,我只考慮在數據庫中使用不支持內置自動增量/串行/標識列的序列(ahem,“Oracle”)。 否則,我會使用設計用於表的機制。

我想指出使用自動遞增的代理鍵還有其他好處。 如果數據庫中存在這樣的概念,這也應該是用於聚類數據的密鑰。 然后,新插入始終位於“結束”(盡管如果要刪除數據,則可能僅部分使用頁面)。 主鍵也應該是用於外鍵引用的唯一鍵,即使其他列 - 隔離或一起 - 是唯一的和候選主鍵。

最好的答案是指出你的情況。

首先,許多人更喜歡序列,因為它們易於生成並提供單一數據類型來導航連接。 此外,許多商店需要單列主鍵來進一步協助代碼復雜性。

我們來談談缺點:

序列:當使用b樹索引時,序列通常以升序插入,這可能導致“不平衡樹”並導致不完美的性能(在b樹索引上)隨着時間的推移。 有時,人們會生成哈希或GUID,以生成更平衡的樹。

使用“查找表”時,序列可能導致“難以閱讀”的代碼,尤其是在數據庫中對值進行硬編碼時。 示例:“where status_seq = 1”比“where status_id ='ACTIVE'”更難閱讀。

使用ID的缺點:混合數據類型可能會導致混淆。 有時它們是數字的,有時它們是varchar或char。 許多ORM可能會混淆這些並導致前導零,從而導致結果出錯。 IE 01234!= 1234,但您的ORM可能會返回1234而不是01234。

許多人以人類可讀的形式存儲ID,例如“有效”或州縮寫。 從長遠來看,這可能會導致令人頭疼的問題,因此,即使您確實在桌面上使用ID,您也可能希望避免直接向您的客戶顯示這些ID。

與序列相比,ID字段將來更有可能“需要更改”。 示例:假設您有一個國家/地區代碼表,並且發生了一次革命並且國家/地區代碼發生​​了變化。 你真的想通過主表和所有引用它的外鍵,輸入新的國家代碼 - 或者使用舊的國家代碼,這是你的選擇。 如果在這種情況下使用序列,只需更新基表中的其他非鍵列,就可以了。

優點:

序列的好處:序列本質上是自動生成的。 ID並非總是如此。 添加記錄時,您真的希望程序員或用戶命名一個無法輕易更改的ID嗎? 當您使用序列時,很少需要重新編號,如果出現錯誤,可以輕松更改基礎的人類可讀數據。

如上所述,它們總是一個數值數據類型,如果使用得當可以幫助“導航”你的應用程序(IE,通常只需要“傳遞”一個數字來導航你的表格結構)

使用DB和編程語言之間的通信時,您可以指望能夠將整數轉換為整數而不會出現任何奇怪的數據轉換問題。

ID:主要好處是我們已經在上面解釋過的更容易閱讀的代碼。

總而言之,我認為這取決於表和列的使用情況。 如果您要使用ID,請避免向用戶顯示值的誘惑。 如果表不會改變並只是持有標志或“枚舉”類型數據,那么ID肯定有助於代碼可讀性。 否則,序列通常是數據可維護性的最佳選擇。

有些人選擇GUID或ID來幫助提高索引性能,但就個人而言,如果代碼可讀性有任何損失或代碼變得更復雜,我會在編寫更復雜的代碼之前在更好的硬件上花一些錢 - 作為好處微乎其微。

資料來源:Oracle認證的DBA(關於這一主題的培訓),以及20多年與開發人員和企業數據庫合作的經驗。

我是序列的粉絲。 如果所有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中構建一系列相關數據,然后將它們全部移動到持久存儲中。 這通常更有效率。

我沒有使用序列,但我可以討論身份字段。

首先,他們在使用SQL Server過去18年中使用它們的每種情況下都能很好地工作。 這很可能在其他數據庫上也是如此,這對於使用它們的數據庫來說是一個關鍵特性。 我們從來沒有遇到過使用身份的任何問題。 如果您希望擁有一個非常大的數據庫,則可能需要在設置時將標識定義為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

如果這是一個關鍵問題,那么對性能等事情做出自己的決定至關重要,因為結果會受到您自己特定設置的影響。

如果你想要一個基於某些文本值和遞增數字的有意義的ID(例如CA1,CA2,CA3,TX1,TX2,TX3),那么身份將不起作用,但我認為是一個序列(參見本文: PostgreSQL序列基於另一欄 )。 所以序列可以給你更多的靈活性,但如果你不需要它,那么為什么呢?

也許我會認為維護(以及在你的情況下轉換)有時使用一個,有時使用另一個是最困惑的。 一致如何做事可能是關鍵。 如果你有一個案例,其中序列給你一個靈活性,你必須擁有,但你的身份卻沒有。 我會在整個過程中使用序列,以避免在執行轉換時知道哪個表使用了哪些表的不必要的復雜性。

Db2 IDENTITY列由序列支持(支持緩存和亂序生成以獲得更高性能) - 差異純粹是語法。 使用標識列:

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 ; 如果您需要多個此類列,則必須使用序列。

使用LOADIMPORT實用程序將數據批量加載到具有標識列的表中時,需要特殊處理 - 您需要覆蓋或忽略可能存在的標識值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM