如何select高效地从多个表中获取一致的数据
How to select consistent data from multiple tables efficiently
我正在使用 MySQL 5.6。假设我们有以下两个表:
每个DataSet
都有大量子DataEntry
记录,数量会达到10000或100000或更多。 DataSet.md5sum
和 DataSet.version
在一个事务中插入或删除其子 DataEntry
记录时得到更新。 A DataSet.md5sum
是针对其所有子 DataEntry.content
s.
计算的
在这种情况下,从这两个表中获取一致数据的最有效方法是什么?
如果我发出以下两个不同的 SELECT,我想我可能会由于并发的 INSERT / UPDATE 而得到不一致的数据:
SELECT md5sum, version FROM DataSet WHERE dataset_id = 1000
SELECT dataentry_id, content FROM DataEntry WHERE dataset_id = 1000
-- 我认为这个查询的结果可能与之前查询 获取的 md5sum 不一致
我想我可以通过如下查询获得一致的数据:
SELECT e.dataentry_id, e.content, s.md5sum, s.version
FROM DataSet s
INNER JOIN DataEntry e ON (s.dataset_id = e.dataset_id)
WHERE s.dataset_id = 1000
但它会产生冗余数据集,其中包含 10000 或 100000 个重复的 md5sum
s,所以我猜它效率不高(编辑:我担心的是高网络带宽和内存消耗)。
我认为使用悲观读/写锁 (SELECT ... LOCK IN SHARE MODE
/ FOR UPDATE
) 是另一种选择,但它似乎有点矫枉过正。还有其他更好的方法吗?
连接将确保返回的数据不受两个单独 select 之间发生的任何更新的影响,因为它们是作为单个查询执行的。
当你说 md5sum 和版本更新时,你的意思是 child table 有一个用于插入和更新的触发器吗?
当你加入 table 时,你会得到一个 "duplicate md5sum and version",因为你正在为 DataEntry
table 中的每个项目拉取匹配记录。这非常好,不会成为效率问题。另一种方法是使用两个单独的 selects,但是根据 inserts/updates 的频率,如果没有交易,您 运行 获取数据的风险可能会略有偏差.
我会加入。您可以 运行 从 mysql 中解释您的查询计划并查看查询的执行方式,并根据您的数据以及是否有任何索引等查看两种方法之间的任何差异...
也许将这些记录组 运行 分阶段 table 会更有益。在处理之前,您可以调用一个 pre-processor 函数,该函数获取 "snapshot" 将要处理的数据,将副本放入暂存 table。然后你可以 select 只是版本和 md5sum,然后是所有记录,作为两个不同的 selects。由于这些被复制到一个单独的阶段 table,您不必担心立即更新会破坏您的处理会话。您可以设置定时作业来执行此操作或将其作为 on-demand 调用。不过,鉴于您正在使用的 hardware/network 设置,这将是您需要研究最佳方法的东西。以及您可以使用的任何工作安排软件。
使用此模式:
START TRANSACTION;
SELECT ... FOR UPDATE; -- this locks the row
...
UPDATE ...
COMMIT;
(并在每个语句后检查错误,包括 COMMIT
。)
“100000”不是 "huge",但 "BIGINT" 是。改为评论 INT UNSIGNED
。
对于 MD5,确保您没有使用 utf8:CHAR(32) CHARACTER SET ascii
。这适用于任何其他十六进制字符串。
或者,使用 BINARY(16)
作为 space 的一半。然后插入的时候用UNHEX(md5...)
,取的时候用HEX(...)
。
您担心带宽等问题。请描述您的客户(PHP?Java?...)。请解释需要获取多少(100K 行?)以重新执行 MD5。
注意MySQL中有一个MD5函数。如果您的每个项目都有一个 MD5,您可以获取这些项目的 MD5 连接——并完全在服务器中完成;不需要带宽。 (一定要加group_concat_max_len
)
我正在使用 MySQL 5.6。假设我们有以下两个表:
每个DataSet
都有大量子DataEntry
记录,数量会达到10000或100000或更多。 DataSet.md5sum
和 DataSet.version
在一个事务中插入或删除其子 DataEntry
记录时得到更新。 A DataSet.md5sum
是针对其所有子 DataEntry.content
s.
在这种情况下,从这两个表中获取一致数据的最有效方法是什么?
如果我发出以下两个不同的 SELECT,我想我可能会由于并发的 INSERT / UPDATE 而得到不一致的数据:
SELECT md5sum, version FROM DataSet WHERE dataset_id = 1000
SELECT dataentry_id, content FROM DataEntry WHERE dataset_id = 1000
-- 我认为这个查询的结果可能与之前查询 获取的 md5sum 不一致
我想我可以通过如下查询获得一致的数据:
SELECT e.dataentry_id, e.content, s.md5sum, s.version
FROM DataSet s
INNER JOIN DataEntry e ON (s.dataset_id = e.dataset_id)
WHERE s.dataset_id = 1000
但它会产生冗余数据集,其中包含 10000 或 100000 个重复的 md5sum
s,所以我猜它效率不高(编辑:我担心的是高网络带宽和内存消耗)。
我认为使用悲观读/写锁 (SELECT ... LOCK IN SHARE MODE
/ FOR UPDATE
) 是另一种选择,但它似乎有点矫枉过正。还有其他更好的方法吗?
连接将确保返回的数据不受两个单独 select 之间发生的任何更新的影响,因为它们是作为单个查询执行的。
当你说 md5sum 和版本更新时,你的意思是 child table 有一个用于插入和更新的触发器吗?
当你加入 table 时,你会得到一个 "duplicate md5sum and version",因为你正在为 DataEntry
table 中的每个项目拉取匹配记录。这非常好,不会成为效率问题。另一种方法是使用两个单独的 selects,但是根据 inserts/updates 的频率,如果没有交易,您 运行 获取数据的风险可能会略有偏差.
我会加入。您可以 运行 从 mysql 中解释您的查询计划并查看查询的执行方式,并根据您的数据以及是否有任何索引等查看两种方法之间的任何差异...
也许将这些记录组 运行 分阶段 table 会更有益。在处理之前,您可以调用一个 pre-processor 函数,该函数获取 "snapshot" 将要处理的数据,将副本放入暂存 table。然后你可以 select 只是版本和 md5sum,然后是所有记录,作为两个不同的 selects。由于这些被复制到一个单独的阶段 table,您不必担心立即更新会破坏您的处理会话。您可以设置定时作业来执行此操作或将其作为 on-demand 调用。不过,鉴于您正在使用的 hardware/network 设置,这将是您需要研究最佳方法的东西。以及您可以使用的任何工作安排软件。
使用此模式:
START TRANSACTION;
SELECT ... FOR UPDATE; -- this locks the row
...
UPDATE ...
COMMIT;
(并在每个语句后检查错误,包括 COMMIT
。)
“100000”不是 "huge",但 "BIGINT" 是。改为评论 INT UNSIGNED
。
对于 MD5,确保您没有使用 utf8:CHAR(32) CHARACTER SET ascii
。这适用于任何其他十六进制字符串。
或者,使用 BINARY(16)
作为 space 的一半。然后插入的时候用UNHEX(md5...)
,取的时候用HEX(...)
。
您担心带宽等问题。请描述您的客户(PHP?Java?...)。请解释需要获取多少(100K 行?)以重新执行 MD5。
注意MySQL中有一个MD5函数。如果您的每个项目都有一个 MD5,您可以获取这些项目的 MD5 连接——并完全在服务器中完成;不需要带宽。 (一定要加group_concat_max_len
)