建议不用锁,在插入时进行错误处理,当因单号重复而插入不成功时,再取一次单号进行插入.
如果要保证不死锁,只能用排它锁(xlock),将降低并发性.

解决方案 »

  1.   

    兄弟 ,你只要 把存储过程 get_bill 中的 select 和 update 两个句子 调换位置就不会出现死锁了。
    在默认的读已提交隔离级别中,共享锁 一 select 完就马上释放,而不是等到事务结束
    反之,排它锁 一直持有到 事务结束。
      

  2.   

    把存储过程 get_bill 中的 select 和 update 两个句子 调换位置 就能实现
    在高并发的情况下保证得到的单号不重复 并且不死锁
      

  3.   

    调换位置后,还是会出现死锁。其实我的想法是,只要是读了这张表的这一行开始,就已经是排他锁,其它进程就不能读。虽然这样我是可以实现了,但是得到的不是行级别的锁,我的两个测试语句读的不同的行,但是还是被同时锁住了,因为一套ERP系统里面有多个流水号,我就专门用一张表来存储所有单据类型的流水号,如果每取一次就锁住整张表,代价太大了。
      

  4.   

    无语
    1.我们这里几个系统 都是在事务中先 update 加1 然后 select (当然不调换位置也行,select 语句 加上 xlock,holdlock 也可),从来没出现过重复单号,也没有死锁过,是你哪里搞错了。
    2.你这张表 最多不就几十行数据,锁住整张表代价一点都不大,如果你觉得这样的代价也大的话那么数据库中的几百张可能会 查询,插入,更新的 表,每张表每次 select 都要加 行锁,见过这样的 系统吗,从来没见过!
      

  5.   

    这种情况不应该使用事务,加个时间戳即可。
    修改楼主的代码如下:USE tempdb
    GO
    --建表
    IF OBJECT_ID(N'[sys_billno]',N'U') IS NOT NULL
    DROP TABLE [dbo].[sys_billno]
    GO
    CREATE TABLE [dbo].[sys_billno](
    [ID] [int] NOT NULL,
    [bill_type] [varchar](10) NULL,
    [sn] [int] NULL,
    [ts] TIMESTAMP, --加了个时间戳
    CONSTRAINT [PK_sys_billno] PRIMARY KEY CLUSTERED ([ID] ASC)
    ) ON [PRIMARY]
    GO
    insert into sys_billno(ID,bill_type,sn) values(1,'Pos',1),(2,'INV',1);
    GO--建存储过程
    IF OBJECT_ID(N'[dbo].[get_bill]',N'P') IS NOT NULL
    DROP PROC [dbo].[get_bill]
    GO
    create proc [dbo].[get_bill](@type varchar(10),@billno varchar(20) output)
    as
    begin
    declare @ts timestamp,@sn int
    select @sn=sn,@ts=ts from sys_billno where bill_type=@type
    update sys_billno set sn=sn+1 where bill_type=@type and ts=@ts

    while @@ROWCOUNT=0
    begin
    select @sn=sn,@ts=ts from sys_billno where bill_type=@type
    update sys_billno set sn=sn+1 where bill_type=@type and ts=@ts
    end

    set @billno=@type+convert(varchar(10),@sn)
    end
    GO
    存储过程的调用方法与楼主原来的相同。高并发环境下,也不会有任何问题。如OK,记得结贴哦!
      

  6.   

    http://blog.csdn.net/misterliwei/archive/2010/12/14/6076487.aspx
      

  7.   

    www.007sf.com 每天都来学习
      

  8.   

    lz,我运行了,你的例子
    看了一下锁的情况,不是行锁不起作用是select 的时候 会对每个行都请求行锁。
    因为第一个事务,已经对某个资源 进行了加锁, 到第二个事务的select 无法请求到 inv那个锁了。
    update 的是用的是U锁 就是S-》X的一个过程,也是一样的。因此在高并发的情况下 一定出现死锁。直接加tablock锁
      

  9.   

    递增字段可以,但是要单独增加了存储消耗,而且不易控制,如果是ORACLE 的sequence 就好得多,
    不过2008R2的后续版本已经增加这个功能。
      

  10.   

    在高并发的程序中,Oracle的方法很多,在这方面比Sql server强大许多..
      

  11.   

    是什么数据库啊?SQL SERVER 2005还是2008?
      

  12.   

    用事务,先把流水号的字段相应加1,然后再生成单号,再添加数据到表单, 成功就提交事务,不成功就回滚。 
        SET ANSI_WARNINGS  OFF
        set nocount on
        declare @intError int
        declare @billNo varchar(50)
        set @intError = 0
        BEGIN TRAN t1
        update s_seeks set seekvalue = seekvalue +1 
        where category = '%0:s' and datediff(day, seekdate, getdate()) = 0 and seektag = '%1:s'
        
        SELECT @billNo = (select sTag from s_billcoderegu where billCode = '%0:s') + convert(varchar(6), seekdate, 12) + seektag + dbo.fcFormatInt('000', seekvalue-1)
        from s_seeks
        where category = '%0:s' and datediff(day, seekdate, getdate()) = 0 and seektag = '%1:s' --这里是添加相应的表单数据
        set @intError = @@error
        if @intError = 0
          commit tran t1 
          else
          rollback tran t1 
          SET ANSI_WARNINGS  ON
          set nocount off
      

  13.   

    http://blog.csdn.net/zjcxc/archive/2006/09/17/1232660.aspx