[转]php加速 PHP APC 浅析
PHP APC提供两种缓存功能,即缓存Opcode(目标文件),我们称之为apc_compiler_cache。同时它还提供一些接口用于PHP开发人员将用户数据驻留在内存中,我们称之为apc_user_cache。我们这里主要控讨php-apc的配置。
安装PHP APC
作为测试环境,我们这里使用的是CentOS5.3(2.6.18-128.el5PAE) + Apache2.0(prefork) + php5.2。我们可以去pecl apc下载APC-3.0.19.tgz
# tar -xzvf APC-3.0.19.tgz #cd APC-3.0.19 # /usr/bin/phpize # ./configure --enable-apc --enable-mmap --enable-apc-spinlocks --disable-apc-pthreadmutex #make #make install
注意:我们这里支持mmap,同时采用spinlocks自旋锁。Spinlocks是Facebook推荐使用,同时也是APC开发者推荐使用的锁机制。
PHP APC 配置参数
如果你使用的系统环境跟我的测试环境是一样的话,可以在/etc/php.d目录下创建文件apc.ini,并且相关配置写入/etc/php.d/apc.ini文件。这里,我们挑了一些常用到的配置,并进行探讨。把相关的配置放在一起解释。
apc.enabled=1
apc.enabled默认值是1,你可设成0禁用APC。如果你设置为0的时候,同样把extension=apc.so也注释掉(这样可以节约内存资源)。一旦启用了APC功能,则会缓存Opcodes到共享内存。
apc.shm_segments = 1
apc.shm_size = 30
APC既然把数据缓存在内存里面,我们就有必要对它进行内存资源限定。通过这二个配置可以限定APC可以使用的内存空间大小。 apc.shm_segments指定了使用共享内存块数,而apc.shm_size则指定了一块共享内存空间大小,单位是M。所以,允许APC使用的内存大小应该是 apc.shm_segments * apc.shm_size = 30M。你可以调整一块共享内存的大小空间。当然,一块共享内存最大值是受操作系统限制的,即不能超过/proc/sys/kernel/shmmax大小。否则APC创建共享内存的时候,会失败。在apc.shm_size达到了上限的时候,你可以通过设置apc.shm_segments来允许APC 使用更多的内存空间。我们推荐,如果调用APC使用内存空间的话,先考滤apc.shm_size,后考滤apc.shm_segments。具体数值,可以根据apc.php监控情况进行规划与调整。值得注意的是,每一次调整需要重启httpd守护进程,这样可以重新加载apc.so模块。跟随着 httpd守护进程启动,apc.so模块就会加载。apc.so加载初始化的时候,通过mmap请求分配内存指定大小的内存,即 apc.shm_size * apc.shm_segments。而且,这里使用的是匿名内存映射方式,通过映射一个特殊设备/dev/zero,提供一个“大型”的,填满了零的内存供APC管理。
为了验证以上陈述,我们注释掉apc.ini配置,并且写了以下php脚本观察apc.so模块初始化的分配的内存空间。
<?php //@file: apc_load.php if (!extension_loaded('apc')) { dl('apc.so'); #加载apc.so模块 echo posix_getpid(); #//输出当前进程的pid,我这里这里输出的是14735 ob_flush(); flush(); sleep(3600); #让进程进入休眠状态.这样,我们可以观察内存分配情况 } ?>
#strace -p `cat /var/run/httpd.pid`
open("/var/www/html/apc_load.php", O_RDONLY) = 13
...
mmap2(NULL, 31457280, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) = 0xb5ce7000
...
nanosleep({3600, 0},
红色部分,我们可以看出。通过mmap系统内核调用分配了30M(31457280/1024/1024)内存空间。 PROT_READ|PROT_WRITE表示该内存空间可供读取与写入。MAP_SHARED表示该内存空间与其它进程是共享的,即其它进程也可以进行读取与写入,我们可以通过apc.php进行管理该块内存空间亦是受益于此设定。MAP_ANONYMOUS则表示匿名映射。其中fd=-1表示忽略,因为这里映射的特殊设备/dev/zero。最后的0表示无偏移量。我们还可以通过进程映像文件查看该块内存的具体情况
#cat /proc/14735/smaps
b5ce7000-b7ae7000 rw-s 00000000 00:08 633695 /dev/zero (deleted)
Size: 30720 kB
Rss: 44 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 44 kB
可以很容易地发现起始地址0xb5ce7000与上面mmap系统内核调用返回的地址一样。该块内存是可读写rw,并与其它进程共享s。而/dev /zero则是映射文件,该文件节点是633695。其中,size表示进程可以使用的内存空间,而rss则表示实际分配的内存空间,且由 Private_Dirty可以看出,实际分配的44kb内存是由当前进程自己分配的。
apc.num_files_hint = 1000
apc.user_entries_hint = 4096
这二配置指定apc可以有多少个缓存条目。apc.num_files_hint说明你估计可能会有多少个文件相应的opcodes需要被缓成,即大约可以有多少个apc_compiler_cache条目。另外apc.user_entries_hint则说明你估计可能会有多少个 apc_userdata_cache条目需要被缓存。如果项目中不使用apc_store()缓存用户数据的话,该值可以设定得更小。也就是说 apc.num_files_hint与apc.user_entries_hint之和决定了APC允许最大缓存对象条目的数量。准确地设置这二个值可以得到最佳查询性能。当然,如果你不清楚要进行多少缓存(缓存对象实例)的情况下,你可以不必修改这二项配置。
其中apc.user_entries_hint要根据项目实际开发使用了apc_store()条目估计其值大小。相较而言,apc.num_files_hint可以通过find命令,更容易地估计其大小。比如我们的web根目是/var/vhosts,则使用下面的 find命令可以大致地统计当前apc.num_files_hint数目.
#find /var/vhosts \( -name “*.php” -or -name “*.inc” \) -type f -print |wc -l
1442
apc.stat = 1
apc.stat_ctime = 0
这二个参数,只跟apc_compiler_cache缓存相关,并不影响apc_user_cache。我们前面提到过 apc_complier_cache,它缓存的对象是php源文件一一对应的opcodes(目标文件)。PHP源文件存放在磁盘设备上,与之相对应的 Opcodes目标文件位置内存空间(共享内存),那么当php源文件被修改以后,怎么通知更新内存空间的opcodes呢?每次接收到请求后,APC都会去检查打开的php源文件的最后修改时间,如果文件的最后修改时间与相应的内存空间缓存对象记录的最后修改时间不一致的话,APC则会认为存放在内存空间的Opcode目标文件(缓存对象)已经过期了,acp会将缓存对象清除并且保存新解析得到的Opcode。我们关心的是,即便没有更新任何php源文件,每次接受到http请求后,APC都会请求系统内核调用stat()来获取php源文件最后修改时。我们可以通过将apc.stat设置为0,要求 APC不去检查Opcodes相对应的php源文件是否更新了。这样可以获得最佳的性能,我们也推荐这么做。不过,这样做有一点不好的就是,一旦有PHP 源文件更新了之后,需要重启httpd守护进程或者调用apc_cache_clear()函数清空APC缓存来保证php源文件与缓存在内存空间的 Opcodes相一致。
<?php define('ROOTP', dirname(__FILE__) . '/'); include(ROOTP . 'i1.php'); require(ROOTP . 'i2.php'); include_once(ROOTP . 'i3.php'); require_once(ROOTP . 'i4.php'); require(ROOTP . 'i5.php'); include(ROOTP . 'i6.php'); ?> # strace -e trace=file -p `cat /var/run/httpd.pid` getcwd("/var/www/html", 4096) = 14 stat64("/var/www/html/i1.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0 stat64("/var/www/html/i2.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0 lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/i3.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0 open("/var/www/html/i3.php", O_RDONLY) = 12 stat64("/var/www/html/i3.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0 lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 lstat64("/var/www/html/i4.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0 open("/var/www/html/i4.php", O_RDONLY) = 12 stat64("/var/www/html/i4.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0 stat64("/var/www/html/i5.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0 stat64("/var/www/html/i6.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0 chdir("/tmp")= 0
# strace -e trace=file -p `cat /var/run/httpd.pid`
getcwd("/var/www/html", 4096) = 14
open("/var/www/html/i3.php", O_RDONLY) = 12
open("/var/www/html/i4.php", O_RDONLY) = 12
chdir("/tmp") = 0
对比可见,当apc.stat=0时,省了很多系统内核调用,我们没有看到系统内核调用stat64了。其中,i3.php和i4.php分别是 php的include_once和require_once函数调用,它要交给fstat()系统内核调用来检查文件是否打开过。单从性能角度出发的话,require比require_once性能更佳。
设置apc.stat_ctime的意义并是很大。如果apc.stat_ctime值为1时,仅当php源文件的创建时间(ctime)大于 php源文件的最后修改时间(mtime)时,缓存对象的mtime时间会被php源文件的ctime所代替,否则缓存对象的mtime依然记录为php 源文件的mtime。这样做是防止通过cvs, svn或者rsync等工具刷新php源文件的mtime,这样会导致APC通过比对php源文件的创建时间ctime来决定缓存对象有没有过期。我们推荐该保持默认值,即apc.stat_ctime = 0
apc.ttl=0
apc.user_ttl=0
缓存对象的生命周期。其中ttl表示Time To Live,意味着指定时间后缓存对象会被清除。其中0表示永不过期。我们前面提过,APC能缓存的条目是受限定的,如果你把ttl设置永不过期的话,当缓存条目已满或者缓存空间不够,之后的缓存都将失败。
其中apc.ttl作用于apc_compiler_cache。当apc.ttl大于0时,每次请求都会对比这次的请求时间与上一次请求时间之差是不是大于apc.ttl,如果大于apc.ttl,则会被认缓存条目过期了,会被清理。
比较有意思的是apc.user_ttl,它主要作用于apc_user_cache缓存。我们知道,这种类型的缓存是通过 apc_store($key, $var, $ttl = 0)创建的缓存对象。函数apc_store()中指定的$ttl与php.ini中设定的apc.user_ttl有什么异同,是我们比较关心的。因为它们同样作用于apc_userdata_cache缓存。经过分析,我们知道:判断apc_user_cache缓存过期的依据是,当 apc.user_ttl大于0,且这次http请求时间与上一次http请求时间之差大于apc.user_ttl,则认为相应的缓存条目已过期;或者,user.data.ttl(php函数apc_store()中指定的$ttl)大于0,且这次http请求时间与缓存对象创建时间ctime之差大于user.data.ttl,则同样认为缓存条目已过期,会被清除。
我们推荐,如果你的项目较为稳定,并且apc.stat设置为0。同时apc.shm_size、apc.num_files_hint设置合理的话,apc.ttl建议设置为0。即apc_compiler_cache永不回收,直到重启httpd守护进程或者调用函数 apc_cache_clear()清缓存。至于apc.user_ttl,建议设置为0,由开发人员调用apc_store()函数的时候,设置$ttl来指定该缓存对象的生命周期。
apc.slam_defense=0
apc.write_lock=1
apc.file_update_protection=2
之所以把这三个配置放在一起解释,是因为他们的意义很相近。其中apc.file_update_protection最好理解,它的单位是时间单位秒。如果当前http请求时间与php源文件最好修改时间mtime之差小于apc.file_update_protection时间,APC则不会缓存该 php源文件与之对应的Opcodes,直到接下来的某次访问,并且访问时间与php源文件的最后修改时间大于 apc.file_update_protection时间,相之相应的Opcodes才会被缓存到共享内存空间。这样做的好处是,不容易被用户访问到你正在修改的源文件。我们推荐在开发环境,该值可以设置得更大一点,但在运营环境,我们推荐保留默认值即可。
当你的网站并发量很大的时候,可能出现由http守护进程fork的多个子进程同时缓存同一份Opcodes的情况。通过 apc.slam_defense则可以减少这种事情的发生机率。比如,apc.slam_defense值设置为60的时候,当遇到未缓存的 Opcodes,每100次有60次是不缓存的。对于并发量不大的网站,我们推荐该值设定为0,对于并发量高的网站我们可以根据统计适当地调整该值。而 apc.write_lock是一个布尔值,当该值设置为1的时候,当多个进程同时缓存同一份Opcodes时,仅当最先那个进程缓存有效,其它的无效。通过apc.write_lock设置,有效地避免了缓存写竞争的出现。
apc.max_file_size=1M
apc.filters = NULL
apc.cache_by_default=1
这三个配置放在一起,是因为他们都用于限制缓存。其中apc.max_file_size表示如果php源文件超过了1M,则与之对应的opcodes不被缓存。而apc.filters指定一个文件过滤列表,以逗号(,)隔开。当apc.cache_by_default等于1时,与 apc.filters列表中指定的文件名相匹配的文件不会被缓存。相反,apc.cache_by_default等于0时,仅缓存与 acp.filters列表中指定的文件相匹配的文件。
总结
1,使用Spinlocks锁机制,能够达到最佳性能。
2,APC提供了apc.php,用于监控与管理APC缓存。不要忘记修改管理员名和密码
3,APC默认通过mmap匿名映射创建共享内存,缓存对象都存放在这块”大型”的内存空间。由APC自行管理该共享内存
4,我们需要通过统计调整apc.shm_size、apc.num_files_hints、apc.user_entries_hint的值。直到最佳
5,好吧,我承认apc.stat = 0 可以获得更佳的性能。要我做什么都可以接受.
6,PHP预定义常量,可以使用apc_define_constants()函数。不过据APC开发者介绍说pecl hidef性能更佳,抛异define吧,它是低效的。
7,函数apc_store(),对于系统设置等PHP变量,生命周期是整个应用(从httpd守护进程直到httpd守护进程关闭),使用APC比Memcached会更好。必竟不要经过网络传输协议tcp。
8,APC不适于通过函数apc_store()缓存频繁变更的用户数据,会出现一些奇异现象。

PHPSession失效的原因包括配置錯誤、Cookie問題和Session過期。 1.配置錯誤:檢查並設置正確的session.save_path。 2.Cookie問題:確保Cookie設置正確。 3.Session過期:調整session.gc_maxlifetime值以延長會話時間。

在PHP中調試會話問題的方法包括:1.檢查會話是否正確啟動;2.驗證會話ID的傳遞;3.檢查會話數據的存儲和讀取;4.查看服務器配置。通過輸出會話ID和數據、查看會話文件內容等方法,可以有效診斷和解決會話相關的問題。

多次調用session_start()會導致警告信息和可能的數據覆蓋。 1)PHP會發出警告,提示session已啟動。 2)可能導致session數據意外覆蓋。 3)使用session_status()檢查session狀態,避免重複調用。

