sql server 多线程update 丢失更新 求助

wyx900620 2018-10-12 06:24:56
create proc test
@sno int
as

--1.aa中每个sno有10000条数据
insert into a (value,sno) select from aa where sno = @sno

--2.bb中每个sno有10000条数据
insert b (value,sno) select from bb where sno = @sno

--3.更新数据
update a with(tablock, xlock) set a.value = b.value from a,b where a.sno = b.sno and a.sno = @sno

return 0
go


问题:
多线程执行上述存储过程(10个线程左右,每个线程的 @sno不同),更新数据不全(线程1应该更新10000条,可是只更新了999950条)。请教大家是为什么?

这种情况不容易发生,运行100次左右发生1次,也不一定是哪个线程丢失了数据。初步跟踪应该是线程2中的insert b (value,sno) select from bb where sno = @sno影响到了线程1中的update a with(tablock, xlock) set a.value = b.value from a,b where a.sno = b.sno and a.sno = @sno。可是为什么会这样影响?
...全文
542 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
wyx900620 2018-10-19
  • 打赏
  • 举报
回复
引用 5 楼 yenange 的回复:
[quote=引用 4 楼 wyx900620 的回复:]
感谢回答,对同一个表做很多增删改多线程意义不大,还需要各种加锁,已经考虑分表或者其他处理办法。但是想搞明白这个是为什么(按道理不应该只update一部分数据)。另外,使用您提供事务好像会造成死锁。


疏忽了, 两个表一起加锁, 还要并发确实容易死锁。

你弄一个可以重现的测试库, 备份之后, 打个压缩包, 放云盘共享, 把测试代码也贴出来, 确保能重现你的bug吧。[/quote]

十分感谢回答,最近比较忙,有空再搞一个单独的测试版本。
吉普赛的歌 2018-10-15
  • 打赏
  • 举报
回复
引用 4 楼 wyx900620 的回复:
感谢回答,对同一个表做很多增删改多线程意义不大,还需要各种加锁,已经考虑分表或者其他处理办法。但是想搞明白这个是为什么(按道理不应该只update一部分数据)。另外,使用您提供事务好像会造成死锁。
疏忽了, 两个表一起加锁, 还要并发确实容易死锁。 你弄一个可以重现的测试库, 备份之后, 打个压缩包, 放云盘共享, 把测试代码也贴出来, 确保能重现你的bug吧。
wyx900620 2018-10-15
  • 打赏
  • 举报
回复
引用 3 楼 yenange 的回复:
create proc test
@sno int
as
BEGIN
--1.aa中每个sno有10000条数据
insert into a (value,sno) select from aa where sno = @sno

--2.bb中每个sno有10000条数据
insert b (value,sno) select from bb where sno = @sno

--3.更新数据
BEGIN TRAN
BEGIN TRY
SELECT * FROM a WITH(XLOCK, ROWLOCK) INNER JOIN b WITH(XLOCK, ROWLOCK) ON a.sno = b.sno and a.sno = @sno
update a set a.value = b.value from a INNER JOIN b ON a.sno = b.sno and a.sno = @sno
COMMIT TRAN;
END TRY
BEGIN CATCH
DECLARE @errMsg NVARCHAR(MAX)
SET @errMsg=ERROR_MESSAGE()
RAISERROR(16,1,@errMsg)
ROLLBACK TRAN;
END CATCH

return 0
END
go


可能跟只锁了一个表有关系。
按上面的来试下。

不过, 你这一次就要更新那么多的记录, 实际上多线程少线程、加锁什么的已经没太意义了。
不需要那么多高大上的东西, 数据库同一个表的不同进程更新本身就是有影响的。

只留一个进程来更新, 每次只更新 100~1000 条, 循环更新, 试下效果。



注意:数据库的恢复模式改成简单, 更新速度会加快。
另外, 减少不必要的触发器、索引也能加快更新的速度。


感谢回答,对同一个表做很多增删改多线程意义不大,还需要各种加锁,已经考虑分表或者其他处理办法。但是想搞明白这个是为什么(按道理不应该只update一部分数据)。另外,使用您提供事务好像会造成死锁。
吉普赛的歌 2018-10-14
  • 打赏
  • 举报
回复
create proc test
    @sno int
as 
BEGIN
	--1.aa中每个sno有10000条数据
	insert into a (value,sno) select from aa where sno = @sno

	--2.bb中每个sno有10000条数据
	insert b (value,sno) select from bb where sno = @sno

	--3.更新数据
	BEGIN TRAN
	BEGIN TRY
		SELECT * FROM a WITH(XLOCK, ROWLOCK) INNER JOIN b WITH(XLOCK, ROWLOCK) ON a.sno = b.sno and a.sno = @sno
		update a set a.value = b.value from a INNER JOIN b ON a.sno = b.sno and a.sno = @sno
		COMMIT TRAN;
	END TRY
	BEGIN CATCH
		DECLARE @errMsg NVARCHAR(MAX)
		SET @errMsg=ERROR_MESSAGE()
		RAISERROR(16,1,@errMsg)
		ROLLBACK TRAN;
	END CATCH

	return 0
END
go
可能跟只锁了一个表有关系。 按上面的来试下。 不过, 你这一次就要更新那么多的记录, 实际上多线程少线程、加锁什么的已经没太意义了。 不需要那么多高大上的东西, 数据库同一个表的不同进程更新本身就是有影响的。 只留一个进程来更新, 每次只更新 100~1000 条, 循环更新, 试下效果。 注意:数据库的恢复模式改成简单, 更新速度会加快。 另外, 减少不必要的触发器、索引也能加快更新的速度。
wyx900620 2018-10-14
  • 打赏
  • 举报
回复
引用 1 楼 wmxcn2000 的回复:
从这三个语句上来看,应该是某个 sno 的数据,不是 1000 个,而是 950 个。

事务可以保证数据是一致的。


不是950个。我把有问题的数据插入到临时表中,数据没问题。相同的数据重新运行,就没问题了。(相同的数据多次运行,有时几十次,有时上百次,偶尔才能重现一次)
卖水果的net 2018-10-13
  • 打赏
  • 举报
回复
从这三个语句上来看,应该是某个 sno 的数据,不是 1000 个,而是 950 个。 事务可以保证数据是一致的。

22,210

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 疑难问题
社区管理员
  • 疑难问题社区
  • 尘觉
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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