首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

Hibernate 整合 MySQL UUID_SHORT:自定義 ID 生成器實現 64 位無符號長整數主鍵

     編輯:MySQL 綜合教學

MySQL UUID_SHORT() 返回 64 位無符號長整數(基於 server_id、啟動時間、計數器),**全局唯一、單調遞增、無破折號、比標準 UUID 短 50%、極適合 InnoDB 主鍵**(避免頁分裂、索引緊湊)。本文實現 Hibernate 自定義 IdentifierGenerator 調用 UUID_SHORT(),含映射配置、實體定義、事務邊界處理與效能對比。

UUID_SHORT() 特性

  • 格式:BIGINT UNSIGNED (0 ~ 18446744073709551615)
  • 組成:(server_id & 255) << 56 + (startup_time_seconds << 24) + counter
  • 單調遞增:同一實例內嚴格遞增,**利於 InnoDB 聚簇索引順序寫入**
  • 全局唯一:需保證 server_id (my.cnf) 全局唯一
  • 無需網絡協調,比 Snowflake 簡單

Hibernate 自定義生成器實現

1. 生成器類

package com.example.util;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class ShortUUIDIncrementGenerator implements IdentifierGenerator {
    private static final Logger log = LoggerFactory.getLogger(ShortUUIDIncrementGenerator.class);
    private static final String SQL = "SELECT UUID_SHORT()";

    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) {
        try (PreparedStatement ps = session.connection().prepareStatement(SQL);
             ResultSet rs = ps.executeQuery()) {
            if (rs.next()) {
                long id = rs.getLong(1);
                log.debug("Generated UUID_SHORT ID: {}", id);
                return id;
            }
            throw new HibernateException("UUID_SHORT() returned no result");
        } catch (SQLException e) {
            log.error("Could not retrieve UUID_SHORT", e);
            throw new HibernateException("Could not retrieve UUID_SHORT", e);
        }
    }
}

2. 實體映射配置

@Entity
@Table(name = "t_order")
public class Order {
    @Id
    @GeneratedValue(generator = "shortUid")
    @GenericGenerator(name = "shortUid", strategy = "com.example.util.ShortUUIDIncrementGenerator")
    @Column(name = "id", unique = true, nullable = false, insertable = false, updatable = false, columnDefinition = "BIGINT UNSIGNED")
    private Long id;

    // 其他字段...
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
}

3. XML 映射替代 (hbm.xml)


    

關鍵細節與避坑

問題原因解決
ID 為負數 / 溢出Java Long 有符號,UUID_SHORT() 最大值 > Long.MAX_VALUE (9.22E18)實際 UUID_SHORT() 當前值 ~ 1.5E18,遠小於 Long.MAX_VALUE,**暫安全**;長期建議用 BigInteger 或字符串存儲
事務邊界:生成器在事務外執行Hibernate 默認在 INSERT 前調用 generate(),若事務回滾 ID 已消耗接受間隙(單調遞增不要求連續),或用增強版:事務提交前生成
並發性能每次 INSERT 需額外 SELECT UUID_SHORT()可接受(單次 RTT),極高併發可改用 HiLo/Sequence/預分配池
server_id 衝突多實例 server_id 相同 → ID 衝突強制配置唯一 server_id (1-255),監控 SHOW VARIABLES LIKE 'server_id'
備庫只讀報錯備庫 read_only=ON,UUID_SHORT() 需寫入計數器生成器僅在主庫調用,備庫不持久化實體或用只讀事務

效能對比:UUID_SHORT vs UUID vs Snowflake vs 自增

方案長度有序性索引友好生成開銷全局唯一適用場景
自增 BIGINT8B嚴格遞增✅ 最佳極低 (內部)單實例單庫、低併發
UUID_SHORT8B單調遞增✅ 極佳低 (1 RTT)全局 (需 server_id)**主從/集群主鍵首選**
UUID (RFC4122)16B/36字符無序❌ 頁分裂嚴重極低 (本地)全局分散式無協調、非主鍵
Snowflake (64bit)8B趨勢遞增✅ 良好極低 (本地)全局 (需 workerId)高併發微服務、客戶端生成

生產環境增強版:預分配池減少 RTT

@ApplicationScoped
public class UuidShortPool {
    private final BlockingQueue pool = new LinkedBlockingQueue<>(10000);
    private final AtomicBoolean loading = new AtomicBoolean(false);
    
    @PostConstruct
    void start() { refillAsync(); }
    
    public synchronized long nextId() {
        Long id = pool.poll();
        if (id == null) {  // 池空,同步補充 (罕見)
            refillSync();
            id = pool.poll();
        }
        if (pool.size() < 1000 && loading.compareAndSet(false, true)) {
            refillAsync();
        }
        return id;
    }
    
    private void refillAsync() {
        CompletableFuture.runAsync(() -> {
            try { refillSync(); } finally { loading.set(false); }
        });
    }
    
    private void refillSync() {
        List batch = jdbcTemplate.queryForList("SELECT UUID_SHORT() FROM DUAL", Long.class, 1000);
        pool.addAll(batch);
    }
}

// 生成器委託給池
public class PooledShortUUIDGenerator implements IdentifierGenerator {
    @Autowired UuidShortPool pool;
    public Serializable generate(...) { return pool.nextId(); }
}

遷移檢查表

  1. ✅ 所有實例 server_id 唯一、非 0、my.cnf 持久化
  2. ✅ 表主鍵定義 BIGINT UNSIGNED,JPA @Column(columnDefinition = "BIGINT UNSIGNED")
  3. ✅ 測試主從切換:備庫提升為主後 UUID_SHORT() 正常生成
  4. ✅ 壓測驗證:QPS、主鍵衝突率、索引頁分裂率
  5. ✅ 監控 SHOW VARIABLES LIKE 'server_id'、SELECT UUID_SHORT() 連續性
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved