sequence 主键获取工具类,先打算如下实现:(基础要求:支持分布式请求下的序列唯一性)
最终表现形式,提供如下两个公共接口,
1、入参是序列名称,返回是序列值,内部逻辑是向数据库查找下一个序列的值然后返回查询结果
SELECT ***.NEXTVAL SEQ FROM DUAL;
这个方法取到的序列严格遵循步长,每次请求都会建一次数据库连接走一次查询语句,相信Oracle的取序列api对并发请求的安全性支持,所以不需要做并发下的安全性处理(序列的严格连续性要看序列定义的时候是否启动缓冲区)
2、入参是序列名称,返回是序列值,内部逻辑是先查询此序列对应的序列信息
SELECT INCREMENT_BY, MAX_VALUE FROM USER_SEQUENCES WHERE SEQUENCE_NAME = '***';
将序列相关信息放入内存缓存,然后查询下一个序列值:
SELECT ***.NEXTVAL SEQ FROM DUAL;
将查到的序列值作为第一次查询的返回,并将查到的序列值放进内存缓存,在后续线程到来的时候,如果步长大于1,比如说是5,那么下4次请求会直接从内存计算取值并返回(这里要做并发安全处理)当第五次时,因为到了下一个步长,所以向数据库请求下一个值返回,并更新内存(这里要做并发安全处理)
这个方法取到的序列不遵循步长,而是每次+1,只有在步长规定的取值出才是向数据库查询拿到的值,其他的值是内存中计算出来。 这样的做法保证了数据库的异常断电仍然不会有重复序列的情况发生,但是服务器本身的进程重启有可能导致部分序列的不连续性
写了个测试类,连续起20个线程去请求序列,先看看表现 单服务请求,表现完美: 2018-10-16 20:11:47.726 INFO 14372 --- [ main] com.tang.billing.demo.SequenceUtilTest : ************===============************ 2018-10-16 20:11:47.841 INFO 14372 --- [ myThread8] com.tang.billing.demo.SequenceUtilTest : seq ret is 1thread number:8 2018-10-16 20:11:47.841 INFO 14372 --- [ myThread5] com.tang.billing.demo.SequenceUtilTest : seq ret is 3thread number:5 2018-10-16 20:11:47.841 INFO 14372 --- [ myThread9] com.tang.billing.demo.SequenceUtilTest : seq ret is 2thread number:9 2018-10-16 20:11:47.841 INFO 14372 --- [ myThread11] com.tang.billing.demo.SequenceUtilTest : seq ret is 4thread number:11 2018-10-16 20:11:47.841 INFO 14372 --- [ myThread7] com.tang.billing.demo.SequenceUtilTest : seq ret is 5thread number:7 2018-10-16 20:11:47.843 INFO 14372 --- [ myThread2] com.tang.billing.demo.SequenceUtilTest : seq ret is 6thread number:2 2018-10-16 2