【发布时间】: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