Skip to content

A mysql-based transaction-free id generator using last_insert_id() function.

License

Notifications You must be signed in to change notification settings

yiding-he/mysql-sequence-generator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mysql-sequence-generator

基于 MySQL 的 last_insert_id() 函数而写的序列生成工具。

效率很高,没有数据库事务。

整个项目只有一个类,且无任何依赖关系,可以直接拷贝到任何项目中使用。

源码 MysqlSequenceGenerator.java

标准序列表

下面是标准的序列表,但也可兼容字段稍微不同的表,具体见后面说明。

create table t_sequence(
  name  varchar(100) primary key             comment '序列名称',
  code  varchar(5) not null default '00000'  comment '序列编号(用于区分不同序列种类)',
  value bigint     not null default 0        comment '当前值',
  min   bigint     not null default 0        comment '最小值',
  max   bigint     not null default 99999999 comment '最大值,当 value 达到最大值时重新从 min 开始',
  step  int        not null default 1000     comment '步长,每次取序列时缓存多少,步长越大,数据库访问频率越低'
);

INSERT into t_sequence (name, code, max) values ('seq1', '886', 99999999);
INSERT into t_sequence (name, code, max) values ('seq2', '887', 99999999);
INSERT into t_sequence (name, code, max) values ('seq3', '888', 99999999);

一、构造方法(含兼容性配置)

MysqlSequenceGenerator 不要求表一定要是标准的样子,但要求表中至少要有下面五个字段:

  • 序列名称
  • 当前值
  • 最小值
  • 最大值
  • 步长

每个字段可以自定义名字,而且标准表中的 code 字段是可选的。下面是一个创建 MysqlSequenceGenerator 对象的例子:

// 1. 使用标准的序列表来创建 MysqlSequenceGenerator
MysqlSequenceGenerator idGenerator = new MysqlSequenceGenerator(dataSource);

// 2. 使用非标准的序列表来创建 MysqlSequenceGenerator
MysqlSequenceGenerator idGenerator = new MysqlSequenceGenerator(
    dataSource::getConnection, // 获得 Connection 的方式
    Connection::close,         // 关闭 Connection 的方式
    "t_sequence",              // 实际的序列表名称
    false,                     // 是否异步获取新的序列号。提前异步获取新的序列号可提升稍许性能
    Arrays.asList(             // 自定义字段名
        ColumnInfo.customName(Column.Min, "min_value"),  // 最小值字段的实际字段名为 min_value
        ColumnInfo.customName(Column.Max, "max_value"),  // 最大值字段的实际字段名为 max_value
        ColumnInfo.undefined(Column.Code)                // 表中没有 code 字段
    )
);

二、使用方法

  1. 获得一个 long 类型的序列:long id = idGenerator.nextLong("seq1")
  2. 获得一个带年月日和编号的字符串序列:String id = idGenerator.nextSequence("seq1")
  3. 获得一个带年月日和自定义编号的字符串序列:String id = idGenerator.nextSequence("seq1", "001")

使用的示例详见单元测试。

字符串序列的格式

字符串序列的格式为 yyyyMMdd[code][sequence],其中:

  • [code] 为 code 字段的值(如果有的话);
  • [sequence] 的长度与 max 字段的值长度相同。

例如某个序列,code 字段值为 888,max 字段值为 999999,那么生成的字符串序列可能为 "20191231888000001"

三、兼容 Spring 数据库事务

如果要在 Spring Boot 项目中使用并兼容 Spring 事务,请参考 com.hyd.mysqlsequencegenerator.MysqlSequenceGeneratorApplication 源码示例

四、原理简介

  1. MySQL 支持在 update 语句中使用 last_insert_id(xxx) 来临时保存最近生成的 ID 到会话中,接下来可以在同一会话中用 select last_insert_id() 来获取刚生成的 ID。
  2. MysqlSequenceGenerator 利用 AtomicLongupdateAndGet() 方法来完成计数器更新和数据库操作。
  3. Counter 是实现并发序列的核心类,它包含一个用于保存自增值的 AtomicLong 对象和一个 Threshold 阈值对象,后者表示前者自增到什么值时需要从数据库重新取一个段。取到后,根据取到的最大值将 Threshold 的阈值推高。
  4. 当多个线程同时从数据库重新取段时,会分别将 Threshold 的阈值推高数次。

五、性能

因为相关的 SQL 都非常简单,性能瓶颈其实都在数据库上。步长越小,数据库操作越频繁。

About

A mysql-based transaction-free id generator using last_insert_id() function.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages