带有OpCache的PHP用户级缓存(未按预期工作) [英] PHP userland cache with OpCache (not working as expected)

查看:75
本文介绍了带有OpCache的PHP用户级缓存(未按预期工作)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用PHP OpCache作为用户区缓存(如APCu,Redis,Memcache)作为后备,而没有更好的缓存解决方案.

i want use PHP OpCache as userland cache (like APCu, Redis, Memcache) as fallback where better caching solution aren't avaible.

这个想法是存储数据以缓存到运行时创建的php文件中,并使用include读取数据.这样,OpCache应该在编译文件的内存中缓存,结果是内存缓存.

The idea is store the data to cache into php files created at runtime and read the data with include. In this way, OpCache should be cache in memory the compiled file and the result is a memory cache.

<?php

/**
 * Simple php cache using php generated files and opcache
 */
class DiskCache {

    const DEFAULT_TTL = 3600;

    /**
     * @var callable
     */
    private static $emptyErrorHandler;

    /**
     * @var string
     */
    protected $cacheDir;

    /**
     * @var int
     */
    protected $defaultTtl;


    /**
     * Constructor
     * @param string  $cacheDir where to store cache files
     * @param integer $ttl      time to live
     */
    public function __construct($cacheDir = null, $ttl = self::DEFAULT_TTL) {

        if( empty($cacheDir) ){
            $cacheDir = sys_get_temp_dir();
        }

        $cacheDir = realpath(rtrim($cacheDir, DIRECTORY_SEPARATOR));

        if( !is_dir($cacheDir) ) {
            throw new InvalidArgumentException('Provided cache dir is not a directory');
        }

        if( !(is_readable($cacheDir) && is_writable($cacheDir)) ) {
            throw new InvalidArgumentException('Provided cache dir is not writable and readable');
        }

        $this->cacheDir   = $cacheDir;
        $this->defaultTtl = (int) $ttl;

        self::$emptyErrorHandler = function(){};
    }

    /**
     * Read cache
     * @param string  $key  the key
     * @return mixed|false  cached data
     */
    public function read($key) {

        $fileName = $this->getCacheFilename($key);

        set_error_handler(self::$emptyErrorHandler);

        $cached = include $fileName;

        restore_error_handler();

        if( $cached && isset($cached['timestamp'], $cached['ttl'], $cached['data']) ) {
            if((time() - $cached['timestamp']) < $cached['ttl']){
                return $cached['data'];
            }
        }

        if( $cached ) {
            $this->delete($key);
        }

        return false;
    }

    /**
     * Write cache
     * @param string  $key    the key
     * @param mixed   $data   the data
     * @param integer $ttl    time to live
     * @return boolean
     */
    public function write($key, $data, $ttl = null) {

        $ttl = $ttl > 0 ? (int) $ttl : $this->defaultTtl;
        $fileName = $this->getCacheFilename($key);
        $code = null;
        $result = false;

        $value = array(
            'timestamp' => time(),
            'ttl'       => $ttl,
            'data'      => $data
        );

        if (is_object($data) && method_exists($data, '__set_state')) {
            $value = var_export($value, true);
            $code  = sprintf('<?php return %s;', $value);
        } else {
            $value = var_export(serialize($value), true);
            $code  = sprintf('<?php return unserialize(%s);', $value);
        }

        if( $code ){
            $result = @file_put_contents($fileName, $code, LOCK_EX);
        }

        return (boolean) $result;
    }

    /**
     * Delete cache
     * @param string $key
     * @return boolean
     */
    public function delete($key) {
        $fileName = $this->getCacheFilename($key);
        return @unlink($fileName);
    }

    /**
     * Return the cache filename
     * @param string $key
     * @throws InvalidArgumentException
     * @return string
     */
    public function getCacheFilename($key){
        if( empty($key) ) {
            throw new InvalidArgumentException('key is empty');
        }
        return $this->cacheDir . DIRECTORY_SEPARATOR . md5($key). '.php';
    }   
}

我已经在 test.php 页面中以这种方式进行了测试:

i have tested in this way in a test.php page:

<?php
$cache = new DiskCache(__DIR__);
echo PHP_EOL;
var_dump($cache->write('test', array('a', 'b', 'c')));
echo PHP_EOL;
var_dump($cache->read('test'));
echo PHP_EOL;
print_r(opcache_get_status());

这是输出:

bool(true)

array(3) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
  [2]=>
  string(1) "c"
}

