• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • opcache 优化

    OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码的好处就是省去了每次加载和解析 PHP 脚本的开销。

    PHP-FPM + Nginx 的工作机制

    在理解 OPCache 功能之前,我们有必要先理解 PHP-FPM + Nginx 的工作机制,以及 PHP 脚本解释执行的机制。请求从 Web 浏览器到 Nginx,再到 PHP 处理完成,一共要经历如下五个步骤:

    • 第一步:启动服务
      • 启动 PHP-FPM。PHP-FPM 支持两种通信模式:TCP socket 和 Unix socket。
      • PHP-FPM 会启动两种类型的进程:Master 进程和 Worker 进程,前者负责监控端口、分配任务、管理 Worker 进程;后者就是 PHP 的 cgi 程序,负责解释编译执行 PHP 脚本。
      • 启动 Nginx。首先会载入 ngx_http_fastcgi_module 模块,初始化 FastCGI 执行环境,实现 FastCGI 协议请求代理。
      • 注意:fastcgi的worker进程(cgi进程),是由PHP-FPM来管理,不是Nginx。Nginx只是代理。
    • 第二步:Request => Nginx
      • Nginx 接收请求,并基于 location 配置,选择一个合适 handler。
      • 这里就是代理 PHP 的 handler。
    • 第三步:Nginx => PHP-FPM
      • Nginx 把请求翻译成 fastcgi 请求。
      • 通过 TCP socket 或者 Unix Socket 发送给 PHP-FPM 的 master 进程。
    • 第四步:PHP-FPM Master => Worker
      • PHP-FPM master 进程接收到请求。
      • 分配 Worker 进程执行 PHP 脚本,如果没有空闲的 Worker,返回 502 错误。
      • Worker(php-cgi)进程执行 PHP 脚本,如果超时,返回 504 错误。
      • 处理结束,返回结果。
    • 第五步:PHP-FPM Worker => Master => Nginx
      • PHP-FPM Worker 进程返回处理结果,并关闭连接,等待下一个请求。
      • PHP-FPM Master 进程通过 Socket 返回处理结果。
      • Nginx Handler 顺序将每一个响应 buffer 发送给第一个 filter →第二个→以此类推→最终响应发送给客户端。


    opcache 运行原理

    OPCache 是 Zend 官方出品的,开放自由的 opcode 缓存扩展,还具有代码优化功能,省去了每次加载和解析 PHP 脚本的开销。PHP 5.5.0 及后续版本中已经绑定了 OPcache 扩展。

    缓存两类内容:

    • OPCode
    • Interned String,如注释、变量名等

    OPCache 缓存的机制主要是:将编译好的操作码放入共享内存,提供给其他进程访问。这里就涉及到内存共享机制,另外所有内存资源操作都有锁的问题,我们一一解读。

    1、共享内存

    UNIX/Linux 系统提供很多种进程间内存共享的方式:

    1. System-V shm API: System V 共享内存。sysv shm 是持久化的,除非被一个进程明确的删除,否则它始终存在于内存里,直到系统关机;
    2. mmap API:
      • mmap 映射的内存在不是持久化的,如果进程关闭,映射随即失效,除非事先已经映射到了一个文件上。
      • 内存映射机制 mmap 是 POSIX 标准的系统调用,有匿名映射和文件映射两种。
      • mmap 的一大优点是把文件映射到进程的地址空间。
      • 避免了数据从用户缓冲区到内核 page cache 缓冲区的复制过程。
      • 当然还有一个优点就是不需要频繁的 read/write 系统调用。
    3. POSIX API:System V 的共享内存是过时的, POSIX 共享内存提供了使用更简单、设计更合理的 API。
    4. Unix socket API:OPCache 使用了前三个共享内存机制,根据配置或者默认 mmap 内存共享模式。依据PHP字节码缓存的场景,OPCache 的内存管理设计非常简单,快速读写,不释放内存,过期数据置为 Wasted。当Wasted内存大于设定值时,自动重启 OPCache 机制,清空并重新生成缓存。

    5. 2、互斥锁

      任何内存资源的操作,都涉及到锁的机制。共享内存:一个单位时间内,只允许一个进程执行写操作,允许多个进程执行读操作;写操作同时,不阻止读操作,以至于很少有锁死的情况。这就引发另外一个问题:新代码、大流量场景,进程排队执行缓存opcode操作;重复写入,导致资源浪费。


      OPCache 的配置

      内存配置

      • opcache.preferred_memory_model="mmap":OPcache 首选的内存模块。如果留空,OPcache 会选择适用的模块,通常情况下,自动选择就可以满足需求。可选值包括:mmap,shm, posix 以及 win32。
      • opcache.memory_consumption=64:OPcache 的共享内存大小,以兆字节为单位,默认 64M。
      • opcache.interned_strings_buffer=4:用来存储临时字符串的内存大小,以兆字节为单位,默认 4M。
      • opcache.max_wasted_percentage=5:浪费内存的上限,以百分比计。如果达到此上限,那么 OPcache 将产生重新启动续发事件。默认 5。


      允许缓存的文件数量以及大小

      • opcache.max_accelerated_files=2000:OPcache 哈希表中可存储的脚本文件数量上限。真实的取值是在质数集合{223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987}中找到的第一个大于等于设置值的质数。设置值取值范围最小值是 200,最大值在 PHP 5.5.6 之前是 100000,PHP 5.5.6 及之后是 1000000。默认值 2000。
      • opcache.max_file_size=0:以字节为单位的缓存的文件大小上限。设置为 0 表示缓存全部文件。默认值 0。


      注释相关的缓存

      • opcache.load_commentsboolean:如果禁用,则即使文件中包含注释,也不会加载这些注释内容。本选项可以和 opcache.save_comments 一起使用,以实现按需加载注释内容。
      • opcache.fast_shutdown boolean:如果启用,则会使用快速停止续发事件。所谓快速停止续发事件是指依赖 Zend 引擎的内存管理模块一次释放全部请求变量的内存,而不是依次释放每一个已分配的内存块。


      二级缓存的配置

      • opcache.file_cache:配置二级缓存目录并启用二级缓存。启用二级缓存可以在 SHM 内存满了、服务器重启或者重置 SHM 的时候提高性能。默认值为空字符串"",表示禁用基于文件的缓存。
      • opcache.file_cache_onlyboolean:启用或禁用在共享内存中的 opcode 缓存。
      • opcache.file_cache_consistency_checksboolean:当从文件缓存中加载脚本的时候,是否对文件的校验和进行验证。
      • opcache.file_cache_fallbackboolean:在 Windows 平台上,当一个进程无法附加到共享内存的时候,使用基于文件的缓存,也即:opcache.file_cache_only=1。需要显示的启用文件缓存。


      OPCache 配置说明

      [opcache]
      ; 是否快开启opcache缓存。
      ;opcache.enable=1
      
      ; 是否在cli模式下开启opcache。
      ;opcache.enable_cli=1
      
      ; opcache共享内存的大小(单位是M)。
      ;opcache.memory_consumption=128
      
      ; 预留字符串的的内存大小(单位是M)。
      ;opcache.interned_strings_buffer=8
      
      ; 在hash表中存储的最大脚本文件数量,范围是200到1000000之间。实际的情况是在{ 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987 }中找到第一个大于等于设置值的质数。最小范围是200。
      ;opcache.max_accelerated_files=10000
      
      ; 浪费内存的上线,如果超过这个上线,opcache将重新启动。
      ;opcache.max_wasted_percentage=5
      
      ; 如果启用,opcache将会在hash表的脚本键后面增加一个文件目录,避免吃同名的脚本产生冲突。禁用的话可以提高性能,但是也容易导致应用不可用。
      ;opcache.use_cwd=1
      
      ; 如果启用(1),opcache会每隔设置的值时间来判断脚本是否更新。如果禁用(0),则不会自动检测脚本更新,必须通过重启PHP服务,或者使用opcache_reset()、opcache_invalidate()函数来刷新缓存。
      ;opcache.validate_timestamps=1
      
      ; opcache检查脚本是否更新的时间周期(单位是秒),如果设置为0则会针对每一个请求进行检查更新,如果validate_timestamps=0,该值不会生效。
      ;opcache.revalidate_freq=60
      
      ; 如果禁用,在统一include_path下面已经缓存的文件将被重用,因此无法找到该路径下的同名文件。
      ;opcache.revalidate_path=0
      
      ; 是否保存PHP脚本中的注释内容。禁用,则不会缓存PHP代码中的注释,可以减少文件中的体积,但是一些依赖注释或者注解将无法使用。
      ;opcache.save_comments=1
      
      ; 如果启用,则会使用快速停止续发事件。 所谓快速停止续发事件是指依赖 Zend 引擎的内存管理模块 一次释放全部请求变量的内存,而不是依次释放每一个已分配的内存块。
      ; 在php7.2.0开始,被移除,这类说的事件将会在PHP中自动处理。
      ;opcache.fast_shutdown=1
      
      ; 如果启用,在调用file_exists()、is_file()和is_readable()函数时,不管文件是否被缓存,都会检测操作码。如果禁用,可能读取的内容是一些旧数据。
      ;opcache.enable_file_override=0
      
      ; 控制优化级别,是一个二进制的位的掩码。
      ;opcache.optimization_level=0xffffffff
      
      ; 不进行编译优化的配置文件路径。该文件中配置具体哪些不被编译的文件。如果文中每行的开头是";"开头,则会被视为注释。黑名单中的文件名,可以是通配符,也可以使用前缀。
      ; 例如配置文件的路径是"/home/blacklist.txt",则该配置的值就是该路径。
      ; 配置的内容可以是如下格式
      
      ; 这是一段注释,在解析的时候因为开头是;,则会被视为注释
      ;/var/www/a.php
      ;/var/www/a/b.php
      
      ;opcache.blacklist_filename=
      
      ; 以字节为单位的缓存的文件大小上限。设置为 0 表示缓存全部文件。
      ;opcache.max_file_size=0
      
      ; 每个N次请求会检查缓存校验和,0是不检查。该项对性能有较大影响,尽量在调试环境中使用。
      ;opcache.consistency_checks=0
      
      ; 如果缓存处于非激活状态,等待多少秒之后计划重启。 如果超出了设定时间,则 OPcache 模块将杀除持有缓存锁的进程, 并进行重启。
      ;opcache.force_restart_timeout=180
      
      ; 错误日志文件位置,不填写将默认输出到服务器的错误日志文件中。
      ;opcache.error_log=
      
      ; 错误日志文件等级。
      ; 默认情况下,仅有致命级别(0)及错误级别(1)的日志会被记录。 其他可用的级别有:警告(2),信息(3)和调试(4)。
      ; 如何设置的是1以上,在进行force_restart_timeout选项时,会将错误日志中插入一条警告信息。
      ;opcache.log_verbosity_level=1
      
      ; opcache首选的内存模块,不配置则自动选择。可以选择的值有mmap,shm, posix 以及 win32。
      ;opcache.preferred_memory_model=
      
      ; 保护共享内存,以避免执行脚本时发生非预期的写入。 仅用于内部调试。
      ;opcache.protect_memory=0
      
      ; 只允许指定字符串开头的PHP脚本调用opcache api函数,默认不做限制。
      ;opcache.restrict_api=
      
      ; 在 Windows 平台上共享内存段的基地址。 所有的 PHP 进程都将共享内存映射到同样的地址空间。 使用此配置指令避免“无法重新附加到基地址”的错误。
      ;opcache.mmap_base=
      
      ; 配置二级缓存目录并启用二级缓存。 启用二级缓存可以在 SHM 内存满了、服务器重启或者重置 SHM 的时候提高性能。 默认值为空字符串 "",表示禁用基于文件的缓存。
      ;opcache.file_cache=
      
      ; 启用或禁用在共享内存中的 opcode 缓存。
      ;opcache.file_cache_only=0
      
      ; 当从文件缓存中加载脚本的时候,是否对文件的校验和进行验证。
      ;opcache.file_cache_consistency_checks=1
      
      ; 在 Windows 平台上,当一个进程无法附加到共享内存的时候, 使用基于文件的缓存。需要开启opcache.file_cache_only选项。建议开启此选项,否则可能导致进程无法启动。
      ;opcache.file_cache_fallback=1
      
      ; 启用或者禁用将 PHP 代码(文本段)拷贝到 HUGE PAGES 中。 此项配置指令可以提高性能,但是需要在 OS 层面进行对应的配置。
      ;opcache.huge_code_pages=1
      
      ; 针对当前用户,验证缓存文件的访问权限。
      ;opcache.validate_permission=0
      
      ; 在 chroot 的环境中避免命名冲突。 为了防止进程访问到 chroot 环境之外的文件,应该在 chroot 的情况下启用这个选项。
      ;opcache.validate_root=0