1问题背景
有一个任务系统,限定每天只有两个任务,允许用户每天更换一次任务,在更换任务的同时,创建一个新的任务。
问题:发现一些用户每天的任务数超过了两个的限制,而且这些任务中,存在更换任务的情况
2相关代码
oracle存储过程部分代码
--今天更换任务的数量haveChanged(userID, gradeID, dirCnt); ---如果今天更换任务个数小于1if dirCnt<1 then --查询任务的的状态 select task_state into taskState from user_task_info where user_id = userID and task_id = taskID; --如果任务状态taskState = 0,表示任务进行中,可以进行更换任务 if taskState = 0 then --修改任务状态 update user_task_info set task_state = 3, m_time = sysdate where user_id = userID and task_id = taskID; --1.commit; --创建一个任务 initUserTask(userID, gradeID); --2.commit; end if;end if;
表面看上面的程序,应该是没有问题
1首先检查今天是否更换过任务
2如果没有更换过任务,检查更换任务的任务状态
3如果任务处于进行中,修改任务状态为更换,调用initUserTask存储过程创建一个新任务
问题分析:一般存储过程执行速度都是非常快的,都毫秒级别的,所以大部分用户更换任务都是没有问题的。
但是程序有严重的并发问题,当用户第一次请求没有commit之前,用户又来一次请求,就会造成创建多个任务
调度时刻 | 请求1 | 请求2 |
T1 | 查询今日是否更换过任务 | |
T2 | 查询当前任务是否允许更新 | |
T3 | 修改任务状态 | |
T4 | 创建一个新任务 | |
T5 | 查询今日是否更换过任务 | |
T6 | 查询当前任务是否允许更新 | |
T7 | commit | |
T8 | 修改任务状态 | |
T9 | 创建一个新任务 | |
T10 | commit |
commit放在修改任务状态之后,或者创建任务之后都是一样
当用户第一个请求没有commit之前,第二个请求只要能进来,就会造成多创建任务
3解决方法
--今天更换任务的数量haveChanged(userID, gradeID, dirCnt); ---如果今天更换任务个数小于1if dirCnt<1 then ---如果任务状态为0进行中时,更新任务状态3表示为更换任务 update user_task_info set task_state = 3, m_time = sysdate where task_state=0 and user_id = userID and task_id = taskID; ---查看sql执行条数 rows := SQL%ROWCOUNT; ---1.commit; ---如果任务更换成功 创建一个任务 if rows=1 then initUserTask(userID, gradeID); end if; ---2.commit;end if;
解决方法:
第一个请求执行update操作时,oracle会进行锁数据操作
第二个请求update操作相同记录时,发现数据已锁,会处于等待状态。
当第一个请求修改任务状态后,rows=1,创建新任务,事务提交后,数据解锁
第二个请求执行update操作时,数据已经不满足查询条件,rows=0就无法创建任务
4总结
在oracle中,事务没有提交之前,update和delete为锁数据操作,如果操作的不是同一条数据,可以同时进行操作
insert为锁表操作,在向一个表insert操作时,会先检查表中是否数据被锁,如果有数据被锁,则等待
如果没有数据被锁,则进行锁表操作