php7性能優化之opcache

鳥哥在博客中說,提高PHP 7性能的幾個tips,第一條就是開啓opcache:

記得啓用Zend Opcache, 因爲PHP7即使不啓用Opcache速度也比PHP-5.6啓用了Opcache快, 所以之前測試時期就發生了有人一直沒有啓用Opcache的事情。

背景

最近業務有所增長,隨之而來的是慢請求逐漸多了起來,在搜索php性能優化的過程中發現了opcache,相關的文章很多,但是都比較零碎,所以在此做個總結。公司當前使用的PHP版本爲php7.x.x。

opcache是什麼?

Opcache 的前生是 Optimizer+ ,它是PHP的官方公司 Zend 開發的一款閉源但可以免費使用的 PHP 優化加速組件。

官網介紹:

OPcache 通過將 PHP 腳本預編譯的字節碼存儲到共享內存中來提升 PHP 的性能, 存儲預編譯字節碼的好處就是 省去了每次加載和解析 PHP 腳本的開銷。

PHP 5.5.0 及後續版本中已經綁定了 OPcache 擴展。 對於 PHP 5.2,5.3 和 5.4 版本可以使用 » PECL 擴展中的 OPcache 庫。

PHP的正常執行流程如下:

request請求(nginx,apache,cli等)-->Zend引擎讀取.php文件-->掃描其詞典和表達式 -->解析文件-->創建要執行的計算機代碼(稱爲Opcode)-->最後執行Opcode--> response 返回。

如上圖,啓用opcache之前,每一次請求PHP腳本都會執行一遍以上步驟,如果PHP源代碼沒有變化,那麼Opcode也不會變化,顯然沒有必要每次都重新生成Opcode,結合在Web中無所不在的緩存機制,我們可以把Opcode緩存下來,以後直接訪問緩存的Opcode豈不是更快。

啓用Opcode緩存之後的流程圖如下所示:

Opcode cache 的目地是避免重複編譯,減少 CPU 和內存開銷。

如何使用opcache?

載入opcache擴展

PHP 5.5及後續版本默認都綁定了opcache擴展,所以我在此就不需要再編譯安裝擴展了,可以直接編輯 php.ini 文件配置載入 opcache 擴展。

載入方法,在php.ini文件加入:

zend_extension=opcache.so

然後重啓,php-fpm服務,通過命令可查看PHP當前支持的擴展:

$ php -m
[Zend Modules]
Zend OPcache

啓用opcache

至此PHP已加載opcache模塊,然後就需要進行修改 php.ini 配置,啓用 opcache (以下是官方推薦配置,僅供參考):

[opcache]
opcache.enable=1
opcache.use_cwd=1
opcache.enable_cli=1
opcache.save_comments=1
opcache.huge_code_pages=1
opcache.memory_consumption=128
opcache.max_wasted_percentage=5
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=40960
opcache.validate_timestamps=0
opcache.force_restart_timeout=180
opcache.error_log=/usr/local/php-fpm/var/log/opcache.log
opcache.log_verbosity_level=1

重啓php-fpm服務後生效。

關於Linux內核HugePage

在opcache配置中有這樣一個參數:huge_code_pages=1,該參數需要配合系統HugePage參數共同使用。

關於Hugepage詳細介紹可參考:Linux HugePage 特性

在系統中開啓Hugepage:

# 分配512個預留的大頁內存
$ sysctl vm.nr_hugepages=512 

# 查看內存信息
$ cat /proc/meminfo | grep Huge

優化效果

未使用opcache:

使用opcache:

很顯然,使用opcache後效果還是很明顯的,請求處理明顯快了很多!

opcache參數詳解

;opcache模塊配置
[opcache]
opcache.enable=1 (default "1")
;OPcache打開/關閉開關。當設置爲Off或者0時,會關閉Opcache, 代碼沒有被優化和緩存。

opcache.enable_cli=1 (default "0")
;CLI環境下,PHP啓用OPcache。這主要是爲了測試和調試。從 PHP 7.1.2 開始,默認啓用。

opcache.memory_consumption=128 (default "64")
;OPcache共享內存存儲大小。用於存儲預編譯的opcode(以MB爲單位)。

opcache.huge_code_pages=1
;啓用或者禁用將 PHP 代碼(文本段)拷貝到 HUGE PAGES 中。 此項配置指令可以提高性能,但是需要在 OS 層面進行對應的配置。

opcache.interned_strings_buffer=16 (default "4")
;這是一個很有用的選項,但是似乎完全沒有文檔說明。PHP使用了一種叫做字符串駐留(string interning)的技術來改善性能。
;例如,如果你在代碼中使用了1000次字符串“foobar”,在PHP內部只會在第一使用這個字符串的時候;分配一個不可變的內存區域來存儲這個字符串,其他的999次使用都會直接指向這個內存區域。這個選項則會把這個特性提升一個層次,
;默認情況下這個不可變的內存區域只會存在於單個php-fpm的進程中,如果設置了這個選項,那麼它將會在所有的php-fpm進程中共享。
;在比較大的應用中,這可以非常有效地節約內存,提高應用的性能。
;這個選項的值是以兆字節(megabytes)作爲單位,如果把它設置爲16,則表示16MB,默認是4MB,這是一個比較低的值。

