数据库唯一序列号生成方案

  首先,我们得知道为什么需要制定数据库唯一序列号生成方案,难道MySQL的主键自增长不好用吗?当然不是。由于现在的业务数据量越来越大,有时候将数据放在一张表里,压力非常大,所以要进行分库分表。一旦进行了分库分表,用MySQL自带的自增长主键就会有问题了。如何保证生成一个唯一的不重复的主键,这是一个严肃的问题。
  我今天介绍一种方案,也是我公司的实现方案之一。几个模块如下:
1. 先定义一张表,结构如下:
序列信息表
biz_code:表示业务表的编码
max_id:当下可以达到的最大的id
offset:偏移量,即每次库里的id达到最大值后的偏移量
2. 再定义一个执行过程,代码如下:

CREATE DEFINER=`sequ`@`%` PROCEDURE `PROC_SEQUENCE_ID`(IN bizCode varchar(50))
BEGIN
  DECLARE t_error INTEGER DEFAULT 0;  
  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error=1;  

   START TRANSACTION;  
   UPDATE sequence SET max_id=max_id+offset WHERE biz_code= bizCode;
   SELECT max_id, offset,biz_code FROM sequence WHERE biz_code=bizCode;

   IF t_error = 1 THEN  
    ROLLBACK;  
   ELSE  
    COMMIT;  
   END IF;  
     END

执行过程逻辑很简单,就是将max_id加上(偏移)offset大小。
3. 最后定义一个双缓冲队列,以生产者-消费者模型分发id。

也许单纯看模块,大家一头雾水,也不知道这讲的是啥。我再说明一下过程:
1. 项目启动前,先将表的序列信息入库。如:
biz_code = ‘USER’
max_id = 10
offset = 100
2. 初始化双缓冲队列,用一个ConcurrentHashMap来维护。
3. 第一次获取id时,由于读队列为空,则读写队列交换身份,运行数据库执行过程(max_id变成110),往写队列里放入值(如在USER表中,就是11-100这个区间的值)。由于是第一次,则之前的写队列也是空的,则重复这个过程,然后获取到id。
4. 在以后获取id的过程中,每当读队列为空时,则读写队列交换身份,运行数据库执行过程,往写队列里放入值。

  这个方案简单但管用,其中用到了一种数据结构,叫双缓冲队列,这里简单介绍一下。双缓冲队列的核心思想就是读写分离,传统队列是生产者线程和消费者线程从同一个队列中存取数据,必然需要互斥访问,在互相同步等待中浪费了宝贵的时间,使队列吞吐量受影响。双缓冲队使用两个队列,将读写分离,一个队列专门用来读,另一个专门用来写,当读队列空或写 队列满时将两个队列互换。这里为了保证队列的读写顺序,当读队列为空且写队列不为空时候才允许两个队列互换。

  当然,很多时候会再生成一个字符串作为code。这个时候只要定好位数,然后给每一段标注意义,如:8位年月日 | 2位系统编号 | 2位业务单号 | 序列号10位不足前面补0 | 2位分库号。这个过程依旧可以用到上面生成序列号的方式。

你可能感兴趣的:(数据库,Java)