2

我有一个应用程序不幸地使用带有 MyISAM 表的遗留 mysql_* 函数(bleagh ...),所以我不能使用事务。我有获取当前余额的代码,检查此余额是否正常,如果是,它将减去一个数量并保存新余额。

问题是,我最近看到一个实例,其中两个查询获取相同的起始余额,减去一个数量,然后记录一个新的余额。由于他们都获得了相同的起始余额,因此两次更新后的期末余额都是错误的。

100 - 10 = 90
100 - 15 = 85

什么时候应该...

100 - 10 = 90
90 - 15 = 75

这些请求相隔几分钟执行,所以我不认为差异是由于竞争条件造成的。我最初的想法是 MySQL 缓存正在存储相同的初始查询的结果,以获得平衡。但是,我读到如果修改了任何相关表,则会删除这种类型的缓存。

我很可能会通过将所有内容放入一个查询来解决,但我仍然想弄清楚这一点。它让我感到迷惑。如果在修改表时删除了缓存,那么发生的事情不应该发生。有没有人听说过这样的事情,或者对为什么会发生有任何想法?

4

3 回答 3

2

它不太可能是查询缓存——如果底层数据集已被另一个查询修改,MySQL 足够聪明,可以使缓存条目无效。如果查询缓存在过期后仍然保留旧的陈旧值,那么 MySQL 将完全没用。

您是否可能有未完成的未提交事务导致此问题?如果没有对相关记录进行适当的锁定,您的第二个查询可能很容易抓取过时的数据。

于 2012-05-01T21:31:07.700 回答
2

您的应用程序很可能有过时的数据。这很好,这取决于有多少数据库应用程序在工作,但是当您执行更新时,而不是执行以下操作:

UPDATE account
SET balance = :current_balance - 10
WHERE account_id = 1

你需要做更多这样的事情:

UPDATE account
SET balance = balance - 10
WHERE account_id = 1

这样,即使有人同时更改了它,您也可以使用数据库中的当前余额,而不是依赖陈旧的应用程序数据。

如果您只想在没有其他人修改它的情况下更改该值,那么您可以执行以下操作:

UPDATE account
SET balance = balance - 10
WHERE account_id = 1
  AND balance = :current_balance

如果受影响的行数为 1,那么您成功了,该记录没有被其他人更改。但是,如果受影响的行数为 0,则其他人更改了记录。然后,您可以从那里决定要做什么。

于 2012-05-01T22:16:58.470 回答
1

我认为锁定表格是解决您的问题的方法:)

于 2012-05-01T21:33:03.503 回答