【问题标题】:MySQL procedure while loop: Gets stuck after one iteration - Cache Clean upMySQL过程while循环:一次迭代后卡住 - 缓存清理
【发布时间】:2016-01-01 21:43:16
【问题描述】:

我正在查询一些非常大的表 (TargetTable),并且有一个特定的过程在第二次迭代中卡住了,永远不会完成也不会崩溃。无论范围的开始 (loopIndex) 或范围的大小 (loopStepShort) 如何,第一次迭代总是在几分钟内完成。

我期待听到您的想法和建议。

[更新 1] 如果我执行以下操作之一,此问题就会消失:

  • 移除内连接的嵌套部分;
  • 为内部连接的嵌套部分使用内存中的临时表(感谢@SashaPachev);
  • 在 while 循环之外运行每个循环迭代;
  • 使用较小的TargetTable

[Update 2] 已解决! 我认为问题可能是在数据库转换过程中没有复制某些数据库索引时出现的。因为,当我试图重现一个示例时,它发生在非索引表(高 CPU 使用率和几乎无限循环步骤)MariaDB Server, JIRA


MySQL InnoDB 引擎(10.0.21-MariaDB Server, Linux x86_64, Fedora v.21)的自定义配置如下:

innodb_buffer_pool_size = 2G
net_write_timeout = 1800
net_read_timeout = 1800
join_buffer_size = 2G
innodb_flush_log_at_trx_commit = 2
innodb_log_buffer_size = 4M
max_allowed_packet = 4G
key_buffer = 2G
sort_buffer_size = 512K

程序主体如下:

SET loopIndex = 0;
SET loopMax = 20000000;
SET loopStepShort = 10000;
WHILE loopIndex < loopMax do    
    UPDATE TargetTable AS t0,
        (SELECT __index, sessionStartAge
        FROM SubjectTable AS t0
        INNER JOIN (SELECT t0.id, t0.admission, 
                    if(t0.startage is null and t0.endage is null, 21, 
                    if(least(t0.startage, t0.endage) <= 1, 1, 
                    if(least(t0.startage, t0.endage) <= 4, 2, 
                    if(least(t0.startage, t0.endage) <= 9, 3, 
                    if(least(t0.startage, t0.endage) <= 14, 4, 
                    if(least(t0.startage, t0.endage) <= 19, 5, 
                    if(least(t0.startage, t0.endage) <= 24, 6, 
                    if(least(t0.startage, t0.endage) <= 29, 7, 
                    if(least(t0.startage, t0.endage) <= 34, 8, 
                    if(least(t0.startage, t0.endage) <= 39, 9, 
                    if(least(t0.startage, t0.endage) <= 44, 10, 
                    if(least(t0.startage, t0.endage) <= 49, 11, 
                    if(least(t0.startage, t0.endage) <= 54, 12, 
                    if(least(t0.startage, t0.endage) <= 59, 13, 
                    if(least(t0.startage, t0.endage) <= 64, 14, 
                    if(least(t0.startage, t0.endage) <= 69, 15, 
                    if(least(t0.startage, t0.endage) <= 74, 16, 
                    if(least(t0.startage, t0.endage) <= 79, 17, 
                    if(least(t0.startage, t0.endage) <= 84, 18, 
                    if(least(t0.startage, t0.endage) <= 89, 19, 
                    if(least(t0.startage, t0.endage) <= 120, 20, 21))))))))))))))))))))) AS sessionStartAge
            FROM SubjectTable AS t0
            INNER JOIN ids AS t1 ON t0.id = t1.id 
                AND t1.id >= loopIndex 
                AND t1.id < (loopIndex + loopStepShort)
            GROUP BY t0.id, t0.admission) AS t1 
        ON t0.id = t1.id AND t0.admission = t1.admission) AS t1
    SET t0.sessionStartAge = t1.sessionStartAge
    WHERE t0.__index = t1.__index;

    SET loopIndex = loopIndex + loopStepShort;
END WHILE;

最后,以下是表格的大致尺寸:

  • 表格:ID:
    • 表行:~1,500,000 条记录,
    • 数据长度:~250 MB,
    • 索引长度:~140 MB,
    • 表格大小:~400 MB
  • 表:目标表:
    • 表行:~6,500,000 条记录,
    • 数据长度:~4 GB,
    • 索引长度:~350 MB,
    • 表格大小:~4.35 MB
  • 表:主题表:
    • 表行:~6,500,000 条记录,
    • 数据长度:~550 MB,
    • 索引长度:不适用,
    • 表格大小:~550 MB

