wha*_*hat 2 sql-server concurrency sql-server-2014
背景: SQL Server Version 2014,设计的是发票订单系统。
我有几个问题来确认我对 SQL Server 并发性的看法。作为简要背景,创建此模型的开发人员构建了每个订单库存表(属于订单的一部分的项目)和带有InUse
字段的订单表(订单)。会发生的情况是,当销售人员尝试更新订单时,通过更改订购商品的名称(例如需要将一本名为《哈利波特》的书重命名为《哈利波特》),如果其他人正在阅读该数据,他们就不能访问对象。
实际的数据库设计是这样的:该SalesOrder
表将具有SalesPersonId
与InUse
列设置为1,没有人可以访问OrderId
。这意味着对于每次读取、更新、插入或删除,都会发生三个事务:首先将更新设置InUse
为 1,以便没有人可以访问特定的OrderId
,然后是实际事务,然后是将最终更新设置InUse
为 0 以允许人们访问那个OrderId
。这意味着多次读取和写入而不是更少 - 随着规模的扩大,性能噩梦!
开发人员解释说,如果两个用户尝试写入或更新订单中的项目,这可以解决并发问题。嗯?
根据我对 SQL Server 的了解,如果两个用户尝试写入相同的顺序——比如添加两个新项目,它们都会被添加,因为这些将作为元数据插入日志中,然后添加到磁盘上。另外,如果插入物完全相同,我们应该设计具有唯一约束的表来防止这种情况(公平地说,该开发人员根本不使用约束和外键),因此不会发生第二次插入。这意味着两个用户可以将插入写入同一个表,并且插入通常会很快发生。
此外,如果两个用户正在更新同一个记录——比如说一个OrderItemId
名字为“Harry Potter”的 1 来更新为“Harry Potter”(这是一个特定的项目OrderId
),即使是两个不同的更新也会发生,但是第二个无关紧要,因为它是相同的更新。 只有当对同一对象发生两次不同的更新时,例如更新一是“哈利波特”,更新二是“哈利波特”,才会出现问题,因为它们不同。但是,不允许用户访问对象并不能解决这个问题(他的设计),因为用户一个的更新可能是错误的更新,从而锁定了用户 2(反之亦然)。
我不明白这个设计;对我来说,它反映了对SQL Server并发的完全误解,以及SQL数据库如何处理事务,而且他创建的大部分内容已经由SQL Server在后端处理(除了没有解决错误更新的问题) . 但是,也许我遗漏了一些东西,其他人可以启发我这是 SQL Server 的标准做法(我以前从未见过这种设计)。
这是在不影响并发的情况下实现悲观锁定的一种完全可以接受的方式。
您需要在逻辑级别确保,每当一个会话编辑一条记录(可以在物理级别转换为多个表中的多行)时,没有其他会话可以修改该逻辑记录的任何组件——这是必要的确保业务数据的一致性。要启用此功能,您需要在所有受影响的表行上放置某种锁定。然而,这在交互式应用程序中存在一个问题,因为人类天生比计算机慢,他们倾向于在完成编辑之前看窗外、打电话、抽烟或出去吃午饭,这可能会留下记录锁定了很长时间。
如果你要依靠物理U或X行锁在悲观锁的情况下,你有时可能会阻止阅读(或者甚至读其他并发事务过去)的锁定行,从而影响整体应用性能。从并发的角度来看,更安全的替代方法是在逻辑上将某些行标记为由某个会话编辑(通过InUse
在您的示例中设置标志),而不对它们放置物理锁。
InUse
交互式应用程序中这些额外更新的成本可以忽略不计,因为正如我们所指出的,其中的人工组件仍然要慢得多。
当然,这只有在访问数据库的所有应用程序都知道并遵守这些逻辑锁时才有效。