我可以将PHP脚本编译为更快的执行格式吗?

时间:2009-10-30 03:18:41

标签: php optimization compilation fastcgi

我有一个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.6秒 完整
  • 如果我使用上面PHP脚本中生成的SQL并从同一个Web服务器运行查询,但直接从MySQL命令中运行,这意味着,网络延迟仍然在起作用 - 获取的结果集只需0.09秒即可完成

正如您所注意到的,PHP在生成结果时要慢一个数量级。网络似乎不是这种情况下的主要瓶颈,但我同意它通常是根本原因。

9 个答案:

答案 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需要注意的。我会像这样扩展测试工具:

  • 在dbserver上运行mytop
  • 从客户端运行ab(apache bench),例如桌面
  • 在网络服务器上运行top或vmstat

注意这些事情:

  • 更新表强制读取等待(MyISAM引擎)
  • 网络服务器上的高负载(可能表示网络服务器上的内存条件不足)
  • 网络服务器上的高磁盘活动,可能来自日志记录或其他导致随机搜索未缓存文件的Web请求
  • apache进程的内存增长。如果您的结果集转换为大型关联数组,或者进行序列化/反序列化,这些可能会成为昂贵的内存分配操作。您的代码可能需要避免调用mysql_fetch_assoc()并一次开始获取一行。

我经常使用一个小的探查器适配器包装我的数据库查询,我可以切换到异常查询时间,如下所示:

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会为每个请求创建一个新连接,有时可能会很慢。

考虑一下,根据您的环境和代码,您将:

  1. 将MySQL服务器的主机名解析为IP
  2. 打开与服务器的连接
  3. 对服务器进行身份验证
  4. 最后运行您的查询
  5. 您是否考虑过使用persistent MySQL connectionsconnection pooling? 它有效地允许您从上面直接跳到查询步骤。

    缓存也非常适合性能。我认为其他人已经很清楚了。