PHP 7有兼容的内存缓存吗?

36

2
尝试过使用Memcached吗? - Thamilhan
1
这不是我问的问题。我知道差异随着对缓存的请求量增加而增加。这是简单的数学。;-) 但是,我从未听说过在任何规模的项目中出现这个问题。而且我仍然不确定这是否真实。在键/值存储方面,APC、Redis或memcached的工作方式基本相同。因此,进行基准测试,否则就是谎言。;-) - Jakub Matczak
1
这只是微小的优化,不值得这样做。正如所说,Redis和memcached是常规选择。我更愿意重构代码,减少频繁请求“数百个值”的次数。 - Axalix
1
@Frodik 如果每个请求都有数百次,你是否可以批量处理它们?Memcached 有 setMulti/getMulti 函数,因此您只需要一次网络请求的开销,而不是数百次。 - Matt Prelude
2
你可以对Memcached(或Redis)进行另一种优化,即在本地主机上通过套接字连接,而不是TCP/IP。你提到你使用基于网络的Memcached,理论上应该比基于套接字的连接慢,因为你需要添加网络协议处理。 - Robbie
显示剩余9条评论
4个回答

39

我建议使用Memcached,特别是如果你关心性能。

虽然你说的没错,APC(u)比Memcache快得多,但你没有考虑到:当你担心这些指标时,你将在多个服务器上运行,而APC(u)不能在节点之间共享。

你可以使用单个Memcache实例或集群来为任意数量的应用程序服务器提供服务。在现代应用程序开发中,可扩展性比“我可以从一个服务器中挤出多少性能”更重要。

话虽如此,你的备选方案是APCu,它具有你从APC那里熟悉的所有功能。虽然它已经被标记为与PHP7稳定兼容,但我不建议使用它,因为它是单节点性质的,而且无法与fastcgi正常工作。


1
感谢您的回答。我们的应用程序非常专业化,我们已经在使用memcache,并且出于您所提到的原因。但是,正如我所说,我们还需要本地缓存数百个键值数据,而对于这些数据,memcached等并不是非常适合的选择。 但我同意您的观点,这个答案对于运行“普通”网站的人会很有用。 - Frodik
3
在这种情况下,APCu 可能是你能够得到的最好选择(至少不知道更多关于你特定用途的情况下)。值得一提的是,Memcache 有一堆 set multiget multi 函数,如果你一次性将它们全部设置,这可能对你有用。这样可以节省多个请求的开销。 - Matt Prelude
如果你在使用Windows操作系统怎么办?似乎没有适用于Windows的Memcached动态链接库(dll)。这是否意味着任何需要使用内存缓存的PHP开发人员都必须在Unix环境下进行开发,还是我漏掉了什么? - Kosi

20

APCU是一个没有代码缓存的字面上的 APC (他们从APC中取出了字节码缓存的部分,发布了APCU),它是一个即插即用的替代品。与APC的用户缓存完全相同,它将数据保存在与PHP运行时相同的进程中,因此使用该值就像检索普通变量一样,速度快。


