高并发场景下oracle触发器+序列产生序号的一些现象与思考

最近工作上因为在处理系统同步的时候遇到了一些问题,在解决过程中,发现了一些现象,所以在这里mark一下,我现有的残缺理论体系还无法支撑做出合理的解释,在网上找了一下,也没有找到类似的案例,还望各位大拿指点一二。

话不多说,直接上案例,在pl/sql上做的模拟。


原始案例(故障):

1.先创建一个表test,三个字段id,name,sno

create table test(id int,name varchar2(10),sno int);
2.创建一个序列,起始1,单步增加1

create sequence ss_test minvalue 1 nomaxvalue increment by 1 start with 1 cache 10000;

3.创建一个触发器
create or replace trigger tr_test before UPDATE or insert on test for each row
begin
    :new.sno := ss_test.nextval;
end;

也就是说 如果我在test上做新增,修改的话,会将该条记录的sno值置为序号发生器ss_test的下一个值。

非并发场景下(也就是未发生阻塞):

insert into test(id) values(1);
insert into test(id) values(2);
insert into test(id) values(3);
 
select * from test;
插入三条记录,触发器工作,并且sno的值等于当前序列值


没毛病,好,来试试更新


这时,id为1的记录sno又累加了,依旧没毛病


好的,现在换个场景,来到并发状态,在我们的软件实际运行状态下,多个线程或进程进行写表或者更新操作的时候,此时我们在pl/sql中用两个会话进行模拟,在oracle的事务隔离机制下,如果两个会话同时更新同一条记录,那么后执行的将会进入阻塞状态,直到另一个会话提交或回滚释放资源。

会话一:更新id为1的记录的名字为‘jay’,不进行提交,此时可以看到sno的值已经更新到了5



新建会话,同样去更新id为1的记录的name,进入挂起状态,等待会话一释放资源



重点来了,提交会话一,会话二马上就会执行,而sno的值直接跳跃式地成了7


以上就是开发过程中发现的一个现象,还请各位大佬能指点一下这种现象的原因,同时,我在探索过程中,模拟了几个其它场景,排除了几个我之间假定的原因,供参考一下。




场景1.当执行的update语句中直接包含对sno的修改时,将不会产生上述现象。

把test表恢复到插入3条记录的状态,把序列也恢复到初始化状态



更新id为1的记录,将sno置为0,触发 触发器,sno又被置为了序列的新值4,未提交



此时,新开一个会话,也更新id为1的记录,sno置为999,进入阻塞状态


提交会话一,会话二执行,令人奇怪的是,此时的sno未连续增长,老老实实地变成了5,可见oracle触发器 在处理update语句中 是否包含:new.sno对应的sno字段的修改 是两种机制






场景2.修改触发器本身,不直接处理test表中的sno值,而是将序列值插入另一张表。


新建一个表test2

create table test2(sno int);
修改触发器tr_test,也就是在新增和修改test的时候,将序列的新值插入另外一张表中记录流水号
drop trigger tr_test;
create or replace trigger tr_test before update or insert on test for each row
begin
    insert into test2 values(ss_test.nextval);
end;
test还是和之前一样插入三条记录并更新

test值如下:


而test2中也已经插入对应的三条记录


现在来试试并发下的update,更新id为1的记录的name,置为‘kk’,不提交,test如下:


test2:4已经写入


新建会话,同样更新id为1的记录设置name = 'xx',阻塞:


提交会话一,会话二马上得到执行,此时的值也没有跳跃增长,累加1为5:





场景3:取序列的值去判断在阻塞状态时,序列的值是不是已经做了累加,结果是在阻塞的时候,序列中值并没有累加,问题变得更加有趣了,也就意味着在会话一提交释放的时候,序列的值直接加了2,应该说是进行了两次累加。


修改触发器为开始的状态:

create or replace trigger tr_test before UPDATE or insert on test for each row
begin
    :new.sno := ss_test.nextval;
end;

test表依旧插入三条记录:


更新test,id为1的记录name设置为‘GG’,不提交:


test2的值更新到了4:


更新test设置id为1的记录name为‘XX’,不提交:


新建会话,同样更新test中id为1的记录的name值,此时被阻塞:


回到会话一,取序列ss_test.nextval(不取currval,是因为currval只是当前会话或者说是当前事务中,上一个取nextval返回的值,也就是说还是第一次更新时产生的值:4),此时,取得ss_test.nextval是5,也就是当前值说是接着会话一之间产生的4来的,会话二在阻塞状态下并没有让序列的值进行累加


提交会话一,会话二马上得到执行:amazing,sno的值直接成了7,也就是在执行时,序列有两次累加



以上就是故障案例和3个排错案例。


猜你喜欢

转载自blog.csdn.net/aricover/article/details/73469233
今日推荐