我感兴趣的是能够用Totals
表中的计数或总和来增量更新Values
表中的行,而不会阻止对Values
的插入。
这个想法是收集ID大于总检查点的所有值,然后更新总计数和检查点。这将为Totals
创建快照。表格如下:
Table: Totals
Create Table: CREATE TABLE `Totals` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`count` int(11) NOT NULL DEFAULT '0',
`values_checkpoint` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Table: Values
Create Table: CREATE TABLE `Values` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`total_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `total_id_id` (`total_id`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
结合快照和值的查询如下所示:
SELECT t.count + count(v.total_id),max(v.id)
FROM `Totals` t
JOIN `Values` v ON v.total_id = t.id AND v.id > t.values_checkpoint
WHERE t.id = 1;
问题在于更新
Totals
行。将其直接转换为更新将不起作用,因为MySQL不允许在更新中使用聚合方法。
加入子查询将不起作用,因为它无法关联,并且我需要
id > t.values_checkpoint
的WHERE子句。分别设置
t.count
和t.values_checkpoint
无效,因为它可能阻止在Values
中插入REPEATABLE READ
,并且在READ COMMITTED
中不一致我能弄清楚如何完成这项工作的唯一方法是启动一个事务,然后在
SELECT ... FOR UPDATE
总行中获取可以在Join子查询中使用的values_checkpoint。这不是理想的,因为我需要在事务打开时挂锁。是否可以通过单个更新语句和
t.count
上的单个SELECT来更新t.values_checkpoint
和Values
? 最佳答案
而不是从一开始就“停下来”一直走到最后,而要限制它的行数不要超过1000行。这样可以将干扰保持更短的时间。 (如果需要,请使用少于1000的内容。)预先计算(在事务外部)“良好”的停止点可能也是个好方法。
SELECT @stop := LEAST(t.values_checkpoint + 1000, MAX(v.id))
FROM ...
然后继续进行查询;大概IODKU就足够了(并且总是进行更新):
INSERT INTO Totals (id, count, values_checkpoint)
SELECT t.count + count(v.total_id), max(v.id)
FROM `Totals` t
JOIN `Values` v ON v.total_id = t.id
AND v.id > t.values_checkpoint
AND v.id <= @stop
WHERE t.id = 1
ON DUPLICATE KEY UPDATE
count = VALUES(count),
values_checkpoint = VALUES(values_checkpoint);
然后足够频繁地进行更新,以至于您很少落后1000。