Alternative PHP Cache (APC) 是一个开放自由的PHP opcode 缓存。它的目标是提供一个自由、 开放,和健全的框架用于缓存和优化PHP的中间代码。它为我们提供了缓存和优化PHP的中间代码的框架。 APC的缓存分两部分:系统缓存和用户数据缓存。
- 系统缓存 它是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存 3600s(一小时)。但是这样仍会浪费大量CPU时间。因此可以在php.ini中设置system缓存为永不过期(apc.ttl=0)。不过如果这 样设置,改运php代码后需要重启WEB服务器。目前使用较多的是指此类缓存。
- 用户数据缓存 缓存由用户在编写PHP代码时用apc_store和apc_fetch函数操作读取、写入的。如果数据量不大的话,可以一试。如果数据量大,使用类似memcache此类的更加专著的内存缓存方案会更好。
在APC中我们也可以享受APC带来的缓存大文件上传进度的特性,需要在php.ini中将apc.rfc1867设为1,并且在表单中加一个隐藏域 APC_UPLOAD_PROGRESS,这个域的值可以随机生成一个hash,以确保唯一。
APC与PHP内核的交互
APC是作为一个扩展添加到PHP体系中的。因此,按照PHP的扩展规范,它会有PHP_MINIT_FUNCTION、 PHP_MSHUTDOWN_FUNCTION、PHP_RINIT_FUNCTION、PHP_RSHUTDOWN_FUNCTION等宏定义的函数。 在PHP_MINIT_FUNCTION(apc)中有调用apc_module_init中,并且在此函数中通过重新给 zend_compile_file赋值以替换系统自带的编译文件过程,从而将APC自带的功能和相关数据结构插入到整个PHP的体系中。
这里会有一个问题,如果出现多个zend_compile_file的替换操作呢?在实际使用过程,这种情况会经常出现,比如当我们使用 xdebug扩展时,又使用了apc,此时PHP是怎么处理的呢?不管是哪个扩展,在使用zend_compile_file替换时,都会有一个自己的 compile_file函数(替换用),还有一个作用域在当前扩展的,一个旧的编译函数:old_compile_file。相当于每个扩展当中都保留 了一个对于前一个编译函数的引用,形成一个单向链表。并且,所有最终的op_array都是在新的zend_compile_file中通过 old_compile_file生成,即都会沿着这条单向链表,将编译的最终过程传递到PHP的zend_compile_file实现。在传递过程 中,每经过一个节点,这些节点都会增加一些属于自己的数据结构,以实现特定的需求。
APC内部存储结构
在APC内部,对于系统缓存和用户缓存分别是以两个全局变量存储,从代码逻辑层面就隔离了两种缓存,当然,这两种存储的实现过程和数据结构是一样的,它们都是apc_cache_t类型,如下:
/* {{{ struct definition: apc_cache_t */ struct apc_cache_t { void* shmaddr; 共享缓存的本地进程地址 cache_header_t* header; 缓存头,存储在共享内存中 slot_t** slots; 缓存的槽数组,存储在共享内存中 int num_slots; 存储在缓存中的槽个数 int gc_ttl; GC列表中槽的最大生存时间 int ttl; 如果对槽的访问时间大于这个TTL,需要则移除这个槽 apc_expunge_cb_t expunge_cb; /* cache specific expunge callback to free up sma memory */ uint has_lock; 为可能存在的造成同一进程递归锁而存在的标记 /* flag for possible recursive locks within the same process */ }; /* }}} */
对于一个缓存,apc_cache_t类型的变量是其入口,它包含了这个缓存的一些全局信息。每个缓存都会有多个缓存槽,包含在slots字段 中,slots的个数包含在num_slots字段,槽的过程时间控制在于ttl字段。对于用户缓存和系统缓存,默认情况下系统缓存数量为1000,实际 上APC创建了1031个,也就是说默认情况下APC最少可以缓存1031个文件的中间代码。当然这个值还需要考虑内存大小,计算slot的key后的分 布等等。更多的关于缓存的统计信息存储在header字段中,header字段结构为cache_header_t,如下:
struct cache_header_t { apc_lck_t lock; 读写锁,独占阻塞缓存锁 apc_lck_t wrlock; 写锁,为防止缓存爆满 unsigned long num_hits; 缓存命中数 unsigned long num_misses; 缓存未命中数 unsigned long num_inserts; 插入缓存总次数 unsigned long expunges; 清除的总次数 slot_t* deleted_list; 指向被清除的槽的链表 time_t start_time; 以上计数器被重置的时间 zend_bool busy; 当apc在忙于清除缓存时告诉客户端此时状态的标记 int num_entries; 统计的实体数 size_t mem_size; 统计的被用于缓存的内存大小 apc_keyid_t lastkey; 用户缓存最后一写入的key };
一个缓存包含多个slots,每个slot都是一个slot结构体的变量,其结构如下:
struct slot_t { apc_cache_key_t key; 槽的key apc_cache_entry_t* value; 槽的值 slot_t* next; 链表中的下一个槽 unsigned long num_hits; 这个bucket的命中数/* number of hits to this bucket */ time_t creation_time; 槽的初始化时间 time_t deletion_time; 槽从缓存被移除的时间 /* time slot was removed from cache */ time_t access_time; 槽的最后一次被访问的时间 };
每个槽包含一个key,以apc_cache_key_t结构体存储;包含一个值,以apc_cache_entry_t结构体存储。如下:
typedef struct apc_cache_key_t apc_cache_key_t; struct apc_cache_key_t { apc_cache_key_data_t data; unsigned long h; /* pre-computed hash value */ time_t mtime; /* the mtime of this cached entry */ unsigned char type; unsigned char md5[16]; /* md5 hash of the source file */ };
结构说明如下:
- data字段 apc_cache_key_data_t类型,一个联合体,存储key的关联信息,比如对于系统缓存,其可能会存储文件的路径或OS的文件device/inode;对于用户缓存可能会存储用户给定的标识或标识长度。
- h字段 文件完整路径或用户给定的标识的hash值,使用的hash算法为PHP自带的time33算法;或者文件所在device和inode的和
- mtime字段 缓存实体的修改时间
- type字段 APC_CACHE_KEY_USER:用户缓存; APC_CACHE_KEY_FPFILE:系统缓存(有完整路径); APC_CACHE_KEY_FILE: 系统缓存(需要查找文件)
- md5字段 文件内容的MD5值,这个字段与前面四个字段不同,它是可选项,可以通过配置文件的apc.file_md5启用或禁用。并且这个值是在初始化实体时创建 的。看到这里源文件的md5值,想起之前做过一个关于MySQL数据表中访问路径查询的优化,开始时通过直接查询路径字段,在数据量达到一定级别时,出现 了就算走索引还是会很慢的情况,各种方案测试后,采用了以新增一个关于访问路径的md5值查询解决。
除了入口,APC在最终的数据存储上对于系统缓存和用户缓存也做了区分,在_apc_cache_entry_value_t分别对应file和user。
typedef union _apc_cache_entry_value_t { struct { char *filename; /* absolute path to source file */ zend_op_array* op_array; 存储中间代码的op_array apc_function_t* functions; /* array of apc_function_t's */ apc_class_t* classes; /* array of apc_class_t's */ long halt_offset; /* value of __COMPILER_HALT_OFFSET__ for the file */ } file; file结构体 系统缓存所用空间,包括文件名,, struct { char *info; int info_len; zval *val; unsigned int ttl; 过期时间 } user; ser结构体 用户缓存所用空间 } apc_cache_entry_value_t;
如图所示:
初始化
在APC扩展的模块初始化函数(PHP_MINIT_FUNCTION(apc))中,APC会调用apc_module_init函数初始化缓存 所需要的全局变量,如系统缓存则调用apc_cache_create创建缓存全局变量apce_cache,默认情况下会分配1031个slot所需要 的内存空间,用户缓存也会调用同样的方法创建缓存,存储在另一个全局变量apc_user_cache,默认情况下会分配4099个内存空间。这里分配的 空间的个数都是素数,在APC的代码中有一个针对不同数量的素数表primes(在apc_cache.c文件)。素数的计算是直接遍历素数表,找到表中 第一个比需要分配的个数大的素数。
缓存key生成规则
APC的缓存中的每个slot都会有一个key,key是 apc_cache_key_t结构体类型,除了key相关的属性,关键是h字段的生成。 h字段决定了此元素落于slots数组的哪一个位置。对于用户缓存和系统缓存,其生成规则不同。
- 用户缓存通过apc_cache_make_user_key函数生成key。通过用户传递进来的key字符串,依赖PHP内核中的hash函数(PHP的hashtable所使用的hash函数:zend_inline_hash_func),生成h值。
- 系统缓存通过apc_cache_make_file_key函数生成key。通过APC的配置项apc.stat的开关来区别对待不同的方案。 在打开的情况下,即 apc.stat= On 时,如果被更新则自动重新编译和缓存编译后的内容。此时的h值是文件的device和inode相加所得的值。在关闭的情况下,即 apc.stat=off时,当文件被修改后,如果要使更新的内容生效,则必须重启Web服务器。此时h值是根据文件的路径地址生成,并且这里的路径是绝 对路径。即使你是使用的相对路径,也会查找PG(include_path)定位文件,以取得绝对路径,所以使用绝对路径会跳过检查,可以提高代码的效 率。
添加缓存过程
以用户缓存为例,apc_add函数用于给APC缓存中添加内容。如果key参数为字符串中,APC会根据此字符串生成key,如果key参数为数 组,APC会遍历整个数组,生成key。根据这些key,APC会调用_apc_store将值存储到缓存中。由于这是用户缓存,当前使用的缓存为 apc_user_cache。执行写入操作的是apc_cache_make_user_entry函数,其最终调用 apc_cache_user_insert执行遍历查询和写入操作。与此对应,系统缓存使用apc_cache_insert执行写入操作,其最终都会 调用_apc_cache_insert。
不管是用户缓存还是系统缓存,大体的执行过程类似,步骤如下:
- 通过求余操作,定位当前key的在slots数组中的位置: cache->slots[key.h % cache->num_slots];
- 在定位到slots数组中的位置后,遍历当前key对应的slot链表,如果存在slot的key和要写入的key匹配或slot过期,清除当前slot。
- 在最后一个slot的后面插入新的slot。
原文:http://www.phppan.com/2012/06/php-opcode-cache-apc-1/
相关推荐
APC(Alternative PHP Cache)是PHP的一个非常重要的扩展,它主要功能是对PHP代码进行编译缓存,以提升PHP应用程序的运行效率。在标题中提到的"php_apc-3.1.10-5.3-vc9-x86.zip"是一个专为Windows平台设计的APC扩展...
APC(Alternative PHP Cache)是PHP的一个流行缓存和优化工具,它能够加速PHP脚本的执行速度,通过将编译后的PHP代码存储在共享内存中,避免了每次请求时重复编译的过程。版本号 "3.1.10" 表示这是APC的第3.1.10次...
- **变量缓存**:可以将PHP变量存储在共享内存中,减少数据库查询和其他昂贵操作的次数。 - **缓存清理策略**:APC自动管理缓存空间,包括LRU(Least Recently Used)策略,当内存不足时,会删除最近最少使用的脚本...
这个类提供了缓存操作的基本框架,包括读取、存储、删除缓存项等核心功能,并且还引入了一些高级功能如锁定和解锁缓存项。下面详细介绍这些方法: #### 1. `fetch($key)` 该方法用于从缓存中获取一个特定键(`$key`...
本文将对五种常见的PHP缓存加速器进行分析,包括Memcached、eAccelerator、APC(Alternative PHP Cache)、Xcache以及Redis,以帮助理解它们的特点和适用场景。 首先,Memcached是一个分布式内存对象缓存系统,常...
这种格式是 tar 和 gzip 压缩算法的组合,通常用于在 Linux 或类 Unix 系统中存储和分发软件。".tgz" 文件可以包含多个文件和目录,保持原有的文件系统结构。 描述中的 "apc加速" 暗示这个 APC 是 Advanced PHP ...
APC(Alternative PHP Cache)是一个广泛使用的PHP缓存和优化器,它能够存储预编译的PHP脚本到共享内存中,以减少每次请求时的解析和编译时间,从而提高整体性能。APC不仅缓存了PHP的opcode,还支持用户级别的变量...
本文将深入探讨标题"php memcache php_apc php_file 缓存插件"所涉及的三个主要缓存机制:文件缓存、Memcache和APC(Alternative PHP Cache),以及它们在PHP中的应用。 1. 文件缓存: 文件缓存是最基础的缓存方式...
2. **缓解 IO 压力:**缓存能够减轻数据库和其他数据存储的压力,降低 I/O 操作频率,进而提高整体系统的吞吐量和响应时间。 #### 三、常见的缓存类型 1. **数据库查询缓存:**这是一种专门针对数据库查询结果的...
1. **下载**:首先,你需要从官方或可靠的第三方源获取适用于PHP 5.3的APC扩展包,确保与你的PHP版本和操作系统相匹配。 2. **放置**:将 `php_apc.dll` 文件放入PHP的 `ext` 目录下。 3. **配置**:打开PHP的配置...
- **代码缓存**:APC将PHP脚本的解析结果存储在内存中,避免了每次请求时重复解析PHP源代码的过程,显著提升了处理速度。 - **数据共享**:APC提供了一个共享内存空间,允许PHP脚本之间快速交换数据,减少了数据库...
PHP缓存技术是提高PHP应用性能的重要手段。它通过减少服务器的计算量、数据库查询次数以及网络传输数据量,来实现提高响应速度和降低服务器负载的目的。下面将详细解析php缓存技术涉及的关键知识点。 一、全页面...
这样,当PHP脚本被首次执行并编译成中间语言(opcode)后,后续的请求可以直接使用缓存的opcode,减少了服务器的CPU使用率和I/O操作。 2. **共享内存**:APC扩展利用共享内存来存储缓存数据,使得多个PHP进程可以...
1. **使用APC缓存**:开发人员可以通过`apc_add`和`apc_fetch`等函数来操作缓存。例如: ```php $data = apc_fetch('my_key'); if (!$data) { $data = 'Hello, World!'; apc_store('my_key', $data); } echo ...
在PHP 5.4版本中,Alternative PHP Cache (APC)是一个非常重要的性能优化工具,它是一个免费且开源的缓存和数据存储机制,主要针对PHP的opcode进行缓存,从而加速PHP应用程序的执行速度。 **APC扩展的作用** 1. **...
APC不仅缓存了PHP的opcode(中间代码),还支持用户数据缓存,可以用于存储应用程序的数据,类似于一个内置的key-value存储系统。在使用APC时,开发者需要注意配置合适的缓存大小、清理策略以及与其他缓存机制的兼容...
- 用户友好的CLI:`cachetool` 命令行工具提供了简洁的命令结构,使得操作缓存变得简单直观,如`cachetool apc:clear` 或 `cachetool opcache:reset`。 - 自定义配置:用户可以根据自己的需求自定义配置文件,调整...
缓存分为内存缓存(如APC, Memcached, Redis)和文件缓存。文件缓存是将数据保存在本地文件系统中,优点是无需额外的硬件资源,缺点是读写速度相对较慢。在PHP中,我们主要讨论文件缓存的实现。 2. 生成缓存文件的...
它还提供了用户变量缓存的功能,允许开发者将常用数据存储在内存中,减少对硬盘I/O操作,进一步提升性能。 APC不仅包含一个编译后缓存,还提供了共享内存空间供PHP脚本使用,使得多个请求可以共享同一份数据,这...
APC 的默认设置将允许您为操作码缓存和用户缓存组合存储 32 MiB。 确保根据您网站的需要进行调整。 一个示例配置可以是在 APC 中缓存“cache”和“cache_bootstrap”,在 Memcached 中缓存“cache_field”和“cache...