我有一个PHP脚本,它充当我的后端数据库的JSON API。
意思是,你发送一个HTTP请求,如:http://example.com/json/?a=1&b=2&c=3 ...它将返回一个json对象,其结果集来自我的数据库。
PHP非常适用于此,因为它实际上大约有10行代码。
但是我也知道PHP很慢,而且这个API有时被称为每秒40x,而PHP正在努力跟上。
有没有办法可以将我的PHP脚本编译成更快的执行格式?我已经在使用PHP-APC,它是PHP和FastCGI的字节码优化。
或者,是否有人推荐使用我重写脚本的语言,以便Apache仍然可以处理example.com/json/个请求?
由于
更新:我刚刚运行了一些基准测试:
正如您所注意到的,PHP在生成结果时要慢一个数量级。网络似乎不是这种情况下的主要瓶颈,但我同意它通常是根本原因。
答案 0 :(得分:8)
在优化之前,首先要弄清楚它是否有问题。考虑到它只有10行代码(据你所知)我非常怀疑你没有问题。时间脚本执行的时间。请记住,网络延迟通常会使琐碎的脚本执行时间相形见绌。
换句话说:在发生问题之前不要解决问题。
您已经在使用操作码缓存(APC)。它没有那么快。更重要的是,它很少需要来获得更快的速度。
如果有任何问题,您的数据库会出现问题。连接太多(不太可能是每秒20次),连接速度太慢或者大连接:查询太慢。如果您发现自己处于这种情况,那么10次有效索引和数据库调整就足够了。
如果不是你去某种缓存的地方:memcached,beanstalkd等。
但老实说,每秒20倍意味着这些解决方案几乎可以肯定是过度工程,而不是问题。
答案 1 :(得分:3)
我非常幸运地使用PHP,memcached和nginx的memcache模块来获得非常快速的结果。最简单的方法是使用完整的URL作为缓存密钥
我会假设这个网址:
/widgets.json?a=1&b=2&c=3
示例PHP代码:
<?
$widgets_cache_key = $_SERVER['REQUEST_URI'];
// connect to memcache (requires memcache pecl module)
$m = new Memcache;
$m->connect('127.0.0.1', 11211);
// try to get data from cache
$data = $m->get($widgets_cache_key);
if(empty($data)){
// data is not in cache. grab it.
$r = mysql_query("SELECT * FROM widgets WHERE ...;");
while($row = mysql_fetch_assoc($r)){
$data[] = $row;
}
// now store data for next time.
$m->set($widgets_cache_key, $data);
}
var_dump(json_encode($data));
?>
这本身就提供了巨大的性能提升。如果你那时使用nginx作为Apache的前端(将Apache放在8080上,将nginx放在80上),你可以在你的nginx配置中执行此操作:
worker_processes 2;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
access_log off;
sendfile on;
keepalive_timeout 5;
tcp_nodelay on;
gzip on;
upstream apache {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name _;
location / {
if ($request_method = POST) {
proxy_pass http://apache;
break;
}
set $memcached_key $uri;
memcached_pass 127.0.0.1:11211;
default_type text/html;
proxy_intercept_errors on;
error_page 404 502 = /fallback;
}
location /fallback {
internal;
proxy_pass http://apache;
break;
}
}
}
注意set $memcached_key $uri;
行。这将memcached
缓存键设置为使用REQUEST_URI
,就像PHP脚本一样。因此,如果nginx发现带有该密钥的缓存条目,它将直接从内存中提供它,您永远不必触及PHP或Apache。很快。
还有一个非官方的Apache memcache module。没试过,但如果你不想搞乱nginx,这对你也有帮助。
答案 2 :(得分:1)
优化的第一条规则是确保您确实遇到性能问题。第二条规则是通过测量代码来确定性能问题的位置。不要猜。进行严格的测量。
PHP不会成为你的瓶颈。我几乎可以保证。网络带宽和延迟使得使用PHP与编译的C程序的开销相形见绌。如果不是网络速度,那么它将是磁盘I / O,或数据库访问,或者是一个非常糟糕的算法,或者是一些比语言本身更可能的罪魁祸首。
答案 3 :(得分:1)
如果您的数据库非常重读(我猜它是),那么基本的缓存实现会有所帮助,而memcached
会使它非常快。
让我更改此示例的网址结构:
/widgets.json?a=1&b=2&c=3
对于对Web服务的每次调用,您都可以解析GET参数并使用这些参数创建要在缓存中使用的密钥。我们假设您正在查询widgets
。示例代码:
<?
// a function to provide a consistent cache key for your resource
function cache_key($type, $params = array()){
if(empty($type)){
return false;
}
// order your parameters alphabetically by key.
ksort($params);
return sha1($type . serialize($params));
}
// you get the same cache key no matter the order of parameters
var_dump(cache_key('widgets', array('a' => 3, 'b' => 7, 'c' => 5)));
var_dump(cache_key('widgets', array('b' => 7, 'a' => 3, 'c' => 5)));
// now let's use some GET parameters.
// you'd probably want to sanitize your $_GET array, however you want.
$_GET = sanitize($_GET);
// assuming URL of /widgets.json?a=1&b=2&c=3 results in the following func call:
$widgets_cache_key = cache_key('widgets', $_GET);
// connect to memcache (requires memcache pecl module)
$m = new Memcache;
$m->connect('127.0.0.1', 11211);
// try to get data from cache
$data = $m->get($widgets_cache_key);
if(empty($data)){
// data is not in cache. grab it.
$r = mysql_query("SELECT * FROM widgets WHERE ...;");
while($row = mysql_fetch_assoc($r)){
$data[] = $row;
}
// now store data for next time.
$m->set($widgets_cache_key, $data);
}
var_dump(json_encode($data));
?>
答案 4 :(得分:0)
你已经在使用APC操作码缓存,这很好。如果您发现自己仍然无法获得所需的性能,可以尝试以下其他方法:
1)在您的网络服务器前放置一个Squid caching proxy。如果您的请求具有高度可缓存性,那么这可能很有意义。
2)使用memcached缓存昂贵的数据库查找。
答案 5 :(得分:0)
考虑一下,如果您正在处理数据库更新,那么您的MySQL性能就是IMO需要注意的。我会像这样扩展测试工具:
注意这些事情:
我经常使用一个小的探查器适配器包装我的数据库查询,我可以切换到异常查询时间,如下所示:
function query( $sql, $dbcon, $thresh ) {
$delta['begin'] = microtime( true );
$result = $dbcon->query( $sql );
$delta['finish'] = microtime( true );
$delta['t'] = $delta['finish'] - $delta['begin'];
if( $delta['t'] > $thresh )
error_log( "query took {$delta['t']} seconds; query: $sql" );
return $result;
}
就个人而言,我更喜欢将xcache用于APC,因为我喜欢它附带的诊断页面。
计算您的表现。跟踪并发连接数,并查看是否与性能问题相关。您可以从cronjob grep来自netstat的http连接数,并记录下来以供分析。
考虑启用你的mysql查询缓存。
答案 6 :(得分:0)
请参阅this question。你有几个选择。是的,PHP可以编译为本机ELF(甚至可能是FatELF)格式。问题是所有Zend生物的舒适。
答案 7 :(得分:0)
由于您已经安装了APC,因此可以使用它(类似于memcached建议)来存储对象。如果您可以缓存数据库结果,那就去做吧! http://us2.php.net/manual/en/function.apc-store.php http://us2.php.net/manual/en/function.apc-fetch.php
答案 8 :(得分:0)
从您的基准测试看起来PHP代码确实是问题所在。你可以发布代码吗?
删除MySQL代码时只会输入一个硬编码的字符串,表示您将从数据库中获取的内容会发生什么?
由于php需要0.60秒,而MySQL CLI需要0.09秒,我猜测连接创建需要花费太多时间。默认情况下,PHP会为每个请求创建一个新连接,有时可能会很慢。
考虑一下,根据您的环境和代码,您将:
您是否考虑过使用persistent MySQL connections或connection pooling? 它有效地允许您从上面直接跳到查询步骤。
缓存也非常适合性能。我认为其他人已经很清楚了。