Array
(
    [opcache_enabled] => 1
    [cache_full] => 
    [restart_pending] => 
    [restart_in_progress] => 
    [memory_usage] => Array
        (
            [used_memory] => 123832
            [free_memory] => 66748632
            [wasted_memory] => 236400
            [current_wasted_percentage] => 0.35226345062256
        )

    [opcache_statistics] => Array
        (
            [num_cached_scripts] => 1
            [num_cached_keys] => 2
            [max_cached_keys] => 3907
            [hits] => 17
            [start_time] => 1513796280
            [last_restart_time] => 0
            [oom_restarts] => 0
            [hash_restarts] => 0
            [manual_restarts] => 0
            [misses] => 190
            [blacklist_misses] => 0
            [blacklist_miss_ratio] => 0
            [opcache_hit_rate] => 8.2125603864734
        )

    [scripts] => Array
        (
            [C:\DevEnv\htdocs\test.php] => Array
                (
                    [full_path] => C:\DevEnv\htdocs\test.php
                    [hits] => 1
                    [memory_consumption] => 12704
                    [last_used] => Wed Dec 20 20:49:08 2017
                    [last_used_timestamp] => 1513799348
                    [timestamp] => 1513799344
                )

        )

)

OpCache似乎不缓存运行时创建的php文件.唯一的一个缓存文件是 test.php ,请参阅:

OpCache seem not cache the php files created at runtime. The only one cached file is test.php, see:

[scripts] => Array
            (
                [C:\DevEnv\htdocs\test.php] => Array( .. )
            )

已启用php.ini opcache

into php.ini opcache is enabled

[opcache]
zend_extension=C:\DevEnv\PHP\5.6.24\ext\php_opcache.dll
; Determines if Zend OPCache is enabled
opcache.enable=1

; Determines if Zend OPCache is enabled for the CLI version of PHP
opcache.enable_cli=0

; The OPcache shared memory storage size.
;opcache.memory_consumption=64

; The amount of memory for interned strings in Mbytes.
;opcache.interned_strings_buffer=4

; The maximum number of keys (scripts) in the OPcache hash table.
; Only numbers between 200 and 100000 are allowed.
;opcache.max_accelerated_files=2000

; The maximum percentage of "wasted" memory until a restart is scheduled.
;opcache.max_wasted_percentage=5

; When this directive is enabled, the OPcache appends the current working
; directory to the script key, thus eliminating possible collisions between
; files with the same name (basename). Disabling the directive improves
; performance, but may break existing applications.
opcache.use_cwd=1

; When disabled, you must reset the OPcache manually or restart the
; webserver for changes to the filesystem to take effect.
opcache.validate_timestamps=1

; How often (in seconds) to check file timestamps for changes to the shared
; memory storage allocation. ("1" means validate once per second, but only
; once per request. "0" means always validate)
opcache.revalidate_freq=1

; Enables or disables file search in include_path optimization
;opcache.revalidate_path=0

; If disabled, all PHPDoc comments are dropped from the code to reduce the
; size of the optimized code.
;opcache.save_comments=1

; If disabled, PHPDoc comments are not loaded from SHM, so "Doc Comments"
; may be always stored (save_comments=1), but not loaded by applications
; that don't need them anyway.
;opcache.load_comments=1

; If enabled, a fast shutdown sequence is used for the accelerated code
;opcache.fast_shutdown=0

; Allow file existence override (file_exists, etc.) performance feature.
;opcache.enable_file_override=0

; A bitmask, where each bit enables or disables the appropriate OPcache
; passes
;opcache.optimization_level=0xffffffff

;opcache.inherited_hack=1
;opcache.dups_fix=0

; The location of the OPcache blacklist file (wildcards allowed).
; Each OPcache blacklist file is a text file that holds the names of files
; that should not be accelerated. The file format is to add each filename
; to a new line. The filename may be a full path or just a file prefix
; (i.e., /var/www/x  blacklists all the files and directories in /var/www
; that start with 'x'). Line starting with a ; are ignored (comments).
;opcache.blacklist_filename=

; Allows exclusion of large files from being cached. By default all files
; are cached.
;opcache.max_file_size=0

; Check the cache checksum each N requests.
; The default value of "0" means that the checks are disabled.
;opcache.consistency_checks=0

; How long to wait (in seconds) for a scheduled restart to begin if the cache
; is not being accessed.
;opcache.force_restart_timeout=180

; OPcache error_log file name. Empty string assumes "stderr".
;opcache.error_log=

; All OPcache errors go to the Web server log.
; By default, only fatal errors (level 0) or errors (level 1) are logged.
; You can also enable warnings (level 2), info messages (level 3) or
; debug messages (level 4).
;opcache.log_verbosity_level=1

; Preferred Shared Memory back-end. Leave empty and let the system decide.
;opcache.preferred_memory_model=

; Protect the shared memory from unexpected writing during script execution.
; Useful for internal debugging only.
;opcache.protect_memory=0

我做错了什么?

推荐答案

不幸的是,我认为这不会解决.毕竟APCu存在是有原因的.

Unfortunately, I don't think this is going to work out. There's a reason APCu exists, after all.

PHP操作码缓存使用文件时间戳记来确定文件自缓存以来是否已更改;在许多系统上,这些时间戳仅具有1秒的粒度.如果一秒钟内多次修改文件,更改将丢失.

The PHP opcode cache uses file timestamps to determine if a file has been changed since it was cached; on many systems, those timestamps only have a granularity of 1 second. If a file is modified multiple times within one second, the changes will be missed.

这篇关于带有OpCache的PHP用户级缓存(未按预期工作)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