lamp部署应用和效益
主要的网络媒体资源,例如Wikipedia,Facebook和Yahoo! 使用LAMP体系结构每天可处理数百万个请求,而诸如Wordpress,Joomla,Drupal和SugarCRM之类的Web应用程序软件使用此体系结构使组织能够轻松地部署基于Web的应用程序。
该体系结构的优势在于其简单性。 尽管.NET和Java™技术这样的堆栈可能使用大量的硬件,昂贵的软件堆栈以及复杂的性能调整,但LAMP堆栈可以使用开源软件堆栈在商用硬件上运行。 由于软件堆栈是一组松散的组件而不是一个整体的堆栈,因此性能调整可能是一个挑战,因为需要分析和调整每个组件。
但是,有几个简单的性能任务可能会对各种规模的网站的性能产生巨大影响。 在本文中,我们将研究五个旨在优化LAMP应用程序性能的任务。 如果对您的应用程序进行了任何体系结构更改,那么这些项目所需的内容将非常少,从而使它们成为安全且容易的选择,以最大程度地提高Web应用程序的响应速度和硬件要求。
使用操作码缓存
提高任何PHP应用程序(当然是LAMP中的“ P”)性能的最简单的方法就是利用操作码缓存。 对于我使用的任何网站,这都是我确定存在的一件事,因为对性能的影响是巨大的(很多时候响应时间是没有操作码缓存的一半)。 但是,大多数刚接触PHP的人都有一个大问题,就是为什么改进如此之大。 答案在于PHP如何处理Web请求。 图1概述了一个PHP请求的流程。
图1. PHP请求
由于PHP是一种解释性语言,而不是像C或Java语言这样的已编译语言,因此将对每个请求执行整个parse-compile-execute步骤。 您可以看到这是如何浪费时间和资源的,尤其是当脚本很少在请求之间更改时。 脚本被解析和编译后,该脚本作为一系列操作码处于机器可解析的状态。 这是操作码缓存生效的地方。 它将这些编译的脚本作为一系列操作码进行缓存,以避免每个请求的解析和编译步骤。 您可以在图2中看到这种工作流程如何工作。
图2.利用操作码缓存PHP请求
因此,当存在PHP脚本的缓存操作码时,我们可以跳过PHP请求过程的解析和编译步骤,直接执行缓存操作码并输出结果。 检查算法会处理您可能已更改脚本文件的情况,因此,在更改脚本的第一个请求时,将自动重新编译并缓存操作码,然后针对后续请求替换缓存的脚本。
操作码缓存长期以来一直在PHP中流行,其中一些最早出现在PHP V4的鼎盛时期。 如今,有一些流行的选择正在积极开发和使用中:
- 备用PHP缓存(APC)可能是最流行PHP操作码缓存(请参阅参考资料 )。 它是由几位PHP核心开发人员开发的,并对此做出了重大贡献,它的速度和稳定性都来自Facebook和Yahoo!的工程师。 它还在处理PHP请求方面还进行了其他一些速度改进,包括我们将在本文后面介绍的用户缓存组件。
- Wincache是一种操作码缓存,由Microsoft®的Internet信息服务(IIS)团队最积极地开发,仅在使用IIS Web服务器的Windows®上使用(请参阅参考资料 )。 它的开发主要是为了使PHP成为Windows-IIS-PHP堆栈上的一流开发平台,因为众所周知APC在该堆栈上不能很好地工作。 它在功能上与APC非常相似,并且具有用户缓存组件以及内置的会话处理程序,可以直接将Wincache用作会话处理程序。
- eAccelerator在是原始PHP缓存之一的叉子,图尔克MMCache操作码缓存(参见相关主题 )。 与APC和Wincache不同,它只是一个操作码缓存和优化器,因此它不包含用户缓存组件。 它在UNIX®和Windows堆栈之间完全兼容,并且在不打算利用APC或Wincache提供的附加功能的站点中非常流行。 如果您要使用像memcache这样的解决方案为多Web服务器环境提供单独的用户缓存服务器,通常就是这种情况。
毫无疑问,通过消除对每个请求进行解析和编译脚本的需求,操作码缓存是加快PHP速度的第一步。 第一步完成后,您应该会看到响应时间和服务器负载得到了改善。 但是您可以做更多优化PHP的工作,接下来我们将介绍。
优化您PHP设置
尽管实现操作码缓存对于提高性能是一个巨大的进步,但您可以根据php.ini文件中的设置进行许多其他调整来优化PHP设置。 这些设置更适合生产实例。 在开发或测试实例上,您可能不希望进行这些更改,因为这会使调试应用程序问题更加困难。
让我们看一些对提高性能至关重要的项目。
应该禁用的东西
有几个php.ini设置应该被禁用,因为它们通常用于向后兼容:
-
register_globals
—此功能曾经是PHP V4.2之前的默认功能,在该功能中,传入的请求变量自动分配给普通PHP变量。 除了这样做的主要安全问题(将未经过滤的传入请求数据与常规PHP变量内容混合在一起)外,还必须对每个请求执行此操作的开销。 因此,关闭此选项将使您的应用程序更安全并提高性能。 -
magic_quotes_*
—这是PHP V4的又一遗物,传入的数据将自动转义有风险的表单数据。 它被设计为一种安全功能,可以帮助在将传入数据发送到数据库之前对其进行清理。但是,由于它不能保护用户免受更常见SQL注入攻击,因此它并不是很有效。 由于大多数数据库层都支持可更好地处理这种风险的预准备语句,因此,将其关闭将再次消除烦人的性能问题。 -
always_populate_raw_post_data
—仅出于某种原因,您需要查看未过滤的传入POST
数据的整个有效负载时,才真正需always_populate_raw_post_data
。 否则,它只是在内存中存储POST数据的重复副本,而无需这样做。
但是,在遗留代码上禁用这些选项可能会有风险,因为它们可能取决于为正确执行而设置的选项。 取决于所设置的这些选项,不应开发任何新代码,并且您应该寻找可能的方式来重构现有代码,以免使用它们。
应该启用或调整其设置的内容
您可以在php.ini文件中启用一些良好的性能选项,以使脚本速度有所提高:
-
output_buffering
—您应该确保已启用此选项,因为它将以大块刷新输出到浏览器,而不是在每个echo
或print
语句上,后者会大大减慢您的请求响应时间。 -
variables_order
—该指令控制传入请求的EGPCS(Environment
,Get
,Post
,Cookie
和Server
)变量解析的顺序。 如果您没有使用某些超全局变量(例如环境变量),则可以安全地删除它们,从而不必在每个请求中都解析它们而获得较小的加速。 -
date.timezone
—这是PHP V5.1中添加的指令,用于设置默认时区,以与随后引入的DateTime
函数一起使用。 如果未在php.ini文件中进行设置,则PHP会执行许多系统请求来确定其含义,而在PHP V5.3中,每个请求都会发出警告。
就应该在生产实例上配置的设置而言,这些被认为是“低落的果实”。 就PHP而言,还有另外一件事要看。 这是在您的应用程序中使用require()
和include()
(及其同级的require_once()
和include_once()
)。 这些优化了您PHP配置和代码,以防止对每个请求进行不必要的文件状态检查,这会减慢响应时间。
管理您的require()
和include()
就性能而言,文件状态调用(意味着对基础文件系统进行的调用以检查文件是否存在)可能会非常昂贵。 文件状态最大的罪魁祸首之一是require()
和include()
语句的形式,它们用于将代码带入脚本中。 require_once()
和include_once()
的同级调用可能会遇到更多问题,因为它们不仅需要验证文件的存在,而且还需要验证文件之前是否包含。
那么解决这个问题的最佳方法是什么? 您可以采取一些措施来加快速度。
- 对所有
require()
和include()
调用使用绝对路径。 对于PHP,这将使您希望包含的确切文件更加清楚,从而无需检查文件的整个include_path
。 - 将
include_path
的条目数保持低水平。 通过不检查不包含文件的位置,这将为难以为每个require()
和include()
调用提供绝对路径的情况(在大型旧应用程序中通常是这种情况)提供帮助。
APC和Wincache也具有缓存由PHP进行的文件状态检查结果的机制,因此不需要重复的文件系统检查。 当您将包含文件名保持静态而不是变量驱动时,它们最有效。因此,尽可能尝试执行此操作很重要。
优化数据库
数据库优化很快就会成为一个相当高级的话题,而我在这里几乎没有足够的空间来做这个话题。 但是,如果您要优化数据库的速度,则应首先采取一些步骤,以帮助解决遇到的最常见问题。
将数据库放在自己的计算机上
数据库查询本身会变得非常紧张,通常将CPU占用100%的资源,以执行具有合理大小的数据集的简单SELECT
语句。 如果您的Web服务器和数据库服务器都在单台计算机上争夺CPU时间,那肯定会减慢您的请求。 因此,我认为将Web服务器和数据库服务器放在单独的机器上是一个很好的第一步,并确保您使数据库服务器成为两者中的佼佼者(数据库服务器喜欢大量的内存和多个CPU)。
正确设计和索引表
数据库性能不佳的最大原因可能是数据库设计不佳和索引缺失所致。 SELECT
语句通常是绝大多数在典型Web应用程序中运行的最常见查询类型。 它们也是在数据库服务器上运行的最耗时的查询。 此外,这些类型SQL语句对正确的索引编制和数据库设计最敏感,因此请参考以下指针,以获得最佳性能的提示。
- 确保每个表都有一个主键。 这为表提供了默认顺序,并为将表与其他表连接的快速方法。
- 确保表中的所有外键(即,将记录链接到另一个表中的记录的键)都已正确索引。 许多数据库将自动对这些键施加约束,以使值实际上与另一个表中的记录匹配,这可以帮助您解决这一问题。
- 尝试限制表中的列数。 表中的列太多会使查询的扫描时间比只有几列的情况要长得多。 另外,如果您的表中有很多通常不使用的列,那么您还会浪费磁盘空间中的
NULL
值字段。 对于可变大小字段(例如文本或Blob),表大小可能会变得比所需的大得多,这也是正确的。 在这种情况下,您应该考虑将其他列拆分到另一个表中,并将它们连接到记录的主键上。
分析服务器上正在运行的查询
改善数据库性能的最佳工具是分析数据库服务器上正在运行哪些查询以及运行它们需要多长时间。 几乎每个数据库都有执行此操作的工具。 使用MySQL,您可以利用慢查询日志查找有问题的查询。 要使用它,请将MySQL配置文件中的slow_query_log
设置设置为1,然后将log_output设置为FILE,以将它们记录到文件hostname-slow.log中。 您可以将long_query_time
阈值设置为查询必须运行多长时间(以秒为单位),才能将其视为“慢速查询”。 建议您首先将其设置为5秒,然后根据时间将其降低到1秒,具体取决于您的数据集。 如果查看此文件,将看到与清单1类似的详细查询。
清单1. MySQL慢查询日志
/usr/local/mysql/bin/mysqld, Version: 5.1.49-log, started with:
Tcp port: 3306 Unix socket: /tmp/mysql.sock
Time Id Command Argument
# Time: 030207 15:03:33
# User@Host: user[user] @ localhost.localdomain [127.0.0.1]
# Query_time: 13 Lock_time: 0 Rows_sent: 117 Rows_examined: 234
use sugarcrm;
select * from accounts inner join leads on accounts.id = leads.account_id;
我们要查看的关键是Query_time
,它显示查询花费了多长时间。 要查看的另一件事是Rows_sent
和Rows_examined
的数量,因为它们可能指向查询,如果它查看的行过多或返回的行过多,则可能会错误地编写查询。 您可以通过在查询之前添加EXPLAIN
来更深入地查询查询的编写方式,这将返回查询计划而不是结果集,如清单2所示。
清单2. MySQL EXPLAIN
结果
mysql> explain select * from accounts inner join leads on accounts.id = leads.account_id;
+----+-------------+----------+--------+--------------------------+---------+---
| id | select_type | table | type | possible_keys
| key | key_len | ref | rows | Extra |
+----+-------------+----------+--------+--------------------------+---------+--------
| 1 | SIMPLE | leads | ALL | idx_leads_acct_del | NULL | NULL
| NULL | 200 | |
| 1 | SIMPLE | accounts | eq_ref | PRIMARY,idx_accnt_id_del | PRIMARY | 108
| sugarcrm.leads.account_id | 1 | |
+----+-------------+----------+--------+--------------------------+---------+---------
2 rows in set (0.00 sec)
MySQL手册深入探讨了EXPLAIN
输出的主题(请参阅参考资料 ),但是我要看的主要是'type'列为'ALL'的地方,因为这需要MySQL进行全表扫描和不使用键进行查找。 这些帮助将您指向添加索引将显着提高查询速度的地方。
有效地缓存数据
如上一节所述,数据库很容易成为Web应用程序性能的最大痛点。 但是,如果您查询的数据不经常更改怎么办? 在这种情况下,将这些结果存储在本地而不是在每个请求上调用查询都是一个不错的选择。
我们前面介绍的两个操作码缓存APC和Wincache都具有执行此操作的功能,您可以在其中将PHP数据直接存储到共享内存段中以进行快速检索。 清单3提供了有关如何执行此操作的示例。
清单3.使用APC缓存数据库结果的示例
<?php
function getListOfUsers()
{
$list = apc_fetch('getListOfUsers');
if ( empty($list) ) {
$conn = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'dbuser', 'dbpass');
$sql = 'SELECT id, name FROM users ORDER BY name';
foreach ($conn->query($sql) as $row) {
$list[] = $row;
}
apc_store('getListOfUsers',$list);
}
return $list;
}
我们只需要执行一次查询。 然后,将结果推送到键getListOfUsers
下的APC用户缓存中。 从现在开始,直到缓存过期,您将能够直接从缓存中获取结果数组,而跳过SQL查询。
APC和Wincache并不是用户缓存的唯一选择。 memcache和Redis是其他受欢迎的选择,它们不需要您在与Web服务器相同的服务器上运行用户缓存。 这样可提供更高的性能和灵活性,尤其是在跨多个Web服务器扩展Web应用程序的情况下。
结论
在本文中,我们研究了五种简单的方法来调整LAMP应用程序以获得更好的性能。 我们不仅研究了PHP级别的技术,还利用了操作码缓存并优化了PHP配置,还研究了优化数据库设计以实现正确索引的技术。 我们还研究了如何利用用户缓存(以APC为例)来说明当数据不经常更改时如何避免重复的数据库调用。
翻译自: https://www.ibm.com/developerworks/opensource/library/os-5waystunelamp/index.html
lamp部署应用和效益