none
行锁定问题 RRS feed

  • 常规讨论

  • 我在做并发控制时,试用了行锁,请各位帮忙看看这样做是否有问题

    存储过程类似如下

    --添加销售产品明细到销售单
    
    CREATE PROC XXX
    
    @XiaoShouDanID INT,--销售单ID
    
    @ChanPinID,--产品明细ID
    
    @ShuLiang,--产品数量
    
    AS
    
    
    
    BEGIN TRAN
    
    --行锁
    
    SELECT * FROM XiaoShouDan WITH(UPDLOCK,ROWLOCK)
    
    WHERE XiaoShouDanID=@XiaoShouDanID
    
    --先取出销售明细ID,XiaoShouMingXiID为主键,主要是用NOLOCK取出主键,放到临时表#mxID
    
    SELECT XiaoShouMingXiID INTO #mxID
    
    FROM XiaoShouMingXi WITH(NOLOCK)
    
    WHERE XiaoShouDanID=@XiaoShouDanID
    
    ORDER BY XiaoShouMingXiID 
    
    --再行锁定销售明细
    
    SELECT * INTO #tmpMX FROM XiaoShouMingXi WITH(UPDLOCK,ROWLOCK)
    
    WHERE XiaoShouMingXiID IN (SELECT #mxID)
    
    
    
    ---接下来是一系列运算和库存扣减问题....

    目前遇到的是库存不准的问题,不能锁表,因为多用户操作,提高并发性能,必须用行锁,表结构也不能改变了

    看看各位给些建议....


    2014年7月29日 13:47

全部回复

  • Take look at snapshot isolation, may help here.
    2014年7月29日 14:18
  • 具体如何实现呢,能否给个sample
    2014年7月29日 14:20
  • Books online has details.
    2014年7月29日 19:56
  • 这样用

    --窗口1
    IF OBJECT_ID('T') IS NOT NULL
    DROP TABLE T;
    CREATE TABLE T(ID INT IDENTITY UNIQUE,Num INT)
    INSERT T SELECT 1
    INSERT T SELECT 2
    GO
    BEGIN TRAN
    SELECT * FROM  T WITH (UPDLOCK, READPAST) WHERE ID=1

    --ROLLBACK


    --窗口2
    BEGIN TRAN
    SELECT * FROM  T WITH (UPDLOCK, READPAST) WHERE ID=1--没结果集
    SELECT * FROM  T WITH (UPDLOCK, READPAST) WHERE ID=2 --不影响
    --ROLLBACK

    Roy Wu(吳熹Blog)(微博)

    2014年8月1日 3:31
    版主
  • 用快照隔离级别要小心,因为可能会存在逻辑错误

    举个例子描述这个场景:

    T1事务发起一个修改,读取原库存是10,需求修改库存减1,原库存应该变成9,因为是READ_COMMITTED_SNAPSHOT隔离级别,所以数据库会在tempdb里生成一个快照,但是事务未提交,在这时发起了第二事务T2,也来修改库存,因为看到事务T1未提交,所以他不能获取未提交事务修改的值9(如果获取9就是脏读了),而是他获取的是最后提交版本的库存为10,而正巧T2未提交前,T1先提交了,实际库存应该变9而不是10,但T2事务获取库存值是10,假设T2的需求是减库2,那么最后T2提交后,会覆盖T1事务所做的修改,库存变成了8(我们实际期望的是10-1-2=7),这样就造成了逻辑混乱。

    2014年8月1日 3:43
  • 这样用

    --窗口1
    IF OBJECT_ID('T') IS NOT NULL
    DROP TABLE T;
    CREATE TABLE T(ID INT IDENTITY UNIQUE,Num INT)
    INSERT T SELECT 1
    INSERT T SELECT 2
    GO
    BEGIN TRAN
    SELECT * FROM  T WITH (UPDLOCK, READPAST) WHERE ID=1

    --ROLLBACK


    --窗口2
    BEGIN TRAN
    SELECT * FROM  T WITH (UPDLOCK, READPAST) WHERE ID=1--没结果集
    SELECT * FROM  T WITH (UPDLOCK, READPAST) WHERE ID=2 --不影响
    --ROLLBACK

    Roy Wu(吳熹Blog)(微博)

    用readpast最好个人觉得
    2014年8月1日 3:45
  • But you may not get completed result set with readpast.
    2014年8月1日 13:20
  • Readpast 不是跳过锁了吗,如果两人同时操作的话,那不是发生幻读了
    2014年8月5日 6:16
  • Readpast 不是跳过锁了吗,如果两人同时操作的话,那不是发生幻读了
    不会幻读,当一个人在读的时候,第二个人读的时候会跳过该条记录
    2014年8月5日 6:36