我的任务是维护一个处理具有“日志”的“批处理”的应用程序。这些数据使用 InnoDB 表存储在 MySQL 中,并使用 log.batch_id
外键将每个批处理与其日志链接起来。
我最近必须优化某些操作的性能,其中之一涉及获取批处理列表以及每个批处理的日志计数。此操作过去被实现为按 batch.id
分组的 LEFT JOIN
,但性能 Not Acceptable ,因此我转换为保留 cached_log_count< 的非规范化状态
每批的值。
由于应用程序如何处理其业务,此缓存计数仅在创建后不久为每个批处理更新一次。伪代码如下:
# there are no logs at all for batch id = 42 at this point
$batch = SELECT * FROM batch WHERE id = 42
BEGIN
FOR (EACH LOG)
INSERT INTO log (batch.id, ...) VALUES 42, ...
# error checking elided
END FOR
$logCount = SELECT COUNT(*) FROM log WHERE batch.id = 42
UPDATE batch SET cached_log_count = $logCount WHERE id = 42
COMMIT
我希望上面的内容能够填充 log
表并正确设置关联批处理的 cached_log_count
,大多数时候确实如此会发生什么。然而,我经常会得到一个 cached_log_count
等于 0 的批处理(即创建时的初始值),同时日志在数据库中显示得很好。
发生什么事了?我不明白上面的内容怎么可能不总是正确更新日志计数。我考虑过进行一些重构,以便将 SELECT COUNT(*)
/UPDATE batch
对转换为单个 UPDATE ... SELECT
,但这看起来没有帮助。
可能相关的其他信息:
- 有效的事务隔离级别是 REPEATABLE READ。
- 该问题似乎仅在具有数千个日志的批处理中出现,但即使如此,也很少发生。
最佳答案
如果您使用的是 MySQL 5.5 或 5.6 那么出现这种情况可能是由于以下原因:
- 在某些情况下,优化器可能会优化掉用户定义的变量,从而阻止它们执行您想要的操作。
- 分配的顺序,甚至分配的时间,都可能是不确定的,并且取决于优化器选择的查询计划。结果可能会非常令人困惑,稍后您将会看到。
- := 赋值运算符的优先级低于任何其他运算符,因此必须小心地显式添加括号。
关于mysql - 为什么 InnoDB 无法正确读取或写入计数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22911808/