MySQL长查询进度监控

时间:2011-03-28 20:53:17

标签: mysql sql

只是为了提出我的问题,我明白没有直接支持这样的事情。我正在寻找的是任何类型的解决方案或复杂的推导,这将使我获得半个可敬的结果。

我正在使用集群引擎处理一个相当大的MySQL集群(表格> 4亿行)。

是否有人知道某种方法直接检索或以其他方式通过mysql中的长查询获得一些(或更好)准确的进度指示?我有一些查询可能需要45分钟,我需要通过处理确定我们是10%还是90%。

修改

根据评论中的要求,这是一个经过精心设计的一个查询的版本,导致我原来的问题...

SELECT `userId`
FROM    `openEndedResponses` AS `oe`
WHERE
    `oe`.`questionId` = 3 -- zip code
    AND (REPLACE( REPLACE( `oe`.`value`, ' ', '' ), '-', '' ) IN ( '30071', '30106', '30122', '30134', '30135', '30168', '30180', '30185', '30187', '30317', '30004' ));

此查询针对包含约9500万行的单个表运行。运行查询需要8秒,传输数据需要13秒(总共21秒)。考虑到表的大小,以及使用字符串操作函数的事实,我会说它运行起来非常快。然而,对于用户来说,它仍然是21秒出现卡住或闲置。一些进展的迹象将是理想的。

9 个答案:

答案 0 :(得分:24)

我知道这是一个老问题,但我在寻找类似的答案时,试图弄清楚我的更新会对250米行的查询花费多长时间。

如果你跑:

SHOW ENGINE INNODB STATUS \G

然后在TRANSACTIONS下找到有问题的交易,检查这一部分:

---TRANSACTION 34282360, ACTIVE 71195 sec starting index read
mysql tables in use 2, locked 2
1985355 lock struct(s), heap size 203333840, 255691088 row lock(s), undo log entries 21355084

重要的一点是“撤消日志条目”。对于每个更新的行,在我的情况下,它似乎添加了一个撤消日志条目(尝试在几秒钟后再次运行它,看看有多少已添加)。

如果您跳到状态报告的末尾,您会看到:

Number of rows inserted 606188224, updated 251615579, deleted 1667, read 54873415652
0.00 inserts/s, 1595.44 updates/s, 0.00 deletes/s, 3190.88 reads/s

在这里,我们可以看到正在应用的速度更新是每秒1595.44行(尽管如果您正在串联运行其他更新查询,那么您的查询之间的速度可能会分开。)

所以从这一点开始,我知道21m已经更新了(250m-21m)229m的行。

229,000,000 / 1600 = 143,125秒 (143,125/60)/ 60 = 39.76小时

所以看起来我可以在另外几天扭动我的拇指。除非这个答案是错误的,在这种情况下我会在此之前的某个时候更新它!

答案 1 :(得分:7)

我能够通过查询要处理的行数然后将处理分解为循环来估计这样的事情,一次只处理总行的子集。

完全循环涉及,但基本逻辑如下:

SELECT @minID = Min(keyColumn) FROM table WHERE condition
SELECT @maxID = Max(keyColumn) FROM table WHERE condition
SELECT @potentialRows = (@maxID - @minID) / @iterations

WHILE @minID < @maxID
BEGIN
    SET @breakID = @minID + @potentialRows
    SELECT columns FROM table WITH (NOLOCK, ...)
    WHERE condition AND keyColumn BETWEEN @minID AND @breakID

    SET @minID = @breakID + 1
END

注意,如果ID均匀分布,这种方法效果最佳。

答案 2 :(得分:2)

我认为mysql不支持我确信MySQL不支持任何有关正在运行的查询进度的指示。唯一的解决方案是优化/拆分查询。 如Dour High Arch建议的那样,可以按ID拆分选择。以下是来自33百万行表的查询:

mysql> SELECT SQL_NO_CACHE min(id), max(id) FROM `urls`;
+---------+----------+
| min(id) | max(id)  |
+---------+----------+
|    5000 | 35469678 |
+---------+----------+
1 row in set (0.00 sec)

最好使用整数或至少日期字段进行拆分。它应该是主要唯一索引,并且不应允许空值。

答案 3 :(得分:2)

如果这是您正在尝试的复杂查询,则EXPLAIN SQL命令或MySQL查询分析器可能有助于了解发生了什么。如果它只是一个大型查询,您可以尝试使用SELECT INTO创建一个临时表和/或在SELECT查询中使用LIMIT / OFFSET子句。如果在原始表上使用LIMIT / OFFSET,则可能需要将事务级别设置为可序列化IIRC,以便在迭代数据时获得一致的读取。如果先创建一个临时表,那么该表应保持一致。

答案 4 :(得分:2)

目前 - 对于我非常具体的情况 - 似乎没有真正的解决方案。因为我不能将我的查询分成几个较小的查询,并且它首先证明适用于select count(*),然后运行“真正的”查询(已经非常缓慢的查询的执行时间加倍),所以没有一个解决方法看起来可行无论是。也许很快,MySQL将支持这样的东西

