我有一个交易数据库。其中一张桌几乎是空的(A)。它有一个唯一的索引列(x),没有聚簇索引。
2个并发交易:
begin trans
insert into A (x,y,z) (1,2,3)
WAITFOR DELAY '00:00:02'; -- or manually run the first 2 lines only
select * from A where x=1; -- small tables produce a query plan of table scan here, and block against the transaction below.
commit
begin trans
insert into A (x,y,z) (2,3,4)
WAITFOR DELAY '00:00:02';
-- on a table with 3 or less pages this hint is needed to not block against the above transaction
select * from A with(forceseek) -- force query plan of index seek + rid lookup
where x=2;
commit
我的问题是,当表有很少的行时,2个事务可以死锁,因为SQL Server为select生成表扫描,即使有索引,并且两个都等待新插入的行持有的锁另一笔交易。
当此表中有很多行时,查询计划会更改为索引搜索,并且两者都很乐意完成。
当表格很小时,WITH(FORCESEEK)
提示会强制执行正确的查询计划(对于小表格来说要贵5%)。
是否可以为表格上的所有查询提供默认提示,假装有'forceseek'提示?
上面的死锁代码是由Hibernate生成的,是否有可能让hibernate发出所需的查询提示?
我们可以使表格假装足够大,以便查询优化器选择UPDATE STATISTICS
http://msdn.microsoft.com/en-AU/library/ms187348(v=sql.110).aspx中未记录的功能的索引搜索。任何人都可以看到使所有少于1000行的表假装他们在10页上有1000行的任何缺点吗?
答案 0 :(得分:4)
您可以创建Plan Guide。
或者您可以在数据库中启用Read Committed Snapshot隔离级别。
更好的是:使索引聚集。
对于经历高更新率的小型表,也许您可以应用Using tables as Queues的建议。
任何人都可以看到使所有少于1000行的表假装他们在10页上有1000行的任何缺点吗?
如果该表出现在另一个更复杂的查询(思考联接)中,那么基数估计值可能会大幅下降并产生错误的计划。
答案 1 :(得分:3)
您可以创建一个视图,该视图是表的副本,但使用提示并让查询使用该视图:
create view A2 as
select * from A with(forceseek)
如果要保留查询使用的表名,请将表重命名为其他名称,然后将视图命名为" A":
sp_rename 'A', 'A2';
create view A as
select * from A2 with(forceseek)
答案 2 :(得分:0)
只需添加另一个您可以考虑的选项。
您可以使用
锁定整个表格ALTER TABLE MyTable SET LOCK_ESCALATION = TABLE
如果您没有太多会排队并降低性能的更新,则此解决方法很好。
它是表格范围的,不需要更新其他代码。