问答题2. [问题2] 下面是用SQL实现的抢红包程序的一部分,请补全空缺处的代码。 CREATE PROCEDURE ScrambleRed (IN BatchNo VARCHAR(20), --红包批号 IN mecvrmo VARCHAR(20) ) --接收红包者ID BEGIN --是否已抢过此批红包 if exists( SELECT * FROM Red WHERE BatchID = BatchNo AND ReceiverID = RecvrNo) then return -1; end if; --读取此批派发红包中未领取的红包记录ID DECLARE NonRecvedNo VARCHAR(30); DECLARE NonRecvedRed CURSOR FOR SELECT ID FROM Red WHERE BatchID = BatchNo AND ReceiverID IS NULL; --打开游标 OPEN NonRecvedRed; FETCH NonRecvedRed INTO NonRecvedNo; while not error --抢红包事务 BEGIN TRANSACTION; //写入红包记录 UPDATE Red SET ReceiverID = RecvrNo WHERE ID = NonRecvedNo AND ______; //执行状态判定 if<修改的记录数>=1 then COMMIT; ______; return 1; else ROLLBACK; end if; ______ end while; --关闭游标 CLOSE NonRecvedRed; return 0; END
【正确答案】ReceiverID IS NULL CLOSE NonRecvedRed FETCH NonRecvedRed INTO NonRecvedNO
【答案解析】 本题考查事务并发控制及事务编程。 此类题目要求考生熟练掌握事务基本概念、事务并发控制实现原理和技术,以及事务程序的编写。 [问题1] 根据题目描述,抢红包操作是将抢红包人的ID写入到红包记录的ReceiverID字段。多人抢同一红包即为对同一数据项的读写操作。 (1)分析给定的调度执行序列: a1=R1(X),a2=R2(X),W1(b1,X),W2(b2,X),a3=R3(X) 中,a1=R1(X),a2=R2(X)表示抢红包的第一、第二人读取数据项X,X当前值为空值,两人均可写入自己的ID值;而后的W1(b1,X),W2(b2,X)表示第一、第二人先后将自己的ID值写入X项,第一人写入的值会被随后第二人的写入值所覆盖,X的当前值为第二人ID;a3=R3(X)表示第三人读取X项的值,X的当前值非空(即第二人的ID),根据题目描述的规则“变量a为空值时才会执行W(b,X)操作”,第三人不能再写入自己的ID值。序列执行结束时,X项的值为第二人得ID,故抢到红包的为第二人。 (2)引入锁指令后的调度执行序列: SLock1(X),a1=R1(X),SLock2(X),a2=R2(x),XLock1(X)…中,执行完指令SLock1(X),a1=R1(X),SLock2(X),a2=R2(X)后,数据项X上有事务T1(第一人的抢红包事务)和事务T2(第二人的抢红包事务)分别加的共享锁;随后的指令XLock1(X)为事务T1再对数据项加独占锁,此时数据项X上已有事务T2所加的共享锁。根据锁冲突规则,XLock1(X)指令加锁失败,事务T1处于等待状态,等待事务T2释放X上的共享锁;根据事务的程序逻辑,稍后事务T2也会运行XLock2(X)指令申请对X数据项加独占锁,同样的,事务T2会等待事务T1释放X上的共享锁,T1、T2两个事务相互等待对方释放锁,陷入死锁状态。 (3)为了保证系统第一个响应的抢红包人为最终抢到红包的人,抢红包事务可以在读取数据项X之前执行XLock(X)直接加独占锁,此后的抢红包事务对X项加锁,只能等待第一人的事务T1执行结束,此时数据项己写入第一人的ID值,后续事务读到非空值,无法再写入自己的ID。 直接使用XLock(x)后的指令序列为:XLock1(x),a1=R1(x),W1(b1,x),UnLock1(x),XLock2(X),a2=R2(X),UnLock2(X),XLock3(X),a3=R3(X),UnLock3(X) [问题2] 本问题是用存储过程编写的抢红包事务程序。用户通过调用该存储过程完成抢红包操作。因此,存储过程先查询该用户是否已抢过此批次的红包;然后以游标的方式读取到此批次当前所有未抢的红包(可能是多个),以事务的方式对游标中的当前记录写入用户ID。由于多人同时抢红包,游标所查询到的未抢红包,可能在写入用户ID时,已经被其他人写入,故写入程序应该添加条件“ReceiverID值为空”,即第一处应填入“ReceiverID IS NULL”。如果出现两个及以上用户同时对同一条记录写入,此时会由DBMS进行并发控制,保证第一个响应的用户写入不被覆盖。 根据抢红包规则,一个用户只能抢到一批红包中的一个,因此成功抢到红包后(成功更新一条红包记录),应该退出程序,不能进入下一轮循环再去抢下一个红包。程序中使用了游标,在退出程序前应该关闭游标,因此第二处应该填写关闭游标的指令“CLOSENonRecvedRed”。 游标操作循环体中的最后一条指令应该是推进游标指针,故第三处应填写“FETCHNonRecvedRed INTO NonRecvedNo”。