答案 5 :(得分:1)

以下是您需要做的改进以下查询的内容:

Array ( [0] => stdClass Object ( [word] => alpha [sentence] => A is the first letter in the word Alpha. ) [1] => stdClass Object ( [word] => beta [sentence] => B is the first letter in the word Beta. ) [2] => stdClass Object ( [word] => Select one [sentence] => ) [3] => stdClass Object ( [word] => Select one [sentence] => ) [4] => stdClass Object ( [word] => Select one [sentence] => ) [5] => stdClass Object ( [word] => Select one [sentence] => ) )

count($items) >> 6



We are entering index 0


alpha


We are leaving index 0



We are entering index 1


beta


We are leaving index 1



We are entering index 2


Select one

YES if ( ($items[2]->word)=="Select one" ) AT 2 INDEX.


We are leaving index 2



We are entering index 3


Select one

YES if ( ($items[3]->word)=="Select one" ) AT 3 INDEX.


We are leaving index 3



AFTER:-
Array ( [0] => stdClass Object ( [word] => alpha [sentence] => A is the first letter in the word Alpha. ) [1] => stdClass Object ( [word] => beta [sentence] => B is the first letter in the word Beta. ) [2] => stdClass Object ( [word] => Select one [sentence] => ) ) 

您需要确保将oe.questionId编入索引; 当oe.questionId为3时,您需要确保oe.value在整个表中没有任何空格;假设4或5可以说,城市名称,你仍然想要允许空格。

通过这样做,您将能够删除所有REPLACE,这将让MySQL使用oe.value中的索引。

然后,MySQL将合并两个索引,并在处理方面更快地为您提供结果。

如果您有许多重复的userId;你想把它们分组;以这种方式,立即丢弃索引中的条目。您仍然需要扫描整个合并索引;但结果集的大小将花费更少的时间进行传输;不到13秒!

试一试并向我们发布结果

最佳!

答案 6 :(得分:1)

对于这个古老的问题,我发现here由Baron Schwartz撰写,很有希望。这不是一个精确而完整的解决方案,但确实为估算提供了一些客观的材料,如果您只运行该查询,而服务器上没有其他内容

在查询已运行时运行命令

mysqladmin extended -r -i 10 | grep Handler
  • 10是命令重复执行的秒数,因此请等待刷新
  • 如果需要进行身份验证,请添加类似-u root -p的内容
  • 如果您知道要寻找哪个处理程序,则可以使grep更具针对性,例如Handler_read_rnd_next似乎很适合SELECT's
  • 忽略第一个输出,使用第二个及以下
  • 使用Ctrl-C退出

现在获取该数字,然后进行数学计算。确定每秒处理的行,并了解表的大小,也许可以得出相当准确的总时间估计。

免费的额外提示:该命令似乎并没有进入Bash历史记录(可能是由于Ctrl-C的退出,您可以使用history -s mysqladmin extended -r -i 10 -u root -p | grep Handler手动将其添加在那里)。

答案 7 :(得分:0)

如何查看对mysql表的分区,以便分散读/写负载。试着将每个分区限制为5000万行(显然取决于你的硬件)

答案 8 :(得分:0)

如果查询涉及对大表的线性扫描,则通常可以通过对包含该表的文件运行pmonitor来获得出色的估计。包括--update选项,因为 MySQL 以更新模式打开表文件。

示例:

$ sudo pmonitor --update --file=/home/mysql/ghtorrent/commits.MYD --interval=5

/home/mysql/ghtorrent/commits.MYD 31.66%
/home/mysql/ghtorrent/commits.MYD 33.16% ETA 0:03:42
/home/mysql/ghtorrent/commits.MYD 34.85% ETA 0:03:24
/home/mysql/ghtorrent/commits.MYD 36.43% ETA 0:03:32
/home/mysql/ghtorrent/commits.MYD 38.36% ETA 0:03:12
/home/mysql/ghtorrent/commits.MYD 40.21% ETA 0:03:01
/home/mysql/ghtorrent/commits.MYD 41.95% ETA 0:02:54
[...]
/home/mysql/ghtorrent/commits.MYD 92.01% ETA 0:00:24
/home/mysql/ghtorrent/commits.MYD 93.85% ETA 0:00:18
/home/mysql/ghtorrent/commits.MYD 95.76% ETA 0:00:12
/home/mysql/ghtorrent/commits.MYD 97.60% ETA 0:00:07
/home/mysql/ghtorrent/commits.MYD 98.83% ETA 0:00:03
/home/mysql/ghtorrent/commits.MYD 100% ETA 0:00:00

如果您不知道要监视的文件,请使用--diff选项运行 pmonitor 。这将向您显示进度所在的文件。

示例

$ sudo pmonitor --update -diff --command=mysqld -i 60
[...]
/home/mysql/ghtorrent/projects.MYD      22.41% ETA 2:01:41
/home/mysql/ghtorrent/projects.MYD      23.13% ETA 1:53:23
/home/mysql/ghtorrent/projects.MYD      23.84% ETA 1:50:27