前言
如何解决高并发环境下系统流水号保证唯一性问题?时间戳+订单类型+随机数?加锁?集群环境又如何解决?
如常见系统中的业务号、订单系统中的订单号,物流系统的运单号等都要确保全局唯一性。除此之外,一个设计良好的流水号生成规则还应该包含如下特性:
-
全局唯一性:在整个系统中唯一,可以通过单号直接定位到具体数据
-
可读性:能够直接从单号上获取一些基本信息
-
可扩展性:支持海量id,当应用扩展时可以做到平滑升级
-
递增趋势:需要根据业务或时间呈递增趋势
-
不可推测性:使用户无法猜测下一个或上一个订单号是什么
-
高性能:流水号生成速度要快,不能成为业务瓶颈
-
高可用:流水号生成必须要稳定,不能影响业务发展
背景
项目需求指出工单管理模块工单编号要求由6位县级行政区划代码+4位年份代码+6位顺序号组成,后十位数递增,并保证系统编号唯一。
编号规则:由6位县级行政区划代码+4位年份代码+6位顺序号组成。
例如:3305242022000001
假设表结构如下
CREATE TABLE TEST_TABLE (
YWH NVARCHAR2(16) NOT NULL,
XZQHDM NVARCHAR2(14),
XZQHMC NVARCHAR2(100),
BLZT CHAR(1 BYTE)
);
COMMENT ON COLUMN TEST_TABLE.YWH IS '业务号';
COMMENT ON COLUMN TEST_TABLE.XZQHDM IS '行政区划代码';
COMMENT ON COLUMN TEST_TABLE.XZQHMC IS '行政区划名称';
COMMENT ON COLUMN TEST_TABLE.BLZTIS '办理状态';
关注点:保证编号唯一,那么得解决并发情况。
方案
触发器:
触发器是指被隐含执行的存储过程(存储过程是单线程的),它可以使用PL/SQL进行开发,当发生特定事件(如修改表、创建对象、登录到数据库)时,是由一个事件来触发运行,当事件触发会自动地隐式运行。它的组成可以分为三个部分:
1、触发器执行的条件,即触发器被触发的事件
2、执行触发器的时间,发生事件之前(before)或发生事件之后(after)
3、触发器要做的事情,就是触发器被触发以后具体想执行的任务(PL/SQL语句块)。
下面我们根据需求,当新增一个工单,在执行insert语句之前,我们先设计好触发器TRIG_TEST_TABLE ,并在表TEST_TABLE关联触发器(配置:行级触发、执行插入之前),触发器设计如下所示:
CREATE OR REPLACE TRIGGER TRIG_TEST_TABLE
BEFORE INSERT ON TEST_TABLE FOR EACH ROW WHEN (new.ywh IS NULL) /*触发条件:当表执行insert操作并且ywh为null时触发*/
DECLARE /*定义变量*/
lwxzqh NVARCHAR2(6); /*6位县级行政区划代码*/
swnf NVARCHAR2(4); /*4位年份代码*/
lwxxh NVARCHAR2(6); /*6位顺序号*/
BEGIN
/*判断并行政区划代码是否为空、长度是否大于等于6位,是给默认值,否截取前6位*/
IF :new.xzqhdm IS NOT NULL AND LENGTH(:new.xzqhdm)>=6 THEN
lwxzqh:=SUBSTR(:new.xzqhdm,0,6);
ELSE
lwxzqh:='330524';
END IF;
/*按数据库日期生成4位年份代码*/
swnf:=TO_CHAR(SYSDATE,'yyyy');
/*默认6位顺序号为000001*/
lwxxh:='000001';
/*根据6位县级行政区划代码+4位年份代码,查询该表最大ywh加1并赋值给新的ywh*/
SELECT MAX(ywh)+1 INTO:new.ywh FROM TEST_TABLE WHERE INSTR(ywh,lwxzqh||swnf)>0;
/*判断新的ywh是否为空,为空说明此6位县级行政区划代码+4位年份代码当前并未生成ywh,这里给新的ywh赋值未默认的即可*/
IF :new.ywh IS NULL THEN
:new.ywh:=lwxzqh||swnf||lwxxh;
END IF;
END;
测试:
INSERT INTO TEST_TABLE(XZQHDM, XZQHMC, BLZT) VALUES ('330524104202', '荒草圩村', '1');
INSERT INTO TEST_TABLE(XZQHDM, XZQHMC, BLZT) VALUES ('330524104204', '沙石村', '1');
结果:
到此使用Oracle触发器生成流水号的功能就完成了,以上方案仅供参考,若有不足之处请多多指点。