業務數據的存儲,少不了數據記錄的id序列,id序列(或稱序列)的生成方式有很多種,比如當前時間戳、資料庫的序列值(Oracle的序列,MySQL的自增ID等)、UUID等方式,但這些生成方式均存在一定的局限性,本文介紹一種通用高性能的分散式id序列的設計思路…… ...
原文地址:https://ntopic.cn/p/2023062101/
- Gitee源代碼倉庫:https://gitee.com/obullxl/sequence-jdbc
- GitHub源代碼倉庫:https://github.com/obullxl/sequence-jdbc
分散式id序列說明
業務數據的存儲,少不了數據記錄的id序列。
id序列(或稱序列)的生成方式有很多種,比如當前時間戳、資料庫的序列值(Oracle的序列,MySQL的自增ID等)、UUID等方式。
這些生成方式都有一定的局限性,如時間戳在業務量較大時容易重覆、Oracle序列和MySQL的自增ID限定了資料庫類型(且MySQL的自增ID只能保證單庫唯一,在分庫分表的場景下也不適用)、UUID容易重覆且無法保證遞增等。
同時,一般業務數據的id序列通常會帶上一些業務信息,比如增加業務標識首碼、增加年月日等信息。業務id序列的處理變得多樣,則進一步要求業務id序列的生成通用且高效。
通用分散式id序列組件
為了屏蔽業務獲取id序列因資料庫類型、分庫分表等帶來的研發和維護成本,我們把分散式id序列的獲取抽取為一個通用組件,對業務統一介面和規範。
通用分散式id序列實現方式有很多,本文主要介紹一種基於數據表的實現方式,通過一張表記錄所有的業務序列名和值,業務根據序列名獲取下一個序列值(和Oracle序列類型,但是無需為每個序列創建序列,因此更簡單):
本方案的設計主要考量點:
通用性:
僅依賴一張序列數據表,JDBC支持的資料庫均可使用,包括SQLite、MySQL、Oracle、OceanBase等。高性能:
本地緩存一個序列區間,緩存使用完之前無DB交互;緩存的區間可設置,區間越大,DB訪問越少,性能越高。分散式:
受益於集中式的序列數據表,保證了序列全局唯一。
分散式id序列組件設計
組件介面設計
組件介面只有1個,就是獲取序列:
/**
* Author: [email protected]
* Copyright (c) 2020-2023 All Rights Reserved.
*/
package cn.ntopic.sequence;
/**
* 分散式序列服務
*
* @author obullxl 2023年06月21日: 新增
*/
public interface NTSequence {
/**
* 預設序列名稱
*/
String DEFAULT_SEQUENCE_NAME = "DEFAULT";
/**
* 序列名稱最大長度
*/
int MAX_SEQUENCE_NAME_LENGTH = 64;
/**
* 獲取下一個序列值
*
* @return 獲取預設序列的新的唯一的序列值 {@link #DEFAULT_SEQUENCE_NAME}
* @throws IllegalArgumentException 參數非法
*/
default long next() {
return this.next(DEFAULT_SEQUENCE_NAME);
}
/**
* 獲取下一個序列值
*
* @param sequenceName 序列名稱,非空,1~64字元,業務可隨意指定(如:用戶模塊為`USER`,訂單模塊為`ORDER`等)
* @return 新的唯一的序列值
* @throws IllegalArgumentException 參數非法
*/
long next(String sequenceName);
}
組件可控參數
在追求通用性和性能的同時,以參數的方式供個性化調控:
id序列數據表名:
預設值為nt_sequence;但對於不同的業務,對數據表名有要求規範(如:表名前置等),因此序列數據表名可設置id序列起始值:
預設值為1,即序列值從1開始遞增;但對於存量業務,id值起始值需要比存量最大值要大,否則容易重覆id序列最大值:
預設值為99999999,序列值遞增到最大值,則迴圈從起始值開始序列更新重試次數:
預設值為10,當緩存序列用盡,需要查詢和更新序列數據表,比較存在網路通訊和DB操作,不可避免存在異常失敗,失敗後會進行重試序列緩存大小:
預設值為1000,值越大,訪問DB越少,性能越高,序列的連續性越差(如:緩存大小為1000,當序列用到400時,伺服器重啟了,那麼401~1000直接的序列就丟失了);相反,值越小,訪問DB越多,性能越低,序列的連續性越好。
/**
* 屬性-數據源
*/
private final DataSource ntDataSource;
/**
* 屬性-重試次數
*/
private int retryTimes = 10;
/**
* 屬性-數據表名
*/
private String tableName = "nt_sequence";
/**
* 屬性-序列獲取步長(即序列緩存大小)
*/
private long step = 1000L;
/**
* 屬性-序列最小值
*/
private long minValue = 1L;
/**
* 屬性-序列最大值
*/
private long maxValue = 99999999L;
序列組件使用
- Gitee源代碼倉庫:https://gitee.com/obullxl/sequence-jdbc
- GitHub源代碼倉庫:https://github.com/obullxl/sequence-jdbc
目前JAR包已經發佈,通過Gitee進行倉庫托管,也可直接使用,2步即可:
- 第一步:在項目源代碼的根
pom.xml
中,設置倉庫地址:
<repositories>
<repository>
<id>Gitee-obullxl</id>
<url>https://gitee.com/obullxl/maven-repository/master/repository</url>
</repository>
</repositories>
- 第二步:引用JAR包,僅需要依賴本JAR包,無其他JAR包依賴:
<dependency>
<groupId>cn.ntopic</groupId>
<artifactId>sequence-jdbc</artifactId>
<version>1.0.1</version>
</dependency>
在業務代碼中調用序列組件樣例:
// 1. 構建數據源
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:sqlite:/Users/obullxl/CodeSpace/sequence-jdbc/SequenceJDBC.sqlite");
dataSource.setDriverClassName("org.sqlite.JDBC");
dataSource.setPoolPreparedStatements(false);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(-1);
dataSource.setTestOnBorrow(true);
dataSource.setTestOnReturn(false);
dataSource.setTestWhileIdle(true);
dataSource.setValidationQuery("SELECT '1' FROM sqlite_master LIMIT 1");
// 2. 初始化序列組件
NTSequenceImpl ntSequence = new NTSequenceImpl(dataSource);
ntSequence.setTableName(tableName);
ntSequence.createTable();
ntSequence.init();
// 3. 獲取序列值
@Autowire
@Qualifier("ntSequence")
private NTSequence ntSequence;
// 獲取`DEFAULT`預設序列ID
long newId1 = ntSequence.next();
long newId2 = ntSequence.next();
long newId3 = ntSequence.next();
// 獲取`USER`用戶ID:
long newUserId1 = ntSequence.next("USER");
long newUserId2 = ntSequence.next("USER");
long newUserId3 = ntSequence.next("USER");
// 獲取`ORDER`訂單ID:
long newOrderId1 = ntSequence.next("ORDER");
long newOrderId2 = ntSequence.next("ORDER");
long newOrderId3 = ntSequence.next("ORDER");
完整的使用方法,可參數源代碼倉庫說明文檔(README.md
):
- Gitee源代碼倉庫:https://gitee.com/obullxl/sequence-jdbc
- GitHub源代碼倉庫:https://github.com/obullxl/sequence-jdbc
分散式id序列測試用例
因為代碼較大,請直接查看源代碼:
- Gitee測試源代碼:https://gitee.com/obullxl/sequence-jdbc/blob/master/src/test/java/cn/ntopic/sequence/NTSequenceTest.java
- GitHub測試源代碼:https://github.com/obullxl/sequence-jdbc/blob/master/src/test/java/cn/ntopic/sequence/NTSequenceTest.java