mysql 函数 id生成器_mysql通过 LAST_INSERT_ID 实现ID生成器

首先了解 LAST_INSERT_ID

LAST_INSERT_ID 有自己的存储空间,能存一个数字

LAST_INSERT_ID两种用法

不带参数: 不带参数时返回最近insert的那行记录的自增字段值(与AUTO_INCREMENT配合使用)

带参数: 带参数时会将自己存储的数字刷成参数给定的值

重点是这个特性: 它是多用户安全的,

mysql手册原文:

It is multi-user safe because multiple clients can issue the UPDATE statement and get their own sequence value with the SELECT statement (or mysql_insert_id()), without affecting or being affected by other clients that generate their own sequence values.

本文主要使用第二个用法

为了便于行文,使用特地业务场景进行描写.

场景描述: 按天提供趋势递增的唯一id.

建表:

create table sequence

(

day varchar(45) not null comment '日期key建'

primary key,

id bigint default 0 not null comment '序列id'

)

comment '按天自增序列表';

sql:

首先初始化指定日期的数据.例为日期为: 2020-08-01

insert into sequence values('2020-08-01',1);

select * from sequence;

day

id

2020-08-01

1

查看

select last_insert_id();

result: 0

此时来了一个申请id的请求.我们通过如下逻辑进行处理

update sequence set id=LAST_INSERT_ID(id +1) where day = '2020-08-01';

select last_insert_id();

result: 2

可以看到返回结果为2.

select * from sequence;

day

id

2020-08-01

2

此时又有新的请求过来,则继续调用该逻辑

update sequence set id=LAST_INSERT_ID(id +1) where day = '2020-08-01';

select last_insert_id();

result: 3

这样便可以生成当日的一个趋势递增的唯一 id .

上面说完了核心思想,下面说下在java环境中的具体实现

@Service

public class SequenceServiceImpl implements SequenceService {

private static final Logger log = LoggerFactory.getLogger(SequenceServiceImpl.class);

private final Map> cache = new HashMap>();

@Resource(name = "jdbcTemplate_sequence")

private JdbcTemplate jdbcTemplate;

@Override

@Transactional(value = "sequence")

public long getSequenceId(String key,int step) {

//通过synchronized保障同一时刻只能有一个线程操作cache,保证了并发安全

synchronized (cache){

List sequences = cache.get(key);

if(sequences == null){

//先判断sequence表里是否有key对应的记录,如果没有则插入一条

try {

checkKeyExist(key);

}catch (Exception e){

log.error("insert sequence key occur exception",e);

throw new RuntimeException("insert sequence key occur exception,"+e.getMessage());

}

cache.put(key,new ArrayList());

sequences = cache.get(key);

}

//如果sequence集合里没有数据,则从数据库取一个lastInsertId生成

try {

if (sequences.size() == 0) {

long maxId = getLastInsertId(key, step);

sequences = new ArrayList();

for (long i = maxId - step + 1; i <= maxId; i++) {

sequences.add(i);

}

cache.put(key, sequences);

}

}catch (Exception e){

log.error("getLastInsertId occur exception",e);

cache.put(key,null);

throw new RuntimeException("getLastInsertId occur exception",e);

}

return sequences.remove(0);;

}

}

private void checkKeyExist(String key) {

int n = jdbcTemplate.queryForInt("select count(1) from sequence where day='" +key+"'");

if (n<1){

try{

jdbcTemplate.update("insert into sequence (day,id) VALUES (?,?)",key,0);

}catch (DuplicateKeyException e){

log.error("insert sequence key DuplicateKeyException",e);

}catch (Exception e){

log.error("insert sequence key 出现异常,key="+key,e);

throw new RuntimeException("insert sequence key 出现异常,key="+key,e);

}

}

}

@Override

@Transactional(value = "sequence")

public long getLastInsertId(String key, int step) {

jdbcTemplate.update("update sequence set id=LAST_INSERT_ID(id+"+step+") where day='" +key+"'");

Long id = jdbcTemplate.queryForLong("SELECT LAST_INSERT_ID()");

return id;

}

}

上面代码通过 mysql的 LAST_INSERT_ID (value + step)获取一个全局唯一并且自增的sequnce , 一次获取一个号段,然后通过Map本地cahce 一下,这样操作的目的就是为了避免多次操作数据库,提升了分配id的性能.

结语: 上面主要是针对特定业务场景中LAST_INSERT_ID的使用,重要的是要理解AST_INSERT_ID的特性,这样就可触类旁通.在遇到相关场景时不妨考虑使用AST_INSERT_ID.在笔者所在的部门,有一个全局id生成器,是一个单独的服务,其存在的意义就是为其它应用提供全局唯一的id.而其核心逻辑便是通过LAST_INSERT_ID实现的.

笔者能力有限,文中如有不严谨之处,还望各位及时指正.共勉之.

文章构思及写作用时: 4h

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值