opcache.max_accelerated_files=40960 (default "2000")
;這個選項用於控制內存中最多可以緩存多少個PHP文件。這個選項必須得設置得足夠大,大於你的項目中的所有PHP文件的總和。
;設置值取值範圍最小值是 200,最大值在 PHP 5.5.6 之前是 100000,PHP 5.5.6 及之後是 1000000。也就是說在200到1000000之間。
;你可以運行“find . -type f -print | grep php | wc -l”這個命令來快速計算你的代碼庫中的PHP文件數。

opcache.max_wasted_percentage=5 (default "5")
;計劃重新啓動之前,“浪費”內存的最大百分比。

opcache.use_cwd=1 (default "1")
;如果啓用,OPcache將在哈希表的腳本鍵之後附加改腳本的工作目錄,以避免同名腳本衝突的問題。禁用此選項可以提高性能,但是可能會導致應用崩潰

opcache.validate_timestamps=0 (default "1")
;如果啓用(設置爲1),OPcache會在opcache.revalidate_freq設置的秒數去檢測文件的時間戳(timestamp)檢查腳本是否更新。
;如果這個選項被禁用(設置爲0),opcache.revalidate_freq會被忽略,PHP文件永遠不會被檢查。
;這意味着如果你修改了你的代碼,然後你把它更新到服務器上,再在瀏覽器上請求更新的代碼對應的功能,你會看不到更新的效果,你必須使用 `opcache_reset()` 或者 `opcache_invalidate()` 函數來手動重置OPcache。或者重重你的web服務器或者php-fpm 來使文件系統更改生效。
;我強烈建議你在生產環境中設置爲0,why?因爲當你在更新服務器代碼的時候,如果代碼較多,更新操作是有些延遲的,在這個延遲的過程中必然出現老代碼和新代碼混合的情況,這個時候對用戶請求的處理必然存在不確定性。最後,等所有的代碼更新完畢後,再平滑重啓PHP和web服務器。

;opcache.revalidate_freq=2 (default "2")
;這個選項用於設置緩存的過期時間(單位是秒),當這個時間達到後,opcache會檢查你的代碼是否改變,如果改變了PHP會重新編譯它,生成新的opcode,並且更新緩存,只有當opcache.validate_timestamps=1生效。
;值爲“0”表示每次請求都會檢查你的PHP代碼是否更新(這意味着會增加很多次stat系統調用,譯註:stat系統調用是讀取文件的狀態,這裏主要是獲取最近修改時間,這個系統調用會發生磁盤I/O,所以必然會消耗一些CPU時間,當然系統調用本身也會消耗一些CPU時間)。可以在開發環境中把它設置爲0,生產環境下不用管。

;opcache.revalidate_path=0 (default "0")
;在include_path優化中啓用或禁用文件搜索
;如果被禁用,並且找到了使用的緩存文件相同的include_path,該文件不被再次搜索。因此,如果一個文件與include_path中的其他地方相同的名稱出現將不會被發現。
;如果此優化對此有效,請啓用此指令你的應用程序,這個指令的默認值是禁用的,這意味着該優化是活躍的。

opcache.error_log=/usr/local/php-fpm/var/log/opcache.log
;opcache日誌存儲位置

opcache.log_verbosity_level=1
;opcache日誌級別 0 fatal errors, 1 errors,2 warning, 3 info, 4 debug

opcache.save_comments=1
;如果禁用,腳本文件中的註釋內容將不會被包含到操作碼緩存文件, 這樣可以有效減小優化後的文件體積
;建議開啓,禁用此配置指令可能會導致一些依賴註釋或註解的 應用或框架無法正常工作, 比如: Doctrine, Zend Framework 2 以及 PHPUnit。

opcache.force_restart_timeout=180
;如果緩存處於非激活狀態,等待多少秒之後計劃重啓。 如果超出了設定時間,則 OPcache 模塊將殺除持有緩存鎖的進程, 並進行重啓。

;opcache.file_cache=/tmp
;配置二級緩存目錄並啓用二級緩存。 啓用二級緩存可以在 SHM 內存滿了、服務器重啓或者重置 SHM 的時候提高性能。 默認值爲空字符串 "",表示禁用基於文件的緩存。

更多配置參數見:運行時配置

opcache管理——cachetool

相對於啓用opcache,日常運維人員更關心的是如何對其進行管理。

CacheTool通過CLI管理和查看(當然,你還可以通過phpinfo來查看)APCu、opcache、file-cache的狀態。

下載即用:cachetool

示例:

如果php-fpm限制了監聽地址,需要在 --fcgi 中指定,如 --fcgi=192.168.1.110:9000 (同php-fpm.conf的listen配置),可以通過 netstat -lntp |grep php-fpm 查看。

更多學習參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章