非常感谢。


我想我必须向 Oracle/MariaDB 提交错误报告,并更新帖子。

【问题讨论】:

  • 这样循环更新似乎效率不高。你能解释一下你想用上面的代码实现什么,以防我们可以提出另一个解决方案。
  • 再一次,您何时提交更改?
  • 这个过程的目的,为了清楚起见,这里被缩短了,是为了找到客户端 (id) 的最小年龄 (sessionStartAge)对于每个会话。每个客户都有多次访问,可以分为会话,每个会话可以包括一个或多个具有相似准入日期(admission)的访问。另外,请注意,该过程在循环之外可以正常工作,这是关于 while 循环的东西,它把它搞砸了。谢谢
  • 我已将离散化(if 语句)添加到此处的查询中,这是我最初查询的一部分。我相信这是经过几次运行后的主要影响因素。
  • 另外,关于提交:"默认情况下,MySQL 会在启用自动提交模式的情况下运行。这意味着,一旦您执行更新(修改)表的语句,MySQL 就会存储更新在磁盘上使其永久化。更改无法回滚。" source

标签: mysql caching join while-loop


【解决方案1】:

试试这个(免责声明 - 未经测试,可能包含语法错误或错误):

DROP TABLE IF EXISTS t1;
CREATE TEMPORARY TABLE t1 (key(id)) ENGINE=MEMORY SELECT t0.id, t0.admission, 
                    if(t0.startage is null and t0.endage is null, 21, 
                    if(least(t0.startage, t0.endage) <= 1, 1, 
                    if(least(t0.startage, t0.endage) <= 4, 2, 
                    if(least(t0.startage, t0.endage) <= 9, 3, 
                    if(least(t0.startage, t0.endage) <= 14, 4, 
                    if(least(t0.startage, t0.endage) <= 19, 5, 
                    if(least(t0.startage, t0.endage) <= 24, 6, 
                    if(least(t0.startage, t0.endage) <= 29, 7, 
                    if(least(t0.startage, t0.endage) <= 34, 8, 
                    if(least(t0.startage, t0.endage) <= 39, 9, 
                    if(least(t0.startage, t0.endage) <= 44, 10, 
                    if(least(t0.startage, t0.endage) <= 49, 11, 
                    if(least(t0.startage, t0.endage) <= 54, 12, 
                    if(least(t0.startage, t0.endage) <= 59, 13, 
                    if(least(t0.startage, t0.endage) <= 64, 14, 
                    if(least(t0.startage, t0.endage) <= 69, 15, 
                    if(least(t0.startage, t0.endage) <= 74, 16, 
                    if(least(t0.startage, t0.endage) <= 79, 17, 
                    if(least(t0.startage, t0.endage) <= 84, 18, 
                    if(least(t0.startage, t0.endage) <= 89, 19, 
                    if(least(t0.startage, t0.endage) <= 120, 20, 21)))))))))))))))))))) as sessionStartAge,
FROM SubjectTable AS t0
            INNER JOIN ids AS t1 ON t0.id = t1.id 
                AND t1.id >= loopIndex 
                AND t1.id < (loopIndex + loopStepShort)
            GROUP BY t0.id, t0.admission;
UPDATE TargetTable AS t0,
        (SELECT __index, sessionStartAge
        FROM SubjectTable AS t0
        INNER JOIN t1 ON t0.id = t1.id AND t0.admission = t1.admission) AS t2
    SET t0.sessionStartAge = t1.sessionStartAge
    WHERE t0.__index = t2.__index;

想法是将内部子查询替换为带有键的临时表,以便外部连接可以使用该键。

【讨论】:

  • 嗨,谢谢@Sasha-Pachev。您提供的解决方案有效。我想我必须将它作为错误报告发送给 Oracle/MariaDB。
  • MySQL 中子选择的一个长期问题是,如果创建一个临时表来存储子查询的输出,则不会努力预测哪些键可能对它有用。它可能已在 MySQL/MariaDB 的最新版本中得到解决,但从 5.5 开始,我相信你仍然不走运。我的方法是假设子查询优化器是有限的,并在我怀疑优化器正确处理子查询的能力时使用正确的键手动创建临时表。
猜你喜欢
  • 1970-01-01
  • 2015-03-23
  • 2018-12-15
  • 2016-04-23
  • 2015-10-20
  • 1970-01-01
  • 2012-05-28
  • 2014-10-11
  • 2020-03-01
相关资源
最近更新 更多