嗯,是的,但 PHP 5.3 或更高版本上的 APC 一切都不稳定 :-( 所以即使它可能在 PHP 7 上运行,我非常担心它也会遭受不稳定性的困扰。 - Frodik
不要害怕,试一下吧。APCu除了本机内置的Zend Opcache之外可能是你唯一需要的,而且它非常稳定。 - Spooky

8
另一种方法(我没有尝试过,但听起来非常有趣)是利用opcache作为键值缓存。这个graphiq文章包含更多细节,但不幸的是没有实际代码(请注意Kerry Schwab的评论)。
要点是确保启用了opcache,并为您需要缓存的数据分配了足够的内存,然后按照以下方式操作(摘自文章,请查看完整版)。缓存过期(除手动删除外)也需要处理,但很容易添加(例如,在包含对象中包装您的数据,并在cache_get中检查它是否过期,如果过期则删除和忽略该记录)。
function cache_set($key, $val) {
   $val = var_export($val, true);
   // HHVM fails at __set_state, so just use object cast for now
   $val = str_replace('stdClass::__set_state', '(object)', $val);
   // Write to temp file first to ensure atomicity
   $tmp = "/tmp/$key." . uniqid('', true) . '.tmp';
   file_put_contents($tmp, '<?php $val = ' . $val . ';', LOCK_EX);
   rename($tmp, "/tmp/$key");
}

function cache_get($key) {
    @include "/tmp/$key";
    return isset($val) ? $val : false;
}

由于opcache的存在,它作为内存缓存,但避免了序列化和反序列化的开销。我认为当写入时(在cache_set函数中)以及在cache_delete函数中(在他们的示例中不存在)也应该调用opcache_invalidate,但除此之外,对于不需要在服务器之间共享的缓存,它似乎很不错。
编辑:下面是一个具有缓存过期的示例实现(仅精确到秒,如果需要更高的精度可以使用microtime(true))。实际上进行了最小测试,并且我放弃了HHVM特定的替换,因此您的情况可能会有所不同。欢迎提出改进建议。
class Cache {                                                                   
    private $root;                                                              
    private $compile;                                                           
    private $ttl;                                                               

    public function __construct($options = []) {                                
        $this->options = array_merge(                                           
            array(                                                              
                'root' => sys_get_temp_dir(),                                   
                'ttl'  => false,                                                
            ),                                                                  
            $options                                                            
        );                                                                      
        $this->root = $this->options['root'];                                   
        $this->ttl = $this->options['ttl'];                                     
    }                                                                           

    public function set($key, $val, $ttl = null) {                              
        $ttl = $ttl === null ? $this->ttl : $ttl;                               
        $file = md5($key);                                                      
        $val = var_export(array(                                                
            'expiry' => $ttl ? time() + $ttl : false,                           
            'data' => $val,                                                     
        ), true);                                                               

        // Write to temp file first to ensure atomicity                         
        $tmp = $this->root . '/' . $file . '.' . uniqid('', true) . '.tmp';     
        file_put_contents($tmp, '<?php $val = ' . $val . ';', LOCK_EX);         

        $dest = $this->root . '/' . $file;                                      
        rename($tmp, $dest);                                                    
        opcache_invalidate($dest);                                              
    }                                                                           

    public function get($key) {                                                 
        @include $this->root . '/' . md5($key);                                 

        // Not found                                                            
        if (!isset($val)) return null;                                          

        // Found and not expired                                                
        if (!$val['expiry'] || $val['expiry'] > time()) return $val['data'];    

        // Expired, clean up                                                    
        $this->remove($key);                                                    
    }                                                                           

    public function remove($key) {                                              
        $dest = $this->root . '/' . md5($key);                                  
        if (@unlink($dest)) {                                                   
            // Invalidate cache if successfully written                         
            opcache_invalidate($dest);                                          
        }                                                                       
    }                                                                           
}      

喜欢这段代码,会在项目中使用,感谢分享! - nnimis

5

PHP 7缓存/加速器列表

已死/过时的PHP加速器列表:XCache、APC、memoize、ZendOpcache、chdb、hidef(它们不支持PHP 7)

我们可以在PECL网站上找到一份PHP加速器列表,但正如我所提到的,其中一些已经停止或不再更新。

当前正在开发的(支持PHP 7.3)有:

您可以在下载的tgz/zip文件中找到所有安装说明。

APCu WINDOWS用户: 下载与您的系统规格匹配(x64(64位)或x86(32位Windows))的APCu和APCu_bc DLL文件,选择TS或UTS版本以及正确的PHP版本。将 .DLL 粘贴到 php/ext 目录中。您可以通过查看 php 目录来确定线程模式。找到DLL文件名称(例如:php7ts.dll)。注意文件名中的TS或UTS。

php -v将显示当前PHP CLI安装版本。 只需确保您的PHP版本与服务器上使用的版本相同即可。如果不是,请更新Windows环境路径以获取PHP。

如果有困难,请阅读以下内容:如何在Windows中安装APCu https://kapilpatel84.wordpress.com/2016/06/15/install-xdebug-apcu-on-windows-xampp-for-php7/

对于XAMPP:

1)使用以下链接下载兼容的APCu http://pecl.php.net/package/apcu

2) APCu需要与APC向后兼容,因此您需要使用以下链接下载它。http://pecl.php.net/package/apcu_bc

3) 解压DDL文件并将名为php_apc.dll和php_apcu.dll的DDL文件移动到您的PHP ext目录。即C:\ xampp \ php \ ext

4) 打开php.ini文件,并将以下代码复制到文件底部

[apcu]
extension="C:\xampp\php\ext\php_apcu.dll"
apc.enabled=1
apc.shm_size=32M
apc.ttl=7200
apc.enable_cli=1
apc.serializer=php
extension="C:\xampp\php\ext\php_apc.dll"

当您解压文件时,请将DLL文件复制到PHP扩展文件夹中,例如:.../php/ext。然后在php.ini文件中进行配置(安装文本文件中包含了相关指令)。

Symfony 4

PS. 如果您使用的是Symfony框架,请不要忘记在config>packages>cache.yaml中启用APCu。

framework:
    cache:
        app: cache.adapter.apcu

使用内置服务器:

php bin/console server:run

2
OPCache并没有死亡,它是事实上的标准PHP加速器,并且自PHP 5.6以来几乎随每个发行版一起发布,作为PHP的组成部分。此外,就性能而言,它是必须使用的扩展。 - Danila Vershinin
@DanilaVershinin 没错,感谢你指出这一点。"此扩展程序与 PHP 5.5.0 及以后版本捆绑在一起,并且 » 对于 PHP 版本 5.2、5.3 和 5.4 可在 PECL 中使用。" 我忽略了 "及以后" 的部分。 - DevWL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接