我有一个网站,从上周开始在 VPS 上运行。从周一到周六,一切都很顺利。该网站每天有大约 4,500 名独立访客,平均负载和响应时间都很好。
周日,网站有大约 11,000 名独立访客,因为我们在当天提供独特且独家的内容。内容存储在 MySQL 数据库中,该数据库在不同的 VPS 服务器上运行并使用 InnoDB 引擎。这就是问题所在。由于访客数量的增加,平均负载将上升到极限,直到网站无法访问。
以下是顶部输出:
This is an automated message notifying you that the 5 minute load average on your system is 238.37.
This has exceeded the 10 threshold.
One Minute - 237.31
Five Minutes - 238.37
Fifteen Minutes - 231.1
top - 16:41:12 up 5 days, 18:51, 1 user, load average: 238.68, 238.62, 231.25
Tasks: 517 total, 246 running, 271 sleeping, 0 stopped, 0 zombie
Cpu(s): 1.8%us, 0.3%sy, 0.0%ni, 97.6%id, 0.0%wa, 0.0%hi, 0.1%si, 0.2%st
Mem: 3922920k total, 3542968k used, 379952k free, 2736k buffers
Swap: 1048564k total, 105316k used, 943248k free, 142772k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
14395 apache 20 0 313m 13m 4044 R 2.8 0.4 0:09.81 /usr/sbin/httpd -k start -DSSL
13405 apache 20 0 314m 15m 4432 R 2.3 0.4 0:17.87 /usr/sbin/httpd -k start -DSSL
15865 apache 20 0 312m 13m 4176 R 2.3 0.4 0:01.28 /usr/sbin/httpd -k start -DSSL
15930 apache 20 0 310m 11m 4060 R 2.3 0.3 0:00.88 /usr/sbin/httpd -k start -DSSL
15978 apache 20 0 310m 11m 4048 R 2.3 0.3 0:01.08 /usr/sbin/httpd -k start -DSSL
16041 apache 20 0 309m 10m 4052 R 2.1 0.3 0:00.58 /usr/sbin/httpd -k start -DSSL
16082 apache 20 0 211m 4192 2276 R 1.9 0.1 0:00.09 /usr/sbin/httpd -k start -DSSL
14298 apache 20 0 310m 11m 4044 R 0.6 0.3 0:09.56 /usr/sbin/httpd -k start -DSSL
14457 apache 20 0 311m 11m 4068 R 0.6 0.3 0:10.18 /usr/sbin/httpd -k start -DSSL
14486 apache 20 0 310m 11m 4464 R 0.6 0.3 0:06.13 /usr/sbin/httpd -k start -DSSL
15287 apache 20 0 313m 14m 4048 R 0.6 0.4 0:05.21 /usr/sbin/httpd -k start -DSSL
15363 apache 20 0 310m 11m 4064 R 0.6 0.3 0:04.13 /usr/sbin/httpd -k start -DSSL
15400 apache 20 0 313m 13m 4048 R 0.6 0.4 0:04.09 /usr/sbin/httpd -k start -DSSL
15404 apache 20 0 310m 11m 4056 R 0.6 0.3 0:04.22 /usr/sbin/httpd -k start -DSSL
15649 apache 20 0 313m 14m 4432 R 0.6 0.4 0:02.88 /usr/sbin/httpd -k start -DSSL
15675 apache 20 0 310m 10m 4044 S 0.6 0.3 0:02.22 /usr/sbin/httpd -k start -DSSL
15692 apache 20 0 310m 11m 4084 R 0.6 0.3 0:01.46 /usr/sbin/httpd -k start -DSSL
15702 apache 20 0 311m 12m 4044 R 0.6 0.3 0:01.85 /usr/sbin/httpd -k start -DSSL
15719 apache 20 0 310m 10m 4048 R 0.6 0.3 0:02.32 /usr/sbin/httpd -k start -DSSL
15781 apache 20 0 318m 18m 4044 R 0.6 0.5 0:01.91 /usr/sbin/httpd -k start -DSSL
15788 apache 20 0 312m 13m 4048 R 0.6 0.4 0:02.13 /usr/sbin/httpd -k start -DSSL
15823 apache 20 0 310m 11m 4060 R 0.6 0.3 0:02.04 /usr/sbin/httpd -k start -DSSL
15837 apache 20 0 311m 12m 4052 R 0.6 0.3 0:01.64 /usr/sbin/httpd -k start -DSSL
周日,该网站必须执行一个相当大的查询,并在不同的表上进行几个左连接。
网站运行在 VPS 上,包含 2 个 2.4 Ghz 处理器和 4GB 内存。数据库运行在 SSD VPS 上,包含 2 个 2.4 Ghz 处理器和 2GB 内存。
在那个特定的星期天,我还在服务器的 ErrorLog 中收到了此消息:
Sun Nov 24 15:03:34 2013] [error] server reached MaxClients setting, consider raising the MaxClients setting
该网站是使用 PHP Codeigniter 框架创建的,在共享主机上运行良好(使用相同的代码)。几周后,问题开始出现,这就是我决定转移到 VPS 服务器的原因。但问题似乎还在继续。
我完全不知道事情到底出了什么问题,因此我将非常感激任何帮助。
答案1
你的问题的答案是尽可能地利用内存缓存。即memcache,varnish等......然后使用nginx,你可以水平扩展它,在它后面是一个适合你的负载大小的php-fpm池,与上游nginx盒完全啮合。
一旦达到一定的流量水平,解决问题就不再仅仅依靠硬件,而是利用缓存,并拥有可以单独升级/更新的各个层。
你不能在单个 vps 上拥有超高可用性站点,除非它是静态 html,即使那样 varnish 也是理想的。
获取一对 haproxy 前端负载均衡器,分发到 varnish,从 nginx / php / memcache / redis / mysql (postgres) 中提取数据.....简而言之就是这样 :P
答案2
MaxClients 是 Apache 服务器指令。有几个相关指令,但它们都以不同的方式设置 Apache 处理请求的限制。
这样做的目的是为了不让 Apache 消耗太多资源,从而威胁整个系统的稳定性。因此,如果您增加 MaxClients 和其他类似指令,则需要注意 RAM 等系统资源。
更多详情请阅读: MaxClients 指令
但首先,即使您发现了一些症状(很明显,否则您就不会发帖了),也不清楚问题到底出在哪里。Apache 可能向您展示了其他地方的问题。
但是在输出中您关注的是 Apache,因为那部分相当简单,所以让我们从那里开始。
正如您在链接中看到的,MaxClients 定义了 httpd 服务的最大请求数。当该值填满时,请求将在 ListenBackLog 中排队。当该值已满时,客户端将被拒绝。
要么是因为同时的请求数量太多,maxclients 已经满了。然后你需要为此做些准备,这相对容易。在 Apache 层中向上和/或向外扩展,直到你处于安全状态,或者直到数据库层达到最大值。
或者 maxclients 已满,因为底层无法快速提供服务。因此请求在 Apache 中堆积,直到不再接受。这同样不容易解决,因为它首先引发了大量问题。您立即将注意力集中在数据库层上,这可能是有充分理由的,即使您还没有完全弄清楚(目前)。
看看客户端是否被拒绝、拒绝了多少等等,这些也对你有利。如果你将页面状态代码提取到日志中,你可以解析...503,我似乎记得,但你应该用谷歌查一下。这只是为了了解你的交付情况,并设置一个基准,以便与下次发生的情况进行比较。
以下是我想到的一些问题。我意识到找到答案说起来容易做起来难,除非您拥有可以为您提供极高洞察力的工具。我们使用 Dynatrace(面向 Java),它非常昂贵,但可以在几分钟内回答这些问题,甚至对于系统管理员来说,它可以深入到代码中的组件级别。这使得可以快速确定代码、基础设施或两者结合中的因果关系。我知道市场上还有其他工具,也可能有开源替代品可以做到这一点。我只是没有使用它们的经验。我想人们也可以编写代码调试和应用程序日志来记录同样的事情,只是这会耗费更多的工作时间。
在您描述的错误累积期间,请求是否需要更长的时间来处理?我的 Apachelogs 显示了处理每个请求所需的时间。我不记得这是否是默认设置,但如果没有,您可以在本周初发布这样的指令。
如果他们确实花费了更长时间,是因为请求模式不同还是仅仅是因为数量更多?
就 Apache 服务器负载而言,您提供大量静态内容还是主要提供动态内容?您是否以某种方式设定了静态与动态的基准?这个问题很重要,因为静态内容可以分离到不同的交付服务器,也可以分离到 RAM 缓存(另一个答案中提到了 Varnish)。在某些交付中,节省的成本可能很大,而在其他交付中则不然。
是否有许多请求针对(本质上)相同的动态内容,还是每个请求都重新计算一次?换句话说,是否可以注入早期缓存层来捕获某些动态内容?
深入研究一下,代码处理起来是否繁重?能否使用主要触发逻辑但数据库调用相对较少的请求作为基准?也许是代码需要优化或扩展?
数据库调用在数量和查询方式上是否进行了优化或浪费?在高负载期间,每次呼叫的响应时间是多少?响应时间是否增加了?呼叫数量是否达到最大值?
您会看到它越来越接近数据库,在每一层都寻找优化、缓存、分发(扩展)、原始功率(扩大)的机会。
也许你已经有了所有这些答案,并且完全正确地将注意力集中在 innodb 上,我无法根据给出的信息来判断!我所能提供的只是系统性的问题,这些问题可能相关,也可能不相关,也可能引起共鸣 :-)
但尽快将 Apache 排除在外似乎是个好主意(因为它可能只是遭受附带损害)。如果您确实可以验证 Apache 不是根本原因,请关注应用程序以及它如何使用数据库。
所涉及机器的基本统计数据(例如网络 io、RAM 消耗、CPU 消耗、磁盘 io、页面错误等)也有助于研究。