只执行这个就死锁还是两个线程一起执行就死锁? Select * FROM TableA LEFT JOIN TableBSelect Count(*) FROM TableA LEFT JOIN TableB
有关联,通过聚焦索引Id字段关键的. Id是GUID类型
下面这个是把语句拉出来实测的 上面的语句先执行,再执行select就死锁了
为什么要waitfor delay? 像楼主这样的情况,我看像是表数据量大,或频繁执行select count() 语句,导致与insert事务出现死锁。select count(..) from部分, 可以先通过降低事务隔离级别来避免这样的死锁。如在select count() from 部分的回话中设置: set transaction isolation level read uncommitted 还可以检查select count 部分的性能,是否可以建立索引來优化。
select Count(*) ... from a inner join b 没where条件,目的是干啥? 如果這樣,與insert a ....insert b 事务,不死锁都难。如果不降低事务隔离级别,就要提升select 部分的查询效率和减低insert事务部分的时间。先检查表有没有索引,有没有主键,外键。外键上记得要创建索引,这样可以减少类似死锁的发生。
真实条件是
TableA.Id=TableB.Id
Select *
FROM TableA
LEFT JOIN TableBSelect Count(*)
FROM TableA
LEFT JOIN TableB
Id是GUID类型
上面的语句先执行,再执行select就死锁了
像楼主这样的情况,我看像是表数据量大,或频繁执行select count() 语句,导致与insert事务出现死锁。select count(..) from部分, 可以先通过降低事务隔离级别来避免这样的死锁。如在select count() from 部分的回话中设置:
set transaction isolation level read uncommitted 还可以检查select count 部分的性能,是否可以建立索引來优化。
如果這樣,與insert a ....insert b 事务,不死锁都难。如果不降低事务隔离级别,就要提升select 部分的查询效率和减低insert事务部分的时间。先检查表有没有索引,有没有主键,外键。外键上记得要创建索引,这样可以减少类似死锁的发生。
2、锁表和锁是不衡等的,不是每次都锁表。
3、insert锁表的顺序是先在表上加IX 在页上加IX ,在ROW上加X
4、SELECT COUNT会在所有页上加S锁,而你join会导致两个表都加S锁,锁的范围更广。
5、当线程Aselect 的时候,在两个表上加了S锁,线程B此时第一次INSERT,加IX锁,这个没问题。但是接下来线程A又一次SELECT ,由于S锁在默认隔离级别下select完就会释放,所以第二次select时再次加S锁,但是由于B对TableA加了IX锁,所以A线程不能对TableA加S锁。另外线程B在第一次insert的时候由于A/B两表有外键,所以B的对应数据上也会加IX锁,第二次insert 到B的时候IX和前面INSERT中B表上的IX不兼容,又需要等待,导致A、B两个线程都在等待。最终死锁。
这个好像有点乱。比较不好理解。解决建议:
1、在A/B两表(不是线程)上的外键关联列,如果没有索引的话,加上一个非聚集索引
2、在A/B两表上,找出最窄的非NULL列,建一个索引。
对1楼的第2个死锁图进行简要说明:
===================================1.根据图,先对他们作几个标记:
--------------------------
右边的椭圆: 进程103
左边的椭圆: 进程75
中间位置上边框: 资源37467
中间位置下边框: 资源36940
2.分析图,同一时间内存在下面两种情况:
----------------------------------- a: 进程103对资源37467,持有1个意向独占锁(IX) ,与此同时,进程75对资源37467,请求1个共享锁(S)
b: 进程75对资源36940,持有1个共享锁(S) ,与此同时,进程103对资源36940,请求1个意向独占锁(IX)3.使用二维表描述两个资源的状态:
-----------------------------------------
资源 持有 请求
-----------------------------------------
资源37467 进程103(IX) 进程75(S)
资源36940 进程75(S) 进程103(IX)4.由于在lock models中,IX锁和S锁是互斥,不兼容。简单说就是一个资源,存在了IX锁,不能再存在一个S锁,反之亦然;
在这里场景中,它们两个进程同时对两个资源各存在‘持有’和‘请求’的状态,
由于两个进程不会主动释放资源,它们会进入一个持有与请求的僵局。5.sql server系统(boss),发现这样一个僵局,没办法,为了维持资源的可利用,对对立的双方进行仲裁,宣判其中一个进程作为牺牲品,另一进程继续作业。
这是锁的规则。你想不互斥,可以在Select部分加nolock,如select count(1) from tableA with(nolock) inner join tableB with(nolock) on ...不过,这样的结果会出现脏读,也就是其他进程未提供的tran,你select ...(nolock)都可以看到未提交的数据。
就是一个包含2张表的视图,做新增数据的时候,这2张表的数据需要在同一个事务里insert
查询这个视图的时候也要一起查出来,现在就遇上这样的问题了.
只要先insert事务,再查询这个视图,就会死锁.
没想到这么简单的业务会带来这么麻烦的问题,请问针对这样的业务一般用什么方案呢?其实我的2张表保存的是同一张单据,但是单据中某些字段很大,为了提交效率单独做了1张表来存放经常需要更新的字段
/*
--可以查一下该page里什么数据
--不过这个死锁极好想想
--你一条语句拥有A表的共享锁,然后,请求B表的共享锁
--你另一条语句,拥有B表的排它锁,请求A表的排它锁。
--如下建议,修改隔离模式。
--uncommit read 可以解决
*/
既然两表是一对一的,没必要JOIN 后在计数,随便对一个计数就可以了吧?
sysindexes.rowcnt 有记录数,拿主键的名称取查好了,用不着 COUNT(*),也就不会有锁了。又:已经有了 SELECT * ,再来一个 SELECT Count(*),这样真不觉得奇怪吗?
既然两表是一对一的,没必要JOIN 后在计数,随便对一个计数就可以了吧?
sysindexes.rowcnt 有记录数,拿主键的名称取查好了,用不着 COUNT(*),也就不会有锁了。又:已经有了 SELECT * ,再来一个 SELECT Count(*),这样真不觉得奇怪吗?1,用Join再Count是因为实际上Join的写法在视图里面,外面是没办法的
2,前面用select *其实只是一种写法,实际是分页后取某一页的值,可能只取50条 ,所以后面需要用select Count(*)
2.SELECT TOP 50 ...