我读到Oracle维护行版本来处理并发。我想在非常大的实时数据库上运行更新查询,但此更新作业必须更改该行的最新版本。
这可以通过PL / SQL还是简单的SQL?
编辑如下**
让我清楚一下这个场景,我们在一个非常大的数据库上面临的现实问题。我们的客户是着名的手机服务提供商。
我们的数据库有一个表格,用于管理客户手机帐户中剩余的当前余额记录。在该表的其他列中,一列存储完成的充电量,另一列存储剩余的当前有效余额。
我们有两个独立的PL / SQL脚本。当客户为手机充电并更新其余额时,会自动触发一个脚本。
第二个脚本是关于从客户帐户中扣除某些费用。这是一个批处理作业,因为它适用于所有客户。此脚本计划在一天的特定时间间隔运行。运行此脚本时,它会在内存中加载50,000条记录,更新某些列并执行批量更新回表。
发生的问题是这样的:
一位身份证号为101的客户联系了他当地的商店,让他的手机充电。他付了这笔钱。但是直到他的手机即将充电,第二个脚本的预定时间才开始播放第二个脚本。第二个脚本在内存中加载了50,000个客户的记录。在这个内存记录中,也是该客户的记录之一。
直到第二个脚本的批量更新完成,第一个脚本才成功为客户的帐户充值。
现在发生的事情是实际的表,列:“CurrentAccountBalance”更新为150,但第二个脚本正在运行的内存记录有客户的旧余额,即100。
第二个脚本必须从列中扣除10:“CurrentAccountBalance”。根据实际工作情况,客户的“CurrentAccountBalance”应为140,这个问题使他的余额达到90.
现在该如何处理这个问题。
答案 0 :(得分:4)
我认为如果你UPDATE
,你想要的是无论发生什么。
Oracle确实会保留旧数据一段时间,但只是为了支持一致的读取。也就是说,读取操作只能看到事务开始时的状态 - 即使数据在此期间被覆盖。它被称为多版本并发控制,可以通过事务隔离级别进行控制。
您可以选择`FOR UPDATE
来明确请求最新的一个;为记录添加锁定,以便其他人无法同时更新它(直到您的交易结束)。
但是,如果您需要编写任何内容(例如UPDATE
),Oracle会在最新版本上始终。
答案 1 :(得分:1)
正如@Markus所说,你有竞争条件。如果您在更新表中的行之前将记录加载到内存中并处理它们,而其他内容可能会在此期间尝试更新它们,那么您需要在处理它们时锁定它们。 (我假设你正在做的事情太复杂了,无法进行简单的一步更新)。像这样的东西会起作用:
DECLARE
CURSOR c is SELECT * FROM current_balance_table FOR UPDATE;
BEGIN
FOR r IN c LOOP
/* Do whatever calculations you need */
new_value := r.CurrantAccountBalance - 10;
UPDATE current_balance_table SET CurrentAccountBalance = new_value
WHERE CURRENT OF c;
END LOOP:
END;
现在的问题是所有记录在循环期间都被锁定,因此商店中的客户要么无法更新余额,要么在更新生效之前等待日志 - 尽管它在它会对您存储的更新值起作用吗?因此,您必须将光标分成小块,平衡脚本的性能与尝试更新同一个表的其他任何人的影响。
一种选择是让外部游标选择您没有锁定的所有客户,然后选择一个内部游标,在计算和更新该行时锁定该客户的余额记录。您必须在每个内部循环之后提交以释放该行的锁定。这涉及到更多锁定/解锁和提交之后每行更新减慢了很多事情。但它最大限度地减少了对商店中个人客户的影响,因为一次只锁定一行,并且锁定的时间长度最小化。所以,你需要找到合适的平衡点。