有两种常用的锁定方法。
首先,你有悲观锁定。在这种方法中,您锁定了阻止其他人更改该行的行(SELECT ... FOR UPDATE
)。然后你做UPDATE
。当您提交更改时,锁定被释放。在这种情况下,不需要有版本号/时间戳列(至少不支持锁定),代码相对容易。
悲观锁定的不利之处在于,您需要锁定用户在可能编辑数据的页面上的整个时间。由于HTTP是无状态协议,因此如果您要构建基于Web的应用程序,这在技术上确实很难。最初呈现页面的请求通常会从连接池中获取连接,请执行SELECT
,然后在页面完成后将连接返回到池。后续更新数据的请求通常会发生在与另一个数据库会话不同的连接上,因此您无法在第一个会话中锁定该行并在第二个会话中进行更新。如果您想悲观地锁定该行,则需要在后端执行大量工作,以确保一个数据库连接与特定中间层会话绑定,直到用户完成编辑数据为止。这通常会对可伸缩性产生很大的负面影响,并且会引入各种会话管理问题 - 例如,您如何知道我是否请求了一个页面,锁定了一行,然后在没有注销或更改的情况下关闭了浏览器?你将离开锁定在数据库中的记录多久?如果其他会话尝试锁定该行,会发生什么情况?如果第一个人出去吃午饭,你会让这个会议等待锁定多久?通常,人们不会在基于Web的应用程序中实施悲观锁定,因为管理会话和会话状态太不切实际了。
第二个选项是乐观锁定。在这种方法中,您可以向该行添加版本号/时间戳。您在查询数据时选择此版本号/时间戳。然后,当您稍后进行更新并检查有多少行被实际修改时,您可以在WHERE
子句中使用它。如果您只修改一行,您知道自从您阅读该行后该行没有更改。如果您修改0行,您知道该行确实发生了变化,您可以处理该错误。
因此,举例来说,你会选择数据与版本号一起
SELECT address_line1, city, state, zip, version
FROM addressTable
WHERE address_id = `<<some key>>`
当你准备做更新,你会在那里你在使用version
做这样的事你UPDATE
如果该行改变
UPDATE addressTable
SET address_line1 = `<<new address line 1>>`,
city = `<<new city>>`,
state = `<<new state>>`,
zip = `<<new zip>>`,
version = version + 1
WHERE address_id = `<<some key>>`
AND version = `<<version you read initially>>`
IF(SQL%ROWCOUNT = 0)
THEN
-- Darn. The row must have changed since you read it. Do something to
-- alert the user. Most likely, the application will need to re-query the
-- data to see what the address has been changed to and then ask the user
-- whether they want to re-apply the changes.
RAISE_APPLICATION_ERROR(-20001, 'Oops, the row has changed since you read it.');
END;
您的应用程序,然后会做一些与错误有用抛出一个错误。通常,这意味着再次查询数据,向用户展示更改,并询问他们是否仍然想要应用其更改。例如,如果我读取地址并开始编辑它,请去午餐,我的同事登录,读取相同的地址,进行一些编辑并保存,然后返回并尝试保存我的更改,这通常是有意义的告诉我,我的同事已经将地址更改为新的东西 - 我想继续进行编辑还是要放弃它们?
SELECT FOR UPDATE,但我不认为这是一个乐观的锁定... – Vadim
真的吗?你能详细说明选择更新吗?我认为这锁定了行? – wonderBoy322
@ wonderBoy322它锁定了行,但我会考虑悲观锁定。这是“悲观的”,因为当你确信别人会尝试修改相同的行时使用它。相对于“乐观”,并假设没有其他人会修改同一行,所以你做了所有的工作,然后在最后检查某种标志以确保其他人没有改变任何东西。 –