【问题标题】:MySQL: merge several large tables and add values, optimizationMySQL:合并几个大表并添加值,优化
【发布时间】:2015-08-26 18:42:50
【问题描述】:

我有几个大表,其字段如下:

+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
| fid1  | varchar(10) | NO   | MUL | NULL    |       |
| fid2  | varchar(10) | NO   |     | NULL    |       |
| cnt   | int(11)     | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

我在{fid1,fid2,cnt} 上有索引,并且每个表都有超过20000000 行。

我想将这些表合并为一个,如果fid1fid2 都匹配,那么新的cnt 值将是这些表中cnt 的总和。

我尝试了merge and add values from two tables中建议的方式,得到了类似的东西

SELECT COALESCE(A.fid1, B.fid1) AS fid1, COALESCE(A.fid2, B.fid2) AS fid2, (COALESCE(A.cnt,0)+COALESCE(B.cnt,0))
FROM test1 A LEFT JOIN test2 B ON a.fid1 = b.fid1 AND A.fid2 = B.fid2
UNION
SELECT COALESCE(A.fid1, B.fid1) AS fid1, COALESCE(A.fid2, B.fid2) AS fid2, (COALESCE(A.cnt,0)+COALESCE(B.cnt,0))
FROM test1 A RIGHT JOIN test2 B ON a.fid1 = b.fid1 AND A.fid2 = B.fid2

但是,由于我有几张表而且它们都非常大,所以UNION 的这种方法非常耗时。有没有其他方法可以有效地实现它或优化它?

谢谢!

