问答题 .阅读下列说明,回答问题1和问题2。
    [说明]
    某抢红包软件规定发红包人可以一次抛出多个红包,由多个人来抢。要求每个抢红包的人最多只能抢到同一批次中的一个红包,且存在多个人同时抢同一红包的情况。给定的红包关系模式如下:
    Red(ID,BatchID,SenderID,Money,ReceiverID)
    其中ID唯一标识每一个红包;BatchlD为发红包的批次,一个BatchID值可以对应多个ID值;SenderID为发红包人的标识;Money为红包中的钱数;ReceiverID记录抢到红包的人的标识。
    发红包人一次抛出多个红包,即向红包表中插入多条记录,每条记录表示一个红包,其ReceiveflD值为空值。
    抢某个红包时,需要判定该红包记录的ReceiveflD值是否为空,不为空时表示该红包已被抢走,不能再抢,为空时抢红包人将自己的标识写入到ReceiverID字段中,即为抢到红包。
问答题 1.  [问题1]
    引入两个伪指令a=R(x)和W(b,X)。其中a=R(x)表示读取当前红包记录的ReceiverID字段(记为数据项X)到变量a中,W(b,X)表示将抢红包人的唯一标识b的值写入到当前红包记录的ReceiverID字段(数据项X)中,变量a为空值时才会执行W(b,X)操作。假设有多个人同时抢同一红包(即同时对一记录进行操作),用ai=Ri(X)和Wi(bi,X)表示系统依次响应的第i个人的抢红包操作。假设当前数据项X为空值,同时有三个人抢同一红包,则
    (1)如下的调度执行序列:
    a1=R1(X),a2=R2(X),W1(b1,X),W2(b2,X),a3=R3(X)
    抢到红包的是第几人?并说明理由。
    (2)引入共享锁指令SLocki(X)、独占锁指令XLocki(x)和解锁指令UnLocki(X),其中下标i表示第i个抢红包人的指令。如下的调度执行序列:
    SLock1(X),a1=R1(X),SLock2(X),a2=R2(X),XLock1(X)…
    是否会产生死锁?并说明理由。
    (3)为了保证系统第一个响应的抢红包人为最终抢到红包的人,请使用上述(2)中引入的锁指令,对上述(1)中的调度执行序列进行修改,在满足2PL协议的前提下,给出一个不产生死锁的完整的调度执行序列。
【正确答案】(1)第2人。
   对同一数据项的写入,先写的会被后写的值所覆盖。
   (2)会产生死锁。
   在执行序列SLock1(X),a1=R1(x),SLock2(X),a2=R2(X),XLock1(X)…中,XLockl(x)指令会因为当前X上已经有SLock2(X)所加的锁,因锁冲突而等待第2人释放锁,而随后会有XLock2(X)令等待第1人释放指令SLock1(X)所加的锁,相互等待形成死锁。
   (3)执行序列:
   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.  [问题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”。