在PHP中配置會話生命週期可以通過設置session.gc_maxlifetime和session.cookie_lifetime來實現。 1)session.gc_maxlifetime控制服務器端會話數據的存活時間,2)session.cookie_lifetime控制客戶端cookie的生命週期,設置為0時cookie在瀏覽器關閉時過期。

使用數據庫存儲會話的主要優勢包括持久性、可擴展性和安全性。 1.持久性:即使服務器重啟,會話數據也能保持不變。 2.可擴展性:適用於分佈式系統,確保會話數據在多服務器間同步。 3.安全性:數據庫提供加密存儲,保護敏感信息。

在PHP中實現自定義會話處理可以通過實現SessionHandlerInterface接口來完成。具體步驟包括:1)創建實現SessionHandlerInterface的類,如CustomSessionHandler;2)重寫接口中的方法(如open,close,read,write,destroy,gc)來定義會話數據的生命週期和存儲方式;3)在PHP腳本中註冊自定義會話處理器並啟動會話。這樣可以將數據存儲在MySQL、Redis等介質中,提升性能、安全性和可擴展性。

SessionID是網絡應用程序中用來跟踪用戶會話狀態的機制。 1.它是一個隨機生成的字符串,用於在用戶與服務器之間的多次交互中保持用戶的身份信息。 2.服務器生成並通過cookie或URL參數發送給客戶端,幫助在用戶的多次請求中識別和關聯這些請求。 3.生成通常使用隨機算法保證唯一性和不可預測性。 4.在實際開發中,可以使用內存數據庫如Redis來存儲session數據,提升性能和安全性。

在無狀態環境如API中管理會話可以通過使用JWT或cookies來實現。 1.JWT適合無狀態和可擴展性,但大數據時體積大。 2.Cookies更傳統且易實現,但需謹慎配置以確保安全性。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

WebStorm Mac版
好用的JavaScript開發工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能