一、APC 可选PHP缓存
APC,全称是Alternative PHP Cache,官方翻译叫”可选PHP缓存”。它为我们提供了缓存和优化PHP的中间代码的框架。 APC的缓存分两部分:系统缓存和用户数据缓存。
系统缓存是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存 3600s。但是这样仍会浪费大量CPU时间。因此可以在php.ini中设置system缓存为永不过期(apc.ttl=0)。不过如果这样设置,改变php代码后需要重启WEB服务器。apc系统缓存目前已经不在维护,如需缓存php字节码可使用opcache扩展。
用户数据缓存由用户在编写PHP代码时用apc_store和apc_fetch函数操作读取、写入的键值对缓存。如果数据量不大的话,可以一试。如果数据量大或者需要跨机器缓存,使用类似memcache或redis等更加专著的内存缓存方案会更好。
APC 扩展其实都是基于 opcode caching ,也就是 PHP 自身的 opcode 来实现的缓存能力。
二、APCu
APCu 是 PHP 版的内存键值存储。 键是 string 类型且值可以为 PHP 任何变量。 APCu 仅支持用户空间(userland)级别的变量缓存。
apcu是基于共享内存技术建设的,多个cgi进程之间访问apcu中的cache可以完全等同于访问自己进程的一块内存一样,不需要发任何的网络请求。而redis和memcache等独立服务的缓存需要进行网络请求,因此apcu的速度远高于redis。
apcu的适用场景和局限性:
1、以扩展的方式接入,跟php这门语言有很强的耦合,而redis作为独立服务存在,使用协议接入
2、apcu受限于单机内存的限制,扩展受阻,而目前的redis集群模式已经可以做到动态扩容,理论上无容量风险。
3、apcu数据存于单机内存,多机器之间的数据无法共享,这使其使用场景有限。
所以一般用apcu来缓存数据量小、但是读取量大或者瞬间读取量大的场景,例如对redis中的热数据做二级缓存以避免缓存雪崩带来的影响和缓解redis服务器的压力。
APCu相关ini配置:
具体配置含义可以参考
https://www.php.net/manual/zh/apcu.configuration.php#ini.apcu.shm-segments
这里只介绍几个最常用的配置:
apc.enabled 开启apc 设置为0关闭,1为开启
apc.shm_segments 共享内存块数
apc.shm_size 共享内存大小,可以设置1G那么显然共享内存的总数就是apc.shm_segments*apc.shm_size
apc.num_files_hint 允许多少个opcode被缓存(也就是项目里面有几个php文件)
apc.ttl opcode缓存的过期时间,设置为0表示不过期
apc.enable_cli apcu缓存是否在cli模式可用,默认不可用。
1 | // APCu的数据存储中检索缓存的信息 |
三、APCu内存共享
APCu用户缓存默认使用mmap实现,因此APCu内的数据可以作为共享内存被多个进程读写。
需要注意:APCu段中的数据只能被父子进程共享,而无法被两个独立的进程共享。
在CLI模式下,即使开启了APCu用户缓存,两个独立的cli进程之间也无法通过APCu共享内存。
在FPM模式下,一个进程往APCu内存段中存储的数据可以被另一个fpm进程访问到,因为这些fpm worker进程都是由fpm master进程fork出来的。
master进程在创建时就已经向系统申请内存通过mmap映射的方式创建了一个APCu用户缓存。
master进程fork子进程时,该APCu用户缓存区域会映射到所有的worker子进程的虚拟内存空间中,这些worker进程通过该ACPu内存段的指针对用户缓存进行共享。
四、关于mmap
参考文章:
1、mmap原理
一般来说,修改一个文件的内容需要如下3个步骤:
把文件内容读入到内存中。
修改内存中的内容。
把内存的数据写入到文件中。
1 | read(fd, buf, 1024); // 读取文件的内容到buf |
Refernece
写在最后
欢迎大家关注鄙人的公众号【麦田里的守望者zhg】,让我们一起成长,谢谢。