PHP system()使用MySQL在许多数据库上运行查询


PHP system() using MySQL to run queries on many databases

我下面有一个脚本,它遍历380个MySQL innodb数据库,运行各种创建表、插入、更新。。。等等来迁移模式。它从连接到云数据库服务器的web服务器上运行。我将迁移脚本排除在这个问题之外,因为我认为它不相关。

我遇到了一个问题,我正试图找到一个解决办法。

我有一个运行MySQL 5.6的4gb ram云数据库服务器。我将380个包含40个表的数据库迁移到59个表。大约70%的过程中,我得到了下面的错误。它在一次迁移过程中死亡,服务器也宕机了。我正在观察内存使用情况,但内存不足。它是一个数据库即服务,所以我没有对服务器的root访问权限,所以我不知道所有的细节。

在phppoint_smg 上运行查询


Warning: Using a password on the command line interface can be insecure.
ERROR 2013 (HY000) at line 355: Lost connection to MySQL server during query

在phppoint_soulohhloween 上运行查询


Warning: Using a password on the command line interface can be insecure.
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0

在phppoint_srvais 上运行查询


Warning: Using a password on the command line interface can be insecure.
ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0

以下是PHP脚本的简化版本。

db_host = escapeshellarg($db_host);
$db_user = escapeshellarg($db_user);
$db_password = escapeshellarg($db_password);
foreach($databases as $database)
{
    echo "Running queries on $database'n***********************************'n";
    system("mysql --host=$db_host --user=$db_user --password=$db_password --port=3306 $database < ../update.sql"); 
    echo "'n'n";
}

我的问题:

有没有什么方法可以避免在迁移时内存使用率上升?我一次只做一个数据库。还是表和数据的添加是它上升的原因?

我能够使用服务器的后记,删除了80个数据库并完成了迁移。它有800 mb免费;我预计它将下降到600亿。在迁移之前,它的为500mb

您的PHP示例不占用太多内存,而且它没有在数据库服务器上运行,这就是发生故障的服务器,对吧?所以问题出在您配置的MySQL参数上。

基于您的Gist,使用一个简单的MySQL内存计算器,我们可以看到您的MySQL服务可以使用高达3817MB的内存。如果错误发生时服务器只有4GB,那么很可能是因为这个原因(您需要为操作系统和运行的应用程序提供一些额外的内存)。增加内存或微调服务器可以解决这个问题。查看MySQL关于服务器变量的文档页面,可以最好地了解每个值。

然而,这可能不是断开连接/超时的唯一原因(但这似乎确实是您的情况,因为增加内存解决了问题)。另一个常见的问题是低估了max_allowed_packet的值(在您的配置中为16MB),因为这样的脚本很容易有超出该值的查询(例如,如果您为单个INSERT INTO table ...有多个值)。

考虑一下max_allowed_packet应该大于您向数据库发出的最大命令(它不是SQL文件,而是其中的每个命令,即;之间的块)。

但是,请考虑更仔细的调优,因为配置不好的服务器可能会突然崩溃,或者变得没有响应——而它可以在不添加更多内存的情况下完美运行。我建议运行性能调优脚本,如MySQLTuner-perl,它将分析您的数据、索引使用情况、慢速查询,甚至提出优化服务器所需的调整。

很明显,您的迁移SQL查询会杀死服务器。似乎数据库只需要为这样的操作降低可用RAM。根据数据库文件大小和查询,它肯定会提高RAM的使用率。如果不知道确切的服务器规格、数据库中的数据和您启动的查询,就没有确切的答案可以帮助您。

生成一个文件,然后运行它,而不是生成很多进程

$out = fopen('tmp_script.sql', 'w');
foreach($databases as $database)
{
    fwrite($out, "USE $database;'n");
    fwrite($out, "source ../update.sql;'n");
}
fclose($out);

然后,手动或编程进行

mysql ... < tmp_script.sql

手动操作可能更安全,这样PHP就不会碍事了。

由于服务器的RAM显然非常低,因此您应该尝试释放RAM的一件事是在循环完成后取消设置大数组后强制进行垃圾收集。

