如何使用 MySQL+PHP+nginx 并行化请求?

最近的发现

在我尝试过的所有其他事情中,我用自定义 JavaScript 替换了我的 JMeter 配置文件,该 JavaScript 在无限循环中依次访问我的每个 API 端点,然后在不同的浏览器(一个 Firefox、一个 Chrome、一个 Safari)中并行运行这个脚本 -尝试排除与来自同一来源的所有连接相关的问题(相同的用户代理、相同的 cookie、相同的会话 ID 等)


当我这样做时,我注意到我所有的问题都消失了。查询并行运行,应用程序的响应速度比 JMeter 让您相信的要快得多


在我看来,JMeter 将序列化请求似乎是不可能的,因为它是负载测试的事实上的标准。所以我开始尝试重现这种行为


为了重新创建 JMeter,我创建了以下两个 PHP 脚本,它们(希望)模拟了我的 Yii 应用程序:


慢.php


<?php


session_start();


$_SESSION['some'] = 'value';

// Yii is calling session_write_close() almost immediately after

// the session is initialized, but to try and exacerbate issues,

// I've commented it out:

// session_write_close();


$dsn = "mysql:host=localhost;dbname=platypus;unix_socket=/tmp/mysql.sock";

$pdo = new PDO($dsn, "user", "password");

// Yii was using whatever the default persistence behavior was,

// but to try and exacerbate issues I set this flag:

$pdo->setAttribute(PDO::ATTR_PERSISTENT, true);

// Simulate a query running for 1 second by issuing a 1-second sleep

$pdo->query("DO SLEEP(1)");


echo "Done";

快速.php


<?php


session_start();


$_SESSION['some'] = 'value';


$dsn = "mysql:host=localhost;dbname=platypus;unix_socket=/tmp/mysql.sock";

$pdo = new PDO($dsn, "user", "password");

$pdo->setAttribute(PDO::ATTR_PERSISTENT, true);

// Simulate a query running for 0.1 seconds

$pdo->query("DO SLEEP(0.1)");


echo "Done";

针对这两个新端点运行 JMeter,没有请求序列化。一切都是并行的。即使我扩展到 3、4 和 5 个线程,fast.php 总是在 100-150 毫秒内返回,而 slow.php 总是在 1000-1050 毫秒内返回。我能够看到事情在 11 个线程崩溃,但那是因为我超过了 PHP 中的工作线程数

所以总结一下:

  • 该问题仅在使用 JMeter 分析我的 API 时出现,并且不是应用程序本身固有的

  • 这个问题不仅仅是一个 JMeter 错误,而是与我的应用程序或 Yii 1.1 相关联

  • 我试过但无法提出最低限度的复制案例

尽管在使用其他工具进行分析时不存在该问题,但很多人做出了回应并提供了很多有用的信息:

  • 避免 PHP 中的持久连接(可能导致多个请求共享一个连接,可能不会)

  • session_write_close()通过尽早调用来避免会话锁定

  • 确保您有足够的 PHP 工作线程来处理同时连接的数量

  • MySQL 完全支持并行请求(如果硬件可以处理的话)

  • 警惕表锁定(任何带有UPDATE语句的事务都可能锁定表)

  • MyISAM 执行表级锁定而不是行级锁定


桃花长相依
浏览 161回答 4
4回答

阿晨1998

MySQL + PHP + Apache“一直”非常擅长“并行”运行单独的 SQL 语句。如果单独的用户发出 HTTP 请求,他们自然会快速通过 Apache(可能是按顺序,但很快)并到达单独的 PHP 实例(假设 Apache 已经配置了足够的“孩子”)。每个 PHP 脚本都会建立自己的 MySQL 连接。MySQL 将很快接受多个连接(假设max_connections足够高,默认情况下)。每个 MySQL 连接都将独立工作(使用低级数据库锁、互斥锁等)。每个都将在完成时完成,对于 PHP 和 Apache 也是如此,将结果返回给用户。我假设(不确定)nginx 的工作方式类似。注意:我建议 Apache(和 nginx)按顺序执行。但我怀疑将 HTTP 请求交给 PHP 需要大约一毫秒,所以这个“串行”步骤不会解释你找到的时间。我得出结论,其中之一并没有真正发生:每一步的配置都不允许 3 个孩子/连接/等,或者有 3 个单独的 HTTP 请求。有 3 个单独的 PHP 脚本。这 3 个 SQL 语句没有相互阻塞。(请提供 SQL。) 注意:ENGINE=MyISAM使用表锁定;仅此一项就可以解释问题。(请提供SHOW CREATE TABLE。)有可能(在看到 SQL 之后)加快 SQL 的速度,从而减少缓慢的整体问题。查询假设id是PRIMARY KEY每个表的,那么这些其他索引可能有助于加快查询 2:backup_broadcast:&nbsp; (deletion, id)shares:&nbsp; (media_type, media_id, site_id)broadcast:&nbsp; (site_id, id)video:&nbsp; (deletion, id)playlists_playlists:&nbsp; (playlist_id, broadcast_id)playlist_broadcast闻起来像“多对多映射”表。如果是这样,我建议遵循http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table中的提示。(对于任何类似的表格也是如此。)OR并且IN ( SELECT ... )往往是低效的结构。但听起来你对查询没有任何控制权?那是LIMIT没有的ORDER BY吗??你关心你得到哪 10 行吗?这将是不可预测的。这么多列会发生什么?似乎每次运行查询时它们中的大多数都是相同的,因此主要是浪费时间?对于查询 3,site需要INDEX(deletion, customer_id)(按任意顺序)。然而,重新制定它以使用 a JOINorEXISTS可能会运行得更快。

