主鍵生成策略直接影響數據庫性能、分佈式擴展性、數據遷移兼容性。Hibernate 提供 10 種內置生成器,本文逐一剖析原理、優缺點、適用場景,給出選型決策矩陣與生產環境建議。
| 策略名 | 實現原理 | 依賴 | 優點 | 缺點/限制 |
|---|---|---|---|---|
assigned | 應用程序手動分配 | 無 | 完全可控,支持業務主鍵 | 需保證唯一性,分佈式難協調 |
hilo | 高低位算法:需額外表保存 hi 值 | 額外表 | 減少數據庫往返,批量生成 | 需額外表,跨系統 hi 衝突風險 |
seqhilo | hilo 變體:hi 值存 Sequence | Sequence (Oracle/PostgreSQL) | 利用 Sequence 高性能 | 僅支持有 Sequence 的數據庫 |
increment | 內存計數器單實例遞增 | 無 | 簡單、無數據庫交互 | 單實例唯一,集群/多實例必發重複 |
identity | 數據庫自增列 | MySQL/SQL Server/DB2/PostgreSQL(SERIAL) | 零配置、數據庫保證唯一 | 批量插入需逐條獲取生成值,性能差;不支持 Oracle |
sequence | 數據庫 Sequence | Oracle/PostgreSQL/DB2/H2 | 高性能、支持批量預分配 | 需數據庫支持,Oracle 需創建 Sequence |
native | 根據底層數據庫自動選擇 identity/sequence/hilo | 數據庫原生機制 | 跨數據庫可移植 | 行為不確定,不同庫性能差異大 |
uuid.hex | 128位 UUID → 32位十六進制字符串 | 無 | 全局唯一、無中心協調、跨庫遷移無痛 | 字符串主鍵佔用空間大,索引性能略低於數值 |
uuid.string | 16位原始 UUID 字節數組 | 無 | 空間較小 | 某些數據庫(PostgreSQL)存儲二進制有問題 |
foreign | 共享關聯表主鍵(一對一主鍵關聯) | 關聯實體 | 主鍵即外鍵,天然一致 | 僅適用一對一主鍵關聯場景 |
identity/sequence 看似零成本,實則:
identity 更糟:無法批量預分配,每條 Insert 必須同步獲取生成值,JDBC 批處理失效。| 主鍵類型 | 存儲大小 | 索引深度 | 插入性能(萬/秒) | 範圍查詢 |
|---|---|---|---|---|
| BIGINT (identity/sequence) | 8 字節 | 淺 | 3-5 (受限於計數器) | 極快 |
| CHAR(32) (uuid.hex) | 32 字節 | 深 | 8-12 (純內存) | 慢(無序) |
| BINARY(16) (uuid优化) | 16 字節 | 中 | 8-12 | 慢 |
需要業務含義主鍵(如訂單號)? → assigned (配合雪花算法/分段序列)
否:
單體應用、單數據庫、MySQL/SQL Server? → identity (簡單優先)
單體應用、Oracle/PostgreSQL? → sequence (性能優於 identity)
微服務/分佈式/多寫入節點? → uuid.hex (首選) 或 雪花算法 (有序、可追溯)
數據遷移/異構同步/離線數據合併? → uuid.hex (全局唯一無衝突)
高併發寫入、追求極致插入性能? → uuid.hex + 批量插入
表關聯查詢極多、範圍查詢頻繁? → BIGINT (identity/sequence)
一對一主鍵關聯? → foreign
<id name="id" column="id" type="string" length="32">
<generator class="uuid.hex"/>
</id>
id CHAR(32) PRIMARY KEY 或 VARCHAR(32)。String id = UUID.randomUUID().toString().replace("-", "");(等同 uuid.hex)。com.github.f4b6a3:tsid-java (TSID)、com.baomidou:mybatis-plus-generator (雪花)。increment 都是定時炸彈。// uuid.hex
@Id @GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid.hex")
@Column(length = 32)
private String id;
// identity (MySQL)
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// sequence (Oracle/PostgreSQL)
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_gen")
@SequenceGenerator(name = "seq_gen", sequenceName = "seq_table", allocationSize = 50)
private Long id;
// assigned (手動設置)
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) // JPA 無 assigned,用 IDENTITY 模擬或不加 GeneratedValue
private String id;
主鍵策略選擇沒有標準答案,只有架構約束下的最優解。現代雲原生、微服務、多活架構下,uuid.hex 或有序 UUID 是默認首選,以換取無協調擴展能力與遷移靈活性;單體傳統系統繼續用數據庫原生機制無可厚非。關鍵是:在架構設計階段確定,上線後嚴禁變更。