这种情况下我应该加什么锁?请教!

Guy_Fwakes 2016-12-05 10:41:09
各位,有这么个应用场景,下面是伪代码,代码是在存储过程中。

---------------------------------------第一部分--------------------------------------------------------------
begin tran
select * from [dbo].[a] with (tablockx); --锁表,不想让其他的事务读取表里的任何数据
if exists(select 1 from [dbo].[a] where 条件) --根据产品代码判断是否存在相关记录
begin
select maxid from [dbo].[a] where 条件; --根据产品代码取出id
if(截取id <> 当前年份)
begin
根据年份重新生成NewId --这里的NewID会被第二部分使用
end
将NewId+1更新表a字段maxid
end
else --如果产品不存在记录
begin
根据当前年份生成NewID
将NewID+1插入表a,生成新的记录
end

--------------------------------------------第二部分-----------------------------------------------------
while 条件
begin
根据NewID生成多条记录插入另外一个表B
end
commit


下面讲一下我的思路
这个存储过程是被多个工作站调用的,每个工作站取出的maxid是不能重复的;为了防止下面这种情况发生:
工作站A取出了maxid,但是没有+1更新表a,这时工作站表B取出maxid
所以我对表a添加了tablockx表锁
因为我的需求是:maxid是不能重复读取的
不知道我说明白了没有,现在的问题是:我的这种表锁是否合理,是否还有更好的锁的处理方式?希望各位不吝赐教!
...全文
128 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
Tiger_Zhao 2016-12-06
  • 打赏
  • 举报
回复
现在还有哪个高级语言没有异常捕获的机制?
Guy_Fwakes 2016-12-06
  • 打赏
  • 举报
回复
引用 9 楼 Tiger_Zhao 的回复:
你前台程序可以细化控制啊:发现是特定的错误(主键重复),可以进行重试。 没规定重试一定要人工参与。
嗯,是的,不过上层的应用是组态软件的开发,我不太熟悉,这就比较尴尬了,而且不太确定有没有这种异常捕获的机制
Tiger_Zhao 2016-12-06
  • 打赏
  • 举报
回复
你前台程序可以细化控制啊:发现是特定的错误(主键重复),可以进行重试。
没规定重试一定要人工参与。
Guy_Fwakes 2016-12-06
  • 打赏
  • 举报
回复
引用 7 楼 Tiger_Zhao 的回复:
并发操作本来就有冲突的。 两个客户端同时下单要买最后一件商品,还能都成功?
你说的这种应用场景是对的,但是我的应用并不是对商品的扣减,而是类似于“生产增加”,举个例子:不能因为你向库存表里插记录,而同时别的事务插记录我就报错吧,而是等第一个插完记录的事务完成,我再更新库存字段,这样比较合理吧? 不管怎么说,还是要谢谢你!
Tiger_Zhao 2016-12-06
  • 打赏
  • 举报
回复
并发操作本来就有冲突的。 两个客户端同时下单要买最后一件商品,还能都成功?
Guy_Fwakes 2016-12-06
  • 打赏
  • 举报
回复
引用 13 楼 Tiger_Zhao 的回复:
当然用过,你看我专家分哪个最多! VB 有 On Error 语法啊。
哈哈,牛逼,不多说了,结贴给分
Tiger_Zhao 2016-12-06
  • 打赏
  • 举报
回复
当然用过,你看我专家分哪个最多!
VB 有 On Error 语法啊。
Guy_Fwakes 2016-12-06
  • 打赏
  • 举报
回复
引用 11 楼 Tiger_Zhao 的回复:
现在还有哪个高级语言没有异常捕获的机制?
VB,不是VB.net,用过吗
Tiger_Zhao 2016-12-05
  • 打赏
  • 举报
回复
开头换一下,后面的相同
begin tran
update [dbo].[a] set maxid = maxid where 条件 -- 只需要锁定相关的一条记录
if (@@ROWCOUNT<>0) -- 有更新就表示存在相关记录
begin

卖水果的net 2016-12-05
  • 打赏
  • 举报
回复

-- 可以考虑改成这样的,不用加锁
create table test(id int, name varchar(10))
go
insert into test values((select isnull(max(id),0) + 1 from test) , 'XXX')
insert into test values((select isnull(max(id),1) + 1 from test) , 'YYY')
go
select * from test 
go
drop table test 
go

(1 行受影响)

(1 行受影响)
id          name
----------- ----------
1           XXX
2           YYY

(2 行受影响)


Guy_Fwakes 2016-12-05
  • 打赏
  • 举报
回复
引用 5 楼 Tiger_Zhao 的回复:
A 表里面[产品代码]就算不是主键也应该是个唯一索引吧,重复插入会报错的。
是的,确实是会因为主键重复导致插入失败,这个方案是可行性的,但是对于上层应用程序来说不是会直接报错了?这样的用户体验也不会太好吧。
Tiger_Zhao 2016-12-05
  • 打赏
  • 举报
回复
A 表里面[产品代码]就算不是主键也应该是个唯一索引吧,重复插入会报错的。
Guy_Fwakes 2016-12-05
  • 打赏
  • 举报
回复
引用 2 楼 Tiger_Zhao 的回复:
开头换一下,后面的相同
begin tran
    update [dbo].[a] set maxid = maxid where 条件 -- 只需要锁定相关的一条记录
    if (@@ROWCOUNT<>0)  -- 有更新就表示存在相关记录
    begin
你好,你的代码很好理解,我也看过了相关解释
引用
已提交读 只读取提交的数据并等待其他事务释放排他锁。读数据的共享锁在读操作完成后立即释放。已提交读是SQL Server的默认隔离级别
如果针对一条“存在”的记录我比较好理解,第一个事务会对表a进行行锁,其他事务等待第一个事务提交之后再读,但是如果不存在相关记录,那么事务会对表a,进行表锁吗,因为如果不对表a进行表锁,其他事务也会进行没有记录的判断,进而插入一条记录,造成多个事务的重复插入。 感谢你的回复,如果可能请予以解答
Guy_Fwakes 2016-12-05
  • 打赏
  • 举报
回复
引用 1 楼 wmxcn2000 的回复:

-- 可以考虑改成这样的,不用加锁
create table test(id int, name varchar(10))
go
insert into test values((select isnull(max(id),0) + 1 from test) , 'XXX')
insert into test values((select isnull(max(id),1) + 1 from test) , 'YYY')
go
select * from test 
go
drop table test 
go

(1 行受影响)

(1 行受影响)
id          name
----------- ----------
1           XXX
2           YYY

(2 行受影响)


你好,如果表里已有数据,你的代码怎么会只更新我关心的字段maxid呢,这样不是成了重复插入了,而2楼的代码还比较好理解,但是仍有个问题就是如果表里没有符合条件的记录,那么它会锁住整个表吗,因为如果多个事务同时访问,可能都会判断没有相关记录结果就是多个事务都会插入一条记录

insert into test values((select isnull(max(id),0) + 1 from test) , 'XXX')
insert into test values((select isnull(max(id),1) + 1 from test) , 'YYY')

27,579

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 应用实例
社区管理员
  • 应用实例社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