Select on a big mySQL table 激发所有连接切换到等待处理程序提交的状态
Select on a big mySQL table provokes all connections to switch to status waiting for handler commit
我有一个大约 90Gb 的 InnoDB table。我已经把它停产了。因此只有我在对它进行操作,我不会冒锁定问题的风险。
我正在寻找 Select 和分组的策略,以便在另一个 table 中保留数据摘要,然后删除所有 89Gb。
我尝试过的一切似乎都占用了太多资源:几分钟后,所有连接都卡在等待处理程序提交的状态,这非常糟糕。
(即使没有任何其他用户查询此 table)
我怀疑内存有问题
到目前为止我想到的最好的是这个
CREATE PROCEDURE insert_into_backup_summary()
BEGIN
DECLARE d DATE DEFAULT DATE("2020-07-07");
WHILE d <= "2020-10-16" DO
INSERT INTO backup_summary
SELECT NOW() backupDate, userId,
DATE(createdDate) `date`, COUNT(id) count
FROM backup
WHERE DATE(createdDate) >= d
AND DATE(createdDate) < DATE_ADD(d, INTERVAL 10 DAY)
GROUP BY userId, `date`;
SET d = DATE_ADD(d, INTERVAL 10 DAY);
END WHILE;
END;
但它仍然会在 10-15 分钟后阻止所有连接。
我只尝试了一个简单的 SELECT * FROM backup
,它在几秒钟后阻塞,这让我假设问题出在 SELECT 查询
有没有办法在这个过程中保持良好的记忆力?
听起来你的循环导致了对系统资源的大量争用。当我看到线程在 COMMIT 上被阻塞时,通常是因为 I/O 已饱和。换句话说,您强制从磁盘读取如此多的数据,以至于没有其他人有机会使用磁盘。
我注意到您的条件将导致循环的每次迭代 table-scans。当您使用像 DATE(createdDate) >= d
这样的条件时,即使您在该列上有索引,它也不能在 createdDate
上使用索引。只要您在索引列上使用函数,就会发生这种情况。因此它必须检查 table 中的每一行,而不仅仅是日期范围之间的行。
要解决此问题,请在不将函数放在函数内部的情况下引用函数:
WHERE createdDate >= d AND createdDate < DATE_ADD(d, INTERVAL 10 DAY)
这对于选择日期范围应该同样有效,并且允许使用索引。这应该会减少对磁盘的需求。
您可能还想让循环在迭代之间稍微休息一下。你可以这样做:
SET d = DATE_ADD(d, INTERVAL 10 DAY);
DO SLEEP(10);
END WHILE;
这应该让其他线程有机会在被阻塞时使用磁盘存储。
除此之外,我想问一下您使用的是什么类型的存储?因为如果你有过时或缓慢的存储,你应该在 90GB tables 上工作时考虑到这一点。
回复您的评论:
@@innodb_log_file_size: 50331648 @@innodb_buffer_pool_size: 4294967296
您的日志文件大小为 50MB,即 default。我建议您将此值增加到至少 1GB。在我的公司,我们将其设置为 1GB 作为起点,这对于大多数生产应用程序来说已经足够了。
我现在在想,当您如此快速地写入如此多的记录时,您正在填满您的 innodb 日志文件。当日志文件已满时,innodb 必须阻止提交,直到缓冲池的某些部分被刷新到 table 空间,从而释放部分日志文件以被覆盖。在达到此状态之前,增加日志文件的大小应该允许更多写入。
您的缓冲池大小为 4GB,考虑到您的服务器上有 128GB 的 RAM,这似乎很小。您可能应该将它增加很多,但您必须记住服务器上可能需要 RAM 的其他进程。
我有一个关于调整选项的演示。我在 MySQL 5.5 是当前版本时编写了演示文稿,但它仍然是非常准确的建议。
https://www.slideshare.net/billkarwin/mysql-55-guide-to-innodb-status
这个博客也是很好的资料:
https://www.percona.com/blog/2016/10/12/mysql-5-7-performance-tuning-immediately-after-installation/
我有一个大约 90Gb 的 InnoDB table。我已经把它停产了。因此只有我在对它进行操作,我不会冒锁定问题的风险。
我正在寻找 Select 和分组的策略,以便在另一个 table 中保留数据摘要,然后删除所有 89Gb。
我尝试过的一切似乎都占用了太多资源:几分钟后,所有连接都卡在等待处理程序提交的状态,这非常糟糕。 (即使没有任何其他用户查询此 table) 我怀疑内存有问题
到目前为止我想到的最好的是这个
CREATE PROCEDURE insert_into_backup_summary()
BEGIN
DECLARE d DATE DEFAULT DATE("2020-07-07");
WHILE d <= "2020-10-16" DO
INSERT INTO backup_summary
SELECT NOW() backupDate, userId,
DATE(createdDate) `date`, COUNT(id) count
FROM backup
WHERE DATE(createdDate) >= d
AND DATE(createdDate) < DATE_ADD(d, INTERVAL 10 DAY)
GROUP BY userId, `date`;
SET d = DATE_ADD(d, INTERVAL 10 DAY);
END WHILE;
END;
但它仍然会在 10-15 分钟后阻止所有连接。
我只尝试了一个简单的 SELECT * FROM backup
,它在几秒钟后阻塞,这让我假设问题出在 SELECT 查询
有没有办法在这个过程中保持良好的记忆力?
听起来你的循环导致了对系统资源的大量争用。当我看到线程在 COMMIT 上被阻塞时,通常是因为 I/O 已饱和。换句话说,您强制从磁盘读取如此多的数据,以至于没有其他人有机会使用磁盘。
我注意到您的条件将导致循环的每次迭代 table-scans。当您使用像 DATE(createdDate) >= d
这样的条件时,即使您在该列上有索引,它也不能在 createdDate
上使用索引。只要您在索引列上使用函数,就会发生这种情况。因此它必须检查 table 中的每一行,而不仅仅是日期范围之间的行。
要解决此问题,请在不将函数放在函数内部的情况下引用函数:
WHERE createdDate >= d AND createdDate < DATE_ADD(d, INTERVAL 10 DAY)
这对于选择日期范围应该同样有效,并且允许使用索引。这应该会减少对磁盘的需求。
您可能还想让循环在迭代之间稍微休息一下。你可以这样做:
SET d = DATE_ADD(d, INTERVAL 10 DAY);
DO SLEEP(10);
END WHILE;
这应该让其他线程有机会在被阻塞时使用磁盘存储。
除此之外,我想问一下您使用的是什么类型的存储?因为如果你有过时或缓慢的存储,你应该在 90GB tables 上工作时考虑到这一点。
回复您的评论:
@@innodb_log_file_size: 50331648 @@innodb_buffer_pool_size: 4294967296
您的日志文件大小为 50MB,即 default。我建议您将此值增加到至少 1GB。在我的公司,我们将其设置为 1GB 作为起点,这对于大多数生产应用程序来说已经足够了。
我现在在想,当您如此快速地写入如此多的记录时,您正在填满您的 innodb 日志文件。当日志文件已满时,innodb 必须阻止提交,直到缓冲池的某些部分被刷新到 table 空间,从而释放部分日志文件以被覆盖。在达到此状态之前,增加日志文件的大小应该允许更多写入。
您的缓冲池大小为 4GB,考虑到您的服务器上有 128GB 的 RAM,这似乎很小。您可能应该将它增加很多,但您必须记住服务器上可能需要 RAM 的其他进程。
我有一个关于调整选项的演示。我在 MySQL 5.5 是当前版本时编写了演示文稿,但它仍然是非常准确的建议。 https://www.slideshare.net/billkarwin/mysql-55-guide-to-innodb-status
这个博客也是很好的资料: https://www.percona.com/blog/2016/10/12/mysql-5-7-performance-tuning-immediately-after-installation/