应该对 SQL Server 的锁定架构进行哪些更改才能使其对开发人员更加友好?
我最近遇到了一个相当令人沮丧的情况,即当执行这样的语句 select * from table with (rowlock updlock) where key=value
时,SQL Server 拒绝仅针对主键发出锁它。现在不要误会我的意思,它确实锁定了行,但它更进一步并锁定了表。
我已经阅读了有关 SQL 锁升级的内容,并且研究了在锁提示中使用特定索引,但是您会发现,当表中有大量索引且包含数百万条记录并且需要并发更新时,这并不实用。发生在这些记录上。对于小表和特定查询,可以获得所需的行为,但是当表的宽度很大(许多列)并且有许多进程使用数据时,此方法就会崩溃并可能成为真正的争论点。
我希望看到添加的是一个新的锁定提示,就像PKLock(代表主键锁定)一样,它会针对行的主键发出锁定每当使用索引、表扫描或其他方法来获取行时,它都会检查此锁并遵守它,而不是锁定整个表。
由于不需要发出这样的表锁,这将大大增加针对数据库并行执行代码的能力。
请权衡这个想法,并指出它可能存在的任何缺陷、可以改进的方法或应该添加的其他元素来解决我的困境。
编辑
@Remus
如果我执行此查询
begin transaction
select lockname from locks where lockname='A'
update Locks Set locked=1 where lockname='A'
,然后执行此查询:
begin transaction
select lockname from locks where lockname='A'
在提交事务之前,两个示例中都会返回 A 行。这是更新后面的读取,而不是阻塞。
成功的解决方案应该在不指定要使用的索引的情况下执行以下操作:
- 使用查询 1:读取并锁定 A,更新 A
- 使用查询 2:读取并锁定 B,更新 B,提交查询 2
- 使用查询 2:读取 B 并被阻止直到锁定A 上已发布
- 使用查询 1:提交查询 1
- 使用查询 2:读取并锁定 A,更新 A,提交查询 2
I’ve lately come across a rather frustrating situation where SQL server refuses to issue locks only against a primary key when a statement like this select * from table with (rowlock updlock) where key=value
is executed against it. Now don’t get me wrong here, it does lock the row but it goes one step farther and locks the table too.
I’ve read up about SQL lock escalation and I’ve looked at using specific indexes in the lock hints but you see, this just isn’t practical when there are numerous indexes in a table with millions of records and concurrent updates that need to happen on those records. For a small table and a specific query it’s possible to get the desired behaviour, but when the table has a large width (many columns) and there are numerous processes using the data, this method melts down and can become a real point of contention.
What I’d like to see added is a new lockhint suck as PKLock (which would stand for Primary Key Lock) which would issue a lock against the primary key of a row and anytime an index, a table scan or other method is used to get the row, it would check this lock and honour it instead of locking the entire table.
As such a table lock would not need to be issued and this would greatly increase the capacity for parallel execution of code against the DB.
Please weigh in on this idea and point out any flaws which it might have, ways that it could be improved or other elements that should be added to resolve my dilemma.
EDIT
@Remus
If I execute this query
begin transaction
select lockname from locks where lockname='A'
update Locks Set locked=1 where lockname='A'
and then this query:
begin transaction
select lockname from locks where lockname='A'
Row A is returned in both examples before committing the transactions. This is reading behind the update, not blocking.
A successful solution should do the following without specifying indexes to use:
- With Query 1: Read and lock A, update A
- With Query 2: Read and lock B, update B, Commit query 2
- With Query 2: Read B and be blocked until Locks on A are released
- With Query 1: Commit Query 1
- With Query 2 :Read and lock A, update A, Commit query 2
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您之前曾问过这个问题并得到了答案:修复您的架构和代码。在那篇文章中,锁冲突是 IX 锁,意向锁上的冲突表明高粒度锁,而高粒度锁又表明表扫描。您不需要锁定提示,您只需要一个索引和适当的查询。以您的其他问题为例, 为什么行级锁定在 SQL Server 中似乎无法正常工作?,答案很简单:锁表需要通过 LockName 上的聚集索引来组织:
在一个会话上执行此操作:
在另一个会话上执行此操作:
没有更新冲突,没有阻塞,不需要(错误的)提示,什么都没有。只是很好的正确模式和查询设计。
附带说明一下,您在此处描述的锁已经存在,称为钥匙锁。它们是 SQL Server 运行的默认隐式模式。您到底如何想象 SQL Server 能够发布每秒 16000 tpc 事务的 TPC-C 基准数据?您拥有服务器所需的所有并行能力,您只需阅读一两本书即可了解如何使用它。关于该主题的文献有很多,您可以从 事务处理:概念和技术< /a>.
已更新
无论您尝试多少/不同的锁定提示,这都永远不会起作用。这就是您使用输出语法进行更新的原因:
这可确保您首先更新,然后返回已更新的内容。这种技术在数据库中相当常见,正是为了满足您所寻求的语义:资源获取。事实上,该技术是资源获取典型子项的基石:队列处理。请参阅OUTPUT 子句中的队列段落。在队列中,您有一个要处理的资源表,每个线程获取一个资源,锁定它并开始处理:
现在从单独的会话中运行此命令:
您将看到每个会话获取它自己的资源,锁定它并跳过由其他人。碰巧我在生产中有一个完全像这样运行的系统,表中有超过 500 万个资源(它们是 Web 服务支付处理请求),并且每秒从 100 个并发处理器中出列和处理大约 50 个资源(大约需要 2 秒)。每次调用进程)。在一个垃圾硬件上。所以这绝对是可能的。
You have asked this question before and were given the answer: fix your schema and your code. In that post the lock conflict was an IX lock, and a conflict on intent locks indicates high granularity locks, which in turn indicate table scans. You don't need lock hints, you just need an index and decent queries. Take for instance your other question, Why does row level locking not appear to work correctly in SQL server? where answer is trivial: Locks table needs to be organized by a clustered index on LockName:
On one session do this:
On the other session do this:
There is no update conflict, no blocking, no need for (wrong) hints, nothing. Just good ole' correct schema and query design.
As a side note, the lock you describe here already exists and are called key-locks. They are the default, implicit mode SQL Server operates. Just how in the world do you imagine SQL Server can publish TPC-C benchmark numbers of 16000 tpc transaction per second? You have all the parallelism capacity you need in the server, you just need to read a book or two to understand how to use it. There is plenty of literature on the subject, you can start with Transaction Processing: Concepts and Techniques.
Updated
This will never work, no matter how many/diverse lock hints you try. This is why you have the update with output syntax:
this ensures that you first update, then return what you've updated. This technique is fairly common in databases for exactly the semantics you seek: resource acquisition. In fact this technique is the cornerstone of the resource acquisition poster child: queue processing. See Queues paragraph in OUTPUT Clause. In queues you have a table of resources to be processed, and each thread grabs one, locks it and start processing:
Now from separate sessions, run this:
You'll see each session grabs it's own resource, locks it and skipps everything locked by everybody else. It so happens I have in production a system that runs exactly like this, with over 5M resources int he table (they are web service payment processing requests), and dequeueing and processing around 50 per second, from 100 concurent processors (takes about 2sec. per call to process). On a piece of junk hardware. So it absolutely is possible.
也许您根本不想指定任何锁定?
http: //blogs.msdn.com/davidlean/archive/2009/04/06/sql-server-nolock-hint-other-poor-ideas.aspx
Perhaps you don't want to specify any locking at all?
http://blogs.msdn.com/davidlean/archive/2009/04/06/sql-server-nolock-hint-other-poor-ideas.aspx