php mysql 实现原理_PHP底层和mysql的通信原理
要清楚的幾個概念:
FPM進程:進程數在php-fpm.ini中設置。沒有設置 max_requests ,那么進程是不會銷毀的,也就是說當一個進程里面出現死循環或者內存溢出等導致進程僵死的情況出現的時候,處理的進程就會少一個。
mysql連接數:一個進程連接到mysql的一個庫,算是一個連接。連接數默認100,我們線上是5000,進程數在my.cnf中設置。mysql連接數要大于等于FPM進程數,否則會報錯。
長連接和短連接:FPM短連接MYSQL的時候,無需調用CLOSE函數,因為在RSHUTDOWN的時候,會調用清理。長連接的時候,需要調用close,長連接會一直霸占資源,直到進程死掉。
首先我們來理解一下 php-fpm 的工作原理,php-fpm 是一個 php-cgi 進程管理器,其實就是一個連接池,它和nginx配合的工作原理如下。
我們先從最簡單的靜態方式入手觀察他的工作原理
vim php-fpm.ini
[www]
pm?=?static
pm.max_children?=?5
pm.max_requests?=?2
上面三句話的含義是什么呢:
1、static 表示靜態以靜態方式生成 php-fpm 進程
2、pm.max_children = 5 表示當 php-fpm 啟動時就啟動 5 個 php-fpm 子進程 等待處理 nginx 發過來的請求
3、pm.max_requests = 2 表示每個 php-fpm 子進程處理 2 個請求就銷毀,當然父進程每次看到有銷毀的自然也就會生成新的子進程
我們來簡單驗證一下這個說法:
首先重啟 php-fpm,讓它復位一下
接下來寫一條簡單的語句輸出當前進程ID
echo?"當前 php-fpm 進程ID:".posix_getpid();
不斷刷新瀏覽器觀察輸出變化
當前 php-fpm 進程ID:24548
當前 php-fpm 進程ID:24549
當前 php-fpm 進程ID:24550
當前 php-fpm 進程ID:24547
當前 php-fpm 進程ID:24551
當前 php-fpm 進程ID:24548
當前 php-fpm 進程ID:24549
當前 php-fpm 進程ID:24550
當前 php-fpm 進程ID:24547
當前 php-fpm 進程ID:24551
當前 php-fpm 進程ID:24563
當前 php-fpm 進程ID:24564
當前 php-fpm 進程ID:24565
當前 php-fpm 進程ID:24566
當前 php-fpm 進程ID:24567
當前 php-fpm 進程ID:24563
當前 php-fpm 進程ID:24564
當前 php-fpm 進程ID:24565
當前 php-fpm 進程ID:24566
當前 php-fpm 進程ID:24567
當前 php-fpm 進程ID:24568
當前 php-fpm 進程ID:24569
當前 php-fpm 進程ID:24570
當前 php-fpm 進程ID:24571
當前 php-fpm 進程ID:24572
當前 php-fpm 進程ID:24568
當前 php-fpm 進程ID:24569
當前 php-fpm 進程ID:24570
當前 php-fpm 進程ID:24571
當前 php-fpm 進程ID:24572
可以看得出,第一批id不是按照順序執行的,進程id為24547的進程是在第四位處理的,然后從下面開始,所有id都是順序執行的而且每次生成的一批id都是遞增,是不是有種mysql自增主鍵的趕腳呢?
這里需要注意的是,無論是靜態還是下面的動態配置方式,只要沒有設置 max_requests ,那么進程是不會銷毀的,也就是說當一個進程里面出現死循環或者內存溢出等導致進程僵死的情況出現的時候,處理的進程就會少一個了
好吧理解了靜態的處理方式,我們其實也很容易知道這個方式的弊端了,當然我們平時服務器不可能就開5個進程每個進程處理2個請求,我們來做一個簡單的加減乘除,看看一個服務器應該開多少個 php-fpm 合適
首先我們來看看一個簡單的echo需要多少內存:
$size?=?memory_get_usage();
$unit?=?array('b','kb','mb','gb','tb','pb');
$memory?=?@round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
echo?"當前 php-cgi 進程所使用內存:".$memory;
觀察瀏覽器我們可以得到一下數據:
當前 php-cgi 進程所使用內存:227.17 kb
也就是說一個簡單的什么都不干的php就已經占用了200多K的內存,當然這也不算多。
不過進程多了cpu切換進程速度就會變慢,所以這個數還是需要通過ab等測試工具才能測試出具體應該開多少比較合理
我們先從200個cgi進程開始,不斷的增加,架設增加到800的時候,效率和400一樣,那我們就沒必要開800那么多進程浪費內存了。
那么問題就來了,如果同一時間請求出超過400呢?有人說會排隊等待,真的會排隊等待嗎?答案明顯是 php-fpm 是沒能力排隊了,因為處理請求的php-fpm子進程都用完了,那么等待也就只能是在 nginx 等待,通常一個 nginx 也不只是轉發請求給 php-fpm 就完事了,他還要處理靜態文件呢?如果這些php請求導致nginx的請求數過多一直在等待,那么訪問靜態文件自然也會卡了,這時候我們就需要配置成下面的動態處理方式。
[www]
pm.max_children?=?10
pm.start_servers?=?5
pm.min_spare_servers?=?2
pm.max_spare_servers?=?8
;pm.max_requests?=?2
上面五句話的含義是什么呢:
1、dynamic 表示靜態以動態方式生成 php-fpm 進程
2、pm.max_children = 10 同時活動的進程數 10個
3、pm.start_servers = 5 表示當 php-fpm 主進程啟動時就啟動 5 個 php-fpm 子進程
4、pm.min_spare_servers = 2 表示最小備用進程數
5、pm.max_spare_servers = 8 表示最大備用進程數
6、pm.max_requests = 2 上面說過就不說了
當前 php-fpm 進程ID:2270
當前 php-fpm 進程ID:2271
當前 php-fpm 進程ID:2272
當前 php-fpm 進程ID:2273
當前 php-fpm 進程ID:2274
當前 php-fpm 進程ID:2270
當前 php-fpm 進程ID:2271
當前 php-fpm 進程ID:2272
當前 php-fpm 進程ID:2273
當前 php-fpm 進程ID:2274
當前 php-fpm 進程ID:2270
當前 php-fpm 進程ID:2271
當前 php-fpm 進程ID:2272
當前 php-fpm 進程ID:2273
當前 php-fpm 進程ID:2274
為什么這里沒有重新生成新的進程?因為pm.max_requests = 2被注釋掉了,這個上面其實已經提及過一次了
我們也可以從 ps 看出這批進程id
ps aux|grep php
root?2269?0.0?0.1?134560?4616???Ss?14:27?0:00?php-fpm:?master process?(/etc/php/php-fpm.ini)
www-data?2270?0.2?0.2?136736?9188???S?14:27?0:00?php-fpm:?pool www
www-data?2271?0.2?0.2?136740?9192???S?14:27?0:00?php-fpm:?pool www
www-data?2272?0.2?0.2?134684?7284???S?14:27?0:00?php-fpm:?pool www
www-data?2273?0.2?0.2?136732?9120???S?14:27?0:00?php-fpm:?pool www
www-data?2274?0.1?0.2?134684?7244???S?14:27?0:00?php-fpm:?pool www
從上面我們可以看到一個 id 為 2269 的 php-fpm 主進程 管理著 id 為 2270、2271、2272、2273、2274 的5個php-fpm 子進程
這里需要注意的是,當并發大過start_servers數的處理能力是,備用進程才會啟動,當并發數小的時候,備用進程也會銷毀掉,所以無論什么時候,ps 出來的進程都是上面那5個
下面來看看php-fpm+mysql的效果
mysql>?show processlist;
+----+------------------+-----------+------+---------+------+----------------+-------------------------+
|?Id?|?User?|?Host?|?db?|?Command?|?Time?|?State?|?Info?|
+----+------------------+-----------+------+---------+------+----------------+-------------------------+
|?9?|?root?|?localhost?|?NULL?|?Query?|?0?|?NULL?|?show processlist?|
+----+------------------+-----------+------+---------+------+----------------+-------------------------+
接下來我們看短連接:
$conn?=?new?mysqli("192.168.0.170",?"redol",?"redol",?"test_db");
然后不斷訪問上面的php文件,每次看到的都是
+----+---------+-----------+------+---------+------+----------------+--------------------+
|?Id?|?User?|?Host?|?db?|?Command?|?Time?|?State?|?Info?|
+----+---------+-----------+------+---------+------+----------------+--------------------+
|?9?|?root?|?localhost?|?NULL?|?Query?|?0?|?NULL?|?show processlist?|
+----+---------+-----------+------+---------+------+----------------+--------------------+
這也是php神奇的地方,居然不用close的,每次請求完了他就自己給你close掉和mysql的連接了,這點確實也讓很多新手少了不少下面的煩惱啊
Warning: mysqli::mysqli(): (HY000/1040): Too many connections in ...
不要以為語言應該都是這樣那就打錯特錯了,去看看golang吧
下面看看長連接
$conn = new mysqli("p:192.168.0.170", "redol", "redol", "test_db");
你沒看錯,mysqli的長連接和mysql不同,是在host前面加 p:,沒有mysqli_pconnet 的用法,估計很多剛開始用mysqli也是摸不著頭腦吧?
第一次訪問:
+----+-------+-------------------------+-------+---------+------+-------+------------------+
|?Id?|?User?|?Host?|?db?|?Command?|?Time?|?State?|?Info?|
+----+-------+-------------------------+-------+---------+------+-------+------------------+
|?9?|?root?|?localhost?|?NULL?|?Query?|?0?|?NULL?|?show processlist?|
|?10?|?redol?|?bbs.demo.kkk5.com:16650?|?redol?|?Sleep?|?34?|?|?NULL?|
+----+-------+-------------------------+-------+---------+------+-------+------------------+
刷新一下網頁
+-----+-------+-------------------------+-------+---------+------+-------+------------------+
|?Id?|?User?|?Host?|?db?|?Command?|?Time?|?State?|?Info?|
+-----+-------+-------------------------+-------+---------+------+-------+------------------+
|?9?|?root?|?localhost?|?NULL?|?Query?|?0?|?NULL?|?show processlist?|
|?10?|?redol?|?bbs.demo.kkk5.com:16650?|?redol?|?Sleep?|?4?|?|?NULL?|
|?727?|?redol?|?bbs.demo.kkk5.com:16657?|?redol?|?Sleep?|?1?|?|?NULL?|
+-----+-------+-------------------------+-------+---------+------+-------+------------------+
再刷新一下網頁,效果我就不發了,反正你知道最后無論怎么刷新,都是如下即可
+-----+-------+-------------------------+-------+---------+------+-------+------------------+
|?Id?|?User?|?Host?|?db?|?Command?|?Time?|?State?|?Info?|
+-----+-------+-------------------------+-------+---------+------+-------+------------------+
|?9?|?root?|?localhost?|?NULL?|?Query?|?0?|?NULL?|?show processlist?|
|?10?|?redol?|?bbs.demo.kkk5.com:16650?|?redol?|?Sleep?|?4?|?|?NULL?|
|?727?|?redol?|?bbs.demo.kkk5.com:16657?|?redol?|?Sleep?|?1?|?|?NULL?|
|?728?|?redol?|?bbs.demo.kkk5.com:16659?|?redol?|?Sleep?|?16?|?|?NULL?|
|?729?|?redol?|?bbs.demo.kkk5.com:16661?|?redol?|?Sleep?|?12?|?|?NULL?|
|?730?|?redol?|?bbs.demo.kkk5.com:16663?|?redol?|?Sleep?|?8?|?|?NULL?|
+-----+-------+-------------------------+-------+---------+------+-------+------------------+
也就是說,長連接是真的會一直霸占mysql連接的,那么問題就來了,如果我沒有重啟 php-fpm,只重啟了mysql,會出現什么問題呢?答案是第一次連接的時候會報下面錯誤
Warning: mysqli::mysqli(): MySQL server has gone away in? ?###也就是連接已經存在
所以用長連接query前還是先判斷有沒有連接,沒有就close連接,注意一定要close,再連接,否則連接是失效的。
下面我們來試試 Too many connections 的錯誤吧
先調整一下mysql的最大連接數
vim?/etc/mysql/my.cnf
max_connections?=?3
你沒看錯,我只給了他3個連接,而上面的php-fpm是5個,所以結果不用我說都知道了吧,如期的出現
Warning: mysqli::mysqli(): (HY000/1040): Too many connections in ...
所以線上的mysql,還是注意一下這個max_connections數吧,我只能告訴你他默認是100,如果你覺得100不夠用的話,自己改去吧
從上面可知,短連接是不用close也會自動關閉的,那如果是設置了 pm.max_requests = 2,每個php-fpm處理兩個請求就銷毀,銷毀了會close么?我就不截圖了,直接告訴答案吧,會的,所以無論如何,看到的mysql都是1、2、3、4、5、4、3、2、1、2、3、4、5這樣的連接數,就是慢慢增加,再慢慢減少,減少是因為php-fpm子進程銷毀了嘛
作者:fujun_195a
鏈接:https://www.jianshu.com/p/d955a5413c7e
總結
以上是生活随笔為你收集整理的php mysql 实现原理_PHP底层和mysql的通信原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iPhone 小技巧:支持通过摄像头取景
- 下一篇: mysql用户名锁定_MySQL用户锁定