慕码人2483693

我认为您的 php 会话锁定存在问题:您的第二个和第三个查询正在尝试访问同一个 php 会话,并且正在等待。尝试尽快调用session_write_close,以释放您的 php 会话。尽快:当您确定不会在 php 会话中写入任何数据时。另一个线程,您可以在其中了解未正确处理会话的危险关于什么是 php 会话的长解释(我没有通读,抱歉)一个简单的检查方法是尝试使用 2 个浏览器或匿名/隐身模式:您的 cookie 不会被共享,并且您应该有 2 个会话,而不是相互阻止。

慕码人8056858

MySQL 可以处理大量并行查询,但您不能在同一时间为每个连接执行多个查询。PHP 通常的设置方式是每个请求都转到不同的线程/进程,因此每个进程都会有自己的 MySQL 连接,从而避免了上面提到的问题。除非您在 PHP 中使用持久连接,否则您最终可能会为每个请求使用相同的连接。如果是这种情况,应该很容易禁用它并返回到每个请求模型的标准一个数据库连接。我的第一个猜测是端点 2 在数据库上触发了一些锁定,这就是为什么端点 3 查询排队直到 enpoint2 的查询完成。这可以通过更改代码中的逻辑(避免或最小化数据库锁定)或通过更改数据库配置或用于更好地满足应用程序需求的表引擎来解决。示例:InnoDB 使用行级锁定,而 MyISAM 锁定整个表锁定。如果您不介意配置它,分析将非常有用。如果你走这条路,我建议看看 Blackfire.io、New Relic 或 xdebug profiling。通过这种方式,您将能够更快地找到瓶颈。

UYOU

嗯……评论太长了。稍微简化一下,每个引擎都有一个队列,用于收集要计算的查询,具体取决于硬件,它使用 2 个或 3 个甚至更多线程来计算每个查询。每个查询需要更多的线程运行更多的时间,因为锁,就像它锁定整个表一样,当它插入一个具有自动增量的新行时。(你会发现很多关于锁的例子)。当然,每个查询都需要内存和其他资源,它们必须与服务器上运行的所有计算机软件的其余部分共享。使用集群,您需要为管理多个 sql 服务器付出代价。所以从 sql server 端来看,它是并行的,但是你需要硬件来支持许多线程/许多引擎(应该非常小心地使用)当然,你可以在 sql 中拥有多个用户,但为了方便起见,通常每个 APP 一个,有时甚至每个服务器一个。但是同一个用户可以同时访问数据库,当然你可以禁用它。您的 php 并行运行,因为 Web 服务器是为运行 papallel 请求而构建的,并且它运行 php、Python(django) 或 javascript(nodejs) 、 apache、 IIS、 nginx 都没有关系,还有更多,每一种技术有没有额外的好处,并且导致您添加到引擎的更多模块,它变得慢得多。因此,一切都在一定程度上是平行的,您可以增加您在云提供商或虚拟服务器等中看到的此类系统的功能。只有在引入 Pokemon go 或新游戏时才会注意到这些限制,即使是大型云提供商也会崩溃。或者是奥巴马医改的灾难,没有进行如此规模的测试,无论哪个...负责,并行化这样的任务是困难的,因为在 webserver 和 sqlserver 的情况下,它必须在一定程度上缓存他们经常发出的请求,但通常每个请求都需要自己的数据。实际上一切都要复杂得多,从具有 3 个管道、多核和共享内存(导致 Meltdown 及其兄弟)的 cpus 开始,遍历仅驻留在内存中以实现高性能的表或数据库或仅在缓存中运行的 Web 服务器cpus,比内存或硬盘快得多......
打开App,查看更多内容
随时随地看视频慕课网APP