考虑以下设计为包含最多 10000 行的内容:
CREATE TABLE T(
id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
weight MEDIUMINT NOT NULL,
PRIMARY KEY(id)
);
每 6 小时我需要根据最新数据更新每行的权重。我通过预先比较表和最新数据之间保持相同的值来缩小这个数字,但最坏的情况仍然是 10000 行需要更新。新鲜数据仅由地图 id->value 组成。所以这里我不是在谈论
UPDATE T SET weight = {value};
,因为我需要将每一行的字段设置为特定值。
最初我认为单个查询应该更快,所以我将所有更新重新组合在一个
INSERT INTO T(id, weight) VALUES ({id_1}, {weight_1}), ..., ({id_n}, {weight_n}) ON DUPLICATE KEY UPDATE weight = VALUES(weight);
语句中,这有点快(尽管我不记得到底有多快),但我很快发现我的即使行已更新且未插入,AUTO_INCRMENT id 也会递增,这在我的情况下是非常错误的,因为选择 id 本身的数据类型 (SMALLINT UNSIGNED) 是因为它是具有最低最大值 (32767) 的数据类型表的最大理论行数 (10000)。我可以使用 BIGINT UNSIGNED 但由于我只更新此表,这似乎浪费空间并且是解决问题的一种非常懒惰的方法。
我刚刚尝试对每个 id->value 对进行
UPDATE T SET weight = {value} WHERE id = {id};
查询,但速度非常慢,因为完成所有查询大约需要 600 毫秒。
我也只是尝试将所有 id->value 对重新组合到一个
UPDATE T SET weight = CASE id WHEN {id_1} THEN {weight_1} ... WHEN {id_n} THEN {weight_n} END WHERE id IN ({id_1}, ..., {id_n});
语句中,但仍然需要大约 600 毫秒才能完成。
您可能会告诉我,每 6 小时 600 毫秒并不算多,我同意这个表,但我会更定期地处理类似的情况,这种高延迟肯定会成为一个问题。
建立一个新表,然后交换表可能是最快的:
CREATE TABLE new LIKE real;
INSERT INTO new
SELECT ... -- including `real.id`
FROM real
JOIN updates ON ...
RENAME TABLE real TO old,
new TO real;
如果传入的数据可能会添加新行(因此,创建新的 id),我建议用两个查询来代替那个 INSERT ——一个用于处理更新的行,另一个用于添加新行(参见
LEFT JOIN
)。
(这个问题太模糊了,我无法说出完整的细节。)