【问题标题】:PHP system() using MySQL to run queries on many databasesPHP system() 使用 MySQL 在许多数据库上运行查询
【发布时间】:2016-09-01 04:19:08
【问题描述】:

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

我遇到了一个问题,我正在尝试寻找解决方法。

我有一个运行 MySQL 5.6 的 4GB 内存云数据库服务器。我将 40 个表的 380 个数据库迁移到 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_soulofhalloween 上运行查询


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 的免费空间;我预计它会下降到 600mb。迁移前为 500mb

【问题讨论】:

  • 因为似乎没有时间限制:我假设这不是生产服务器是否正确?
  • 我不确定你所说的时间限制是什么意思?通过使用更多可用内存完成脚本,我能够在错误发生后解决问题。但我想为将来解决这个问题。
  • 我的意思是“脚本不必在一定时间内完成”。我也不完全清楚服务中断是否正常。了解您在此处运行的迁移类型也将有所帮助。如果只需要很少的数据操作,这可能根本不需要 PHP。顺便说一句:你是从 shell 运行你的脚本吗?
  • 我不在乎运行需要多长时间;因为我的系统设置方式可以运行 2 个版本的代码。因此,如果脚本需要更长的时间来一一处理每个数据库,那很好。我不能有太多的停机时间,这就是为什么我有 2 个版本的代码(架构迁移之前 + 之后)。迁移gist.github.com/blasto333/b6ae212929674508bfeec338b992de52
  • 您使用的是什么引擎?你有多少内存? SHOW VARIABLES LIKE "%buffer%"; 可能你的 MySQL 配置不当。

标签: php mysql database


【解决方案1】:

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

根据您的Gist,使用简单的MySQL memory calculator,我们可以看到您的 MySQL 服务最多可以使用 3817MB 内存。如果发生错误时服务器只有 4GB,则很可能这是原因(您需要为操作系统和正在运行的应用程序提供一些额外的内存)。增加内存或微调服务器将解决它。查看MySQL documentation page on server variables 以更好地理解每个值。

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

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

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

【讨论】:

  • 我让 mysql Tuner 说最大使用量是 3400 MB。那好吗?我已经更新了要点以获得最新的设置。这里还有 mysql 调谐器结果gist.github.com/blasto333/669717ceec7e2d9f314967f7fa525b0d
  • 阅读mysqltuner.pl 一般建议,还是说内存太大:Total buffers: 2.3G global + 2.7M per thread (410 max threads)。我建议查看max_connections 直到一个安全的值,并优化其他建议的值——注意mysqltuner 使用你的数据、缓存和统计数据来提出调整,所以你可能想在它投入生产一段时间后查看它.
  • 我认为 4GB 系统中的 3.4GB 有点冒险......您还可以考虑可以连接到服务器的最大可能客户端 - 例如,如果您的限制为 100 PHP Web 服务器进程,不建议在数据库中使用最大 410 个连接。
【解决方案2】:

很明显,您的迁移 SQL 查询会杀死服务器。似乎数据库只需要低可用 RAM 即可执行此类操作。 根据数据库文件大小和查询,它肯定会提高您的 RAM 使用率。 在不知道确切的服务器规格、数据库中的数据和您触发的查询的情况下,没有确切的答案可以帮助您。

【讨论】:

  • 好的,谢谢。我有大约 17GB 的数据库数据。我想我可能需要尽快升级。你认为如果我进行冷迁移或交错组进行迁移会有所帮助吗?这是一次异常大的迁移。
  • 我不明白的一点是我一个接一个地运行它们,但内存呈线性下降。我认为在迁移一个数据库后,它会重新获得一些内存。
  • 我什至不知道数据库的确切机制,但所有这一切听起来都是合理的,即在更多语句后内存会下降。如果它(错误)配置为在 RAM 中保存(太多)信息,那么您会看到这些特征。这完全取决于很多事情,由于缺乏信息,这里无法深入,抱歉。只有您的服务器管理员才能真正提供帮助。
  • 好的;我们将尝试更改一些缓冲区和缓存设置以使用更少的 RAM
  • “很明显……”不!杀死服务器的 SQL 查询不太可能。它们只是提示 DBMS 缓存在其配置所承诺的内存中 - 操作系统无法提供的内存(或服务器有一些故障 RAM)。
