13161216443

您所在位置: 首頁> 學習課程> PHP培訓 | gdb排查PHP數據庫長鏈接問題

PHP培訓 | gdb排查PHP數據庫長鏈接問題

發布百知教育 來源:學習課程 2019-10-14

重構一個老的業務系統,有一個特殊的限制就是,有個資源必須在內網存儲使用,但是數據庫呢還必須放在外網,因為外網有業務與內網要共享數據,可能有人就會問了,為什么不做sql同步呢,這個還真沒法做,內外網都需要操作數據。


所以呢就選了一個方案,程序部署內網,然后鏈接外網,這種方案也有很大風險,數據庫暴露在外網,但是沒有辦法只能考慮怎么樣做的更安全,除去傳統的用戶名密碼,端口號變成隨機的,我們還加了客戶端證書的驗證,算是加了一層保險。


但是有個問題就是這樣一來,導致整個相應的就很慢,首先我們排查了sql的索引以及表結構設計的問題,進行了一番改造,響應速度很可以,但是想著是不是還有其他的方式再次優化,就把目光投向了mysql persist connection,我們用的框架也是支持的,就調整了下參數,OK上線測試響應再次提升。


但是調整之后,突然發現有些原來可用的功能,現在紛紛500了,查看日志后發現是sql的問題,而語句根本沒有改動,不應該有問題的,但問題實實在在就在,是sql的語法錯了。因為integer類型,被插入了null,經過和同事一番排查,發現是因為sqlmode嚴格模式導致的,所以就排查哪個地方可能修改了這個配置,先去PHP框架里面排查。


PHP培訓



但是發現無論是否設置的長鏈接,這個mode的設置都為非嚴格模式,可是長鏈接為什么變成了嚴格模式呢。


決定要去php-src里面一探究竟,但是探尋這種項目真有如大海撈針一般,但是也是有跡可循的,因為長鏈接導致的問題,那我就去看哪里是處理長鏈接的地方就好了,根據上面框架中的邏輯,長鏈接時候host前面會有添加一個標示“p:”,這個就是一條線索,而且執行是鏈接函數是mysql_real_connect,我就照著這兩個標志去找,經過一番排查,找到了位于ext/mysqli/mysql_api.c下面的mysqli_real_connect。


PHP培訓

它調用同目錄下mysqli_nonapi.c下面的mysqli_common_connect函數,而這個函數也的確有判斷是否host里面存在“p:”這個長鏈接標示。


php培訓班



然后就接著去分析里面的邏輯,發現半天看源碼看的懵懵的,陷入進去拔不出來了,排查的目的是看看mysql的init_comands為什么在長鏈接下不執行。


順著這個目標展開調查,首先在php-src正常接收到腳本傳遞過來的,init_command的設置非嚴格模式的sql語句數據。而且數據也存入了mysqlnd_data結構下的options下的init_commands,但是存入不代表就會去用,下一步就要看這個命令是否被執行。


php培訓


set mode執行


php培訓班


執行init_command也就是設置sqlmode的方法


這個時候就要好好去分析mysqli_common_connect了,也就是上圖中的方法。這個方法基本邏輯就是從PHP代碼當中獲取指定連接參數然后根據你的host user 以及port等參數作為key,去查看是否有對應的連接存在如果沒有則需要重新創建連接,也就是需要執行mysql的連接函數,這個時候經過debug發現其實在連接創建成功后,的確執行了設置sqlmode的語句。結果也是第一請求創建連接時候的確變成了非嚴格模式,但是后續的請求由于已經有了連接所以就不會重新創建連接,不設置sqlmode。


根據mysql連接的的原理,這種設置屬于session模式下的,基本可以認定如果連接不關閉代表著session也不會過期,因為這是由狀態的協議。但是奇怪的是mode之后都是嚴格模式,讓我百思不得其解。


然后我接著一步步調試發現每次就算是長鏈接,PHP也會重新創建mysqlnd的數據,但是一旦發現連接池里面有東西就選擇了丟棄使用新的直接使用池子里面的,我就懷疑是不是因為新的對象里面的options的sqlmode沒有通過php代碼寫入成功,經過一陣子gdb發現寫入成功了,而且也沒用。而重用連接時候的options 里面的sqlmode為空,但是并不會執行設置sqlmode的語句。


這就讓我很疑惑了,完全想不明白為什么會這樣,這個問題下班后調試四五次每次都感覺快要解決了,但是總感覺還差點東西。接著在想有沒有重新resetsession的mysql庫函數呢,而PHP的mysqlnd正好也用上了。果不其然


PHP培訓班



mysql clear session state


看到了上面的介紹,之后又專門搜索了下對應的函數mysql_reset_connection,這個函數可以重置session的狀態。


php培訓班



所以看到這里就出現了曙光如何確定如果這次PHP執行的bt中,確實存在這個函數的調用那就足以說明為什么后續的重用的連接設置的sqlmode無效了,接下來我去找對應的調用,


很顯然還是在mysqli_common _connect的長鏈接邏輯里面,有這個函數的調用。不過PHP做了一個宏的重定義。


根據排查以及調試發現我們并沒有設置對應的


MYSQLI_NO_CHANGE_USER_ON_PCONNECT,所以我們每次重用連接的時候就會發生清除所有session變量的情況,這樣sqlmode就會變回原來默認的,就解釋了為什么第一次設置有效之后的設置都無效。


所以解決的辦法就是直接兩個方向,第一在每次調用PHP執行sql語句時候調用一下設置sqlmode的語句而不是將它寫入init_command里面,第二是設置宏定義重新編譯。


PHP培訓班:http://www.onhairsalon.com/php2019







上一篇:成為大數據挖掘專家,怎么學好大數據課程?

下一篇:應屆生去公司找個Java程序員的職位需要什么技能?

相關推薦

www.onhairsalon.com

有位老師想和您聊一聊

關閉

立即申請