一、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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// APCu的数据存储中检索缓存的信息
apcu_cache_info();

// 检索APCu共享内存分配信息
apcu_sma_info();

// APCu key信息
apcu_key_info();

// 当前环境APCu是否可用
apcu_enabled();

// 设置一个缓存,没有失效时间。再次apcu_add()同一个key,值不会覆盖
apcu_add();

// 删除指定key
apcu_delete();

// 获取指定key缓存
apcu_fetch();

// 设置一个缓存,带有失效时间。失效后还会占用内存空间,需使用apcu_delete()才可以彻底删除。
apcu_store();

// 更新一个key的值
apcu_cas();

// 自增
apcu_inc();

// 自减
apcu_dec();

// 判断key是否存在
apcu_exists();

// 以原子方式获取或生成缓存
apcu_entry();

// 清除全部缓存
apcu_clear_cache();

三、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

参考文章:

一文读懂 mmap 原理

mmap原理

认真分析mmap:是什么 为什么 怎么用

1、mmap原理

一般来说,修改一个文件的内容需要如下3个步骤:

把文件内容读入到内存中。

修改内存中的内容。

把内存的数据写入到文件中。

1
2
3
read(fd, buf, 1024);  // 读取文件的内容到buf
... // 修改buf的内容
write(fd, buf, 1024); // 把buf的内容写入到文件

Refernece

  1. PHP常用扩展(一) PHP字节码缓存——Opcache

  2. PHP常用扩展(二) PHP用户级缓存——APCu

  3. 一文读懂 mmap 原理

  4. mmap原理

  5. 认真分析mmap:是什么 为什么 怎么用

写在最后

欢迎大家关注鄙人的公众号【麦田里的守望者zhg】,让我们一起成长,谢谢。
微信公众号