【问题讨论】:

    标签: mysql optimization merge union


    【解决方案1】:

    可以通过非常有效和简单的方式实现。

    第 1 步:

    Alter table test1 
      add constraint fid1_fid2_unique UNIQUE (fid1,fid2);
    

    第 2 步:

    insert into test1 
       select  test2.fid1,test2.fid2,test2.cnt  from test2
     on duplicate key update test1.cnt=test1.cnt+test2.cnt ;
    

    同样可以处理多个表。

    您可以在以下链接验证结果: http://sqlfiddle.com/#!9/07c6b/1

    【讨论】:

    • 谢谢!那么这样一来,就不需要其他索引了吧?
    • 我相信这比联合方式慢,因为 1:每次插入时它都会使索引保持最新 2:不止一次更新结果行,并且取决于数据冗余,这可能意味着性能降低 7-20-200 倍。另请注意,从 mysql 文档开始,特别不建议使用唯一索引:dev.mysql.com/doc/refman/5.6/en/… 最初的问题是关于速度,而不是最简单或更漂亮的问题。 :)
    • 另请注意,这样会丢失 test1 中的原始数据。
    • 如果原来的 test1 表有重复,这个也会失败。很抱歉吹毛求疵,我从昨天开始打开这个窗口。 :)
    • @PetrosHu :假设 test1 没有重复或没有数据。您的解决方案UNION 删除了重复记录(内部查询)。事实上SUM 函数在您的查询中给出了错误的答案。您应该使用UNION ALL 更新它。很抱歉吹毛求疵:)
    【解决方案2】:

    无法在多个表上加速 UNION,因为 UNION 只是一个串联,在您的用例中,您必须从所有表中读取所有行。因此,如果您有 20 个表,并分别从每个表中进行选择,那不会比一个联合更快(给定相同的过滤器)

    如果你想合并几百万行,它所花费的时间取决于 select-s 和写入,而且写入总是比较慢,所以:

    1. 创建表 new_table.... 并且不要添加键,那些只会减慢写入速度
    2. 不要使用任何多次触及一条记录的技巧,最好在内存中求和,一次写入
    3. 使用 INSERT INTO new_table SELECT ... 因为它是写入本地选择的大量数据的最快方法(它将所有内容保存在服务器内存中,不涉及网络:))
    4. 不要使用 LEFT 和 RIGHT 连接,使用外连接
    5. 使用一些脚本或代码将连接生成为一个长查询:

      INSERT INTO new_table
      SELECT fid1,fid2, sum(cnt) as cnt FROM
      (
      SELECT fid1, fid2, cnt FROM table1
      UNION ALL
      SELECT fid1, fid2, cnt FROM table2
      UNION ALL
      SELECT fid1, fid2, cnt FROM table3
      UNION ALL
      SELECT fid1, fid2, cnt FROM table4
      UNION ALL
      SELECT fid1, fid2, cnt FROM table5
      UNION ALL
      SELECT fid1, fid2, cnt FROM table6
      ) GROUP BY fid1, fid2;
      

      如果你的缓冲区用完了,你可能会考虑不要一次做所有事情,而是将 table1,2,3,4,5,6,7,8,9,10 总结为 sum1, table 11,12,13 ,14,15,16,17,18,19,20 到 sum2 中,最后求和。这意味着更多的工作、更多的分配存储空间和更多的磁盘 io,但峰值缓冲区使用率更低。

      最后:您可以添加(或启用)您需要的密钥。

      提示:

      • 在这种特殊情况下,如果您不过于频繁地更新表格,使用 myisam 可能会获得更快的结果
      • 如果您使用大型数据集,您的数据库服务器内存是性能的关键参数(ssd 是第二个 :)),因此您应该花时间调整缓冲区大小的配置。您可能需要 8 小时的调整时间,但您可能会赢得 40 小时的运行时间,并且每次必须重复此操作时也可能需要 40 小时。
      • 如果您不知道在 mysql.cnf 中设置什么,至少看看 Percona 的配置向导,https://tools.percona.com/,这是一个不错的开始。

    更新:更正了用户 seahawk 指出的 UNION ALL

    【讨论】:

    • 感谢您的详细解释。我不确定您所说的DON'T add keys, those just slow down the writes now 是什么意思。 SUM 和 GROUP BY 操作不需要索引吗?
    • 如果你有一个没有键的表,你可以插入一百万行,几乎和磁盘写入一样快。稍后,当您插入每一行时,您可以使用“alter table add key”。解决此问题的另一种方法是,当您使用键创建空表,然后“alter table new_table DISABLE KEYS”,执行批量插入,然后“alter table new_table ENABLE KEYS”,让它重建索引。这个概念是建立一次索引比单独跟上 2 亿次插入要快。此外,虽然在插入端选择时索引很好,但它们对您没有帮助......
    • 我还要指出,如果您多年来每天都在进行此类处理,那么您可能应该研究一些分布式处理系统,例如 hadoop,因为它们更适合“使用模式不那么通用的庞大数据集”。 (所以你不能在一分钟内写出任何类型的选择或加入,但是如果你有大量的数据和 20 台服务器,并且你需要它们可靠地运行,那么你并不真的需要 RDBMS 功能,这就是要走的路。)
    • 我明白了,确实用键将行插入表中要慢得多。再次感谢您提供详细信息!
    • 这个查询给出了错误的结果。请将union 替换为UNION ALL
    【解决方案3】:

    我没有你的场景的很多细节 - 但我假设这是非生产数据库,数据库负载无关紧要(因为无论如何你都在做大量的联合)。还假设您想要一个 MySQl 解决方案。

    试试这个:

    将所有数据插入到 1 个大表中。 在上面添加一个复合索引(fld1,fld2)。 然后按查询运行组 SELECT SUM(cnt), fld1, fld2 FROM table_name GROUP BY fld1,fld2;

    联盟需要多长时间?那将是最坏的情况。并告诉我们您的最终解决方案需要多长时间。

    祝你好运!

    【讨论】:

    • 将所有数据插入一个巨大的表中,然后添加一个索引,然后再按该索引进行分组是次优的。这将读取所有记录,写入所有记录,然后索引构建将读取所有记录,写入几乎相同的数量,然后选择组将再次读取,然后您仍然必须存储结果。这是整个数据集的 3 次完整读取~3 次完整写入。另一方面,一个大联合将读取所有行一次,处理它们并将其中一些写出一次。 (并在途中耗尽所有内存和缓冲区,但这就是服务器的用途)。
    猜你喜欢
    • 1970-01-01
    • 2015-11-08
    • 2022-07-12
    • 1970-01-01
    • 2010-10-16
    • 2021-11-10
    • 2011-02-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多