【解决方案3】:

与其产生大量进程,不如生成一个文件,然后运行它。生成类似的文件

$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 就不会受到影响。

【讨论】:

  • 我不确定 php 与它有什么关系,因为 Web 服务器很好;它是有问题的数据库服务器。除非你说 mysql 进程有某种效果?
  • 我不知道是什么导致了这个问题。我的建议将把 PHP 排除在外并加快整个过程。
【解决方案4】:

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

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

为每个循环尝试这个。

//first unset main immediately at loop start:
unset($databases[$key]);

// second unset process and purge immediately
unset($database);
gc_collect_cycles();

另外,设置一个控件来持续监控负载下的 RAM 使用情况,以查看这是否发生在特定的 $database 上。如果您的 RAM 太低,请将控件设置为对您的 $database 进行分块并执行多个插入批次并在完成时取消设置它们。这将在子插入循环之前清除更多 RAM 并避免数组副本太大。如果您使用带有构造的类,情况尤其如此。使用 4Go,我倾向于设置最多 400 到 500 个异步插入的批次,具体取决于您的插入全局长度。

【讨论】:

  • 这是一个很有价值的建议,当内存耗尽是PHP端,和/因为MySQL服务器运行在同一台主机上。不过,从 OP 的问题来看,在我看来情况并非如此。
【解决方案5】:

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

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

如果您没有对服务器的 root 访问权限,那么这是配置它的人的错。内存过量使用应为disabled(需要root 访问权限)。 mysqltuner 之类的工具将显示 DBMS 配置为使用多少内存(需要管理员权限)。另见this percona post

【讨论】:

  • 它是一个通过知名供应商提供的数据库即服务,并且大约 99.9999% 的人确信它是 linux 的某个变体(Cent os / Redhat)。我会问他们关于内存过度提交的问题,看看他们对这个工具有什么看法。
  • 我提供了有关可能原因的详细信息,以帮助遇到类似问题但对服务器具有管理员访问权限的任何人。如果您为数据库即服务付费,那么我对您关于数据库崩溃的断言持怀疑态度。您确定数据库正在崩溃并且不仅仅是您的会话失败了吗?无论如何,您支付的部分服务包括对此类问题的支持。可能只是服务器配置了非常低的超时/mysql数据包大小。
【解决方案6】:

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

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

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

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

我发现这个工具很特别。

【讨论】:

    【解决方案7】:

    更新:

    您的 cmets 彻底改变了这种情况。
    这是我的更新答案:

    由于您无法访问 MySQL 服务器,因此您需要采取一些替代方法。

    强制从导入文件中删除所有特殊“事物”,例如封闭事务、延迟插入/忽略等等。

    强制使用单个语句执行 SQL - 我不知道插入是什么样子的,但是做一个语句 - 单个插入 - 不要在单个语句中捆绑很多行,

    例如

    而不是

    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。毕竟是服务:)

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

    • 您可以使用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 行之类的。

    替代方法

    如果您的服务器在 Amazon 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 中。

    如果我需要澄清任何事情,请告诉我。

    【讨论】:

    • 网页服务器不存在内存使用问题;只是在数据库服务器上。它是一个没有交换空间的数据库即服务,我只能调整 my.cnf 设置。我需要一个 php 脚本,因为有很多逻辑可以确定要升级哪些数据库。
    • 哦,我读到您无权访问数据库服务器。那么,你不能做任何事情。可能您将能够导入为 myisam,然后使用 alter table xxx engine=innodb 更改为 innodb。这将破坏您可能拥有的任何参考完整性,但这是您唯一可以做的事情。这是 AWS 吗?您可以购买更大的实例来仅加载数据吗?
    • 我将只为导入而扩大规模,或者想办法以另一种方式减少内存使用量。
    • 我很好奇它是否可以 - 请告诉我。
    猜你喜欢
    • 2015-03-23
    • 2016-01-03
    • 2014-08-17
    • 1970-01-01
    • 2017-03-15
    • 1970-01-01
    • 2016-02-09
    • 2013-03-26
    • 1970-01-01
    相关资源
    最近更新 更多