我在PHP7(和512Go的RAM)下的PTHREADS中也遇到了类似的问题,它在一台大型服务器上处理1024个到MariaDB和Postgresql的异步连接。

每个循环都要试试这个。

//first unset main immediately at loop start:
unset($databases[$key]);
// second unset process and purge immediately
unset($database);
gc_collect_cycles();

此外,设置一个控件来持续监控加载下的RAM使用情况,看看这种情况是否发生在特定的$数据库上。如果您的RAM太低,请将控件设置为块化$数据库,并执行多插入批处理,然后在完成时取消设置。这将在子插入循环之前清除更多的RAM并避免过大的阵列副本。如果使用带有construct的类,则情况尤其如此。对于4Go,我倾向于设置最多400到500个异步插入的批,这取决于插入的全局长度。

如果您的数据库服务器正在崩溃(或被oom杀手杀死),那么原因是它被配置为使用比设备上可用的内存更多的内存。

您忘记告诉我们数据库节点上运行的操作系统是什么。

如果你没有对服务器的root访问权限,那么这是配置它的人的错。应该禁用内存过度使用(需要root访问权限)。像mysqltuner这样的工具将显示DBMS配置为使用的内存量(需要管理员权限)。另请参阅此percona post

我认为他们对ram的看法是正确的,但值得注意的是,您使用的工具很重要。

你试过了吗http://www.mysqldumper.net/如果使用它(php脚本),请检查php内存限制的设置,并让它自动检测。

我以前用http://www.ozerov.de/bigdump/但它太慢了,我再也不会了。

另一方面,mysqldumper在备份和恢复方面都很快,不会崩溃(如果你设置了内存限制)

我发现这个工具很特别。

更新:

你的评论彻底改变了局面
这是我最新的答案:

由于您无法访问MySQL服务器,因此需要采取其他方法。

强制从导入文件中删除所有特殊的"东西",如封闭事务、插入延迟/忽略等。

强制执行带有单个语句的SQL-我不知道插入是什么样子的,但do-it单语句-单插入-不要在单语句中捆绑很多行,

例如

而不是

insert into x
             (...fields...)values(...single row...),
             (...fields...)values(...single row...), 
             (...fields...)values(...single row...), 
             (...fields...)values(...single row...)
;

进行

insert into x(...fields...)values(...single row...); 
insert into x(...fields...)values(...single row...); 
insert into x(...fields...)values(...single row...); 
insert into x(...fields...)values(...single row...); 

然后试试这些:

  • 你可以尝试用大缓冲区"上传"my.ini等等。MySQL服务器的提供商可能会给你更多的RAM。毕竟是服务:)

  • 您可以尝试生成带有架构的文件和带有数据的文件。然后导入模式,然后开始逐表导入,看看它在哪里崩溃,然后恢复崩溃的文件。

  • 您可以使用MyISAM表导入所有内容。然后您可以在InnoDB中转换这些。alter table x engine=innodb。但是,这样做将丢失所有引用完整性,以后需要强制执行它。

  • 您可以使用MyISAM表导入所有内容。然后,你可以进行,而不是转换这些

每张桌子都是这样的:

alter table x rename to x_myisam;
create table x(...);
insert into x select * from x_myisam;

我相信只有一张桌子打破了这个过程。如果您找到它,您可以手动进行。例如,一次导入10000行或其他内容。

替代方法

如果你的服务器在亚马逊AWS或类似服务中,你可以尝试"扩大"("放大")服务器进行导入,并在导入完成后"缩小"("缩小")。

老答案

为什么要使用php脚本?尝试通过php创建或生成一个shell脚本。然后运行shell脚本。

在系统上创建巨大的交换文件也是非常重要的。这是一种方法。它可能在旧系统上不起作用:

sudo su # became root
cd /somewhere
fallocate -l 16gb file001
mkswap file001
chmod 0 file001
swapon file001

然后执行php或shell脚本。

完成后,您可以切换并删除该文件或使其永久化在fstab中。

如果我需要澄清什么,请告诉我。