我有一個php,每天計劃任務執行一次,邏輯如下:
1.連接資料庫,從資料庫中Select讀取相關資料到數組
2.根據得到的資料數量進行循環,循環中間包含3個mysql操作( select、insert、update各一個)
3.循環完後關閉資料庫連線
循環執行的次數是根據步驟1的mysql_num_rows而定,基本上幾千上萬。
那麼在循環過程中就會短時間內連續執行幾千上萬X3次資料庫操作,效率非常低。且因為循環次數多任務重需要很長時間才能執行完,會導致nginx出現504錯誤。
且頻繁資料庫操作和長連接,佔用過多資源,導致整個環境效率低下。
請問該怎麼優化呢?
麻煩各位大神賜教,先謝謝了
我有一個php,每天計劃任務執行一次,邏輯如下:
1.連接資料庫,從資料庫中Select讀取相關資料到數組
2.根據得到的資料數量進行循環,循環中間包含3個mysql操作( select、insert、update各一個)
3.循環完後關閉資料庫連線
循環執行的次數是根據步驟1的mysql_num_rows而定,基本上幾千上萬。
那麼在循環過程中就會短時間內連續執行幾千上萬X3次資料庫操作,效率非常低。且因為循環次數多任務重需要很長時間才能執行完,會導致nginx出現504錯誤。
且頻繁資料庫操作和長連接,佔用過多資源,導致整個環境效率低下。
請問該怎麼優化呢?
麻煩各位大神賜教,先謝謝了
就你說的這個情況,建議不要用請求的方式解決,用crontab 加計劃任務在後台運行php 腳本,資料庫查詢時分批處理,比如總共100000 條,每次1000 條;如果必須逐條處理且速度不會很快,建議一邊fetch_row 一邊處理,避免先放入array 再循環。記得根據執行情況 set_time_limit 和資料庫連線的逾時時間。
關於這種資料量稍大的長時任務說幾點我的看法:
1、web環境下不適合長時任務:nginx+php-fpm這種架構是不適合執行長時任務的,中間的各種超時能折磨死人,Apache+PHP要好一些起碼好控制超時,簡單一個set_time_limit(0)就能搞定。
2、任務調度透過web來實現:大多數PHP框架對命令列支援並不好,或者說實現的時候並沒有考慮對命令列的支持,所以一個基於web任務分發機制會容易實現一些,這樣對現有框架的侵入性也會小很多,而且一個穩定的項目,保證入口的統一是極其重要的。如果任務在命令列下運行的話需要考慮很多問題,最突出的問題就是文件權限問題,一般web項目都是用apache之類的用戶運行的,所產生的文件所有者也是apache,而且apache普遍是不允許登陸的,雖然可以實作用apache使用者執行指令,但比較複雜。
3、分而治之:處理長時任務的一個解決方案就是分治,將大任務拆分為小任務,將長時任務轉換為多個短時的小任務,降低對資源的佔用時間,減少因為長時間執行而導致的各種問題,如資料庫連線逾時,PHP記憶體外洩等問題。
附上我寫的一個例子請多多指教
https://github.com/zkc226/cur...
大量資料的時候交由任務系統去執行。首先發起一個請求,由訊息生產者將請求交給消費者處理,並返回,避免等待出現逾時。消費者執行多線程處理。建議使用Gearman,使用起來很方便,而且支援PHP介面。其它的類似Workman,Swoole等都能實現。
所有的操作都集中在同一台伺服器上的同一個時間點執行, 肯定是耗時又耗資源的.
要么像@黃弘說的,分批處理.
要么就增加服務器, 把這些任務分散到其他的伺服器上執行, 所謂分散式處理, 但是又會增加任務的複雜度, 因為還要保證資料的一致性
1.匯出資料到文件,讀文件去循環。 (如mysqldump)
2.考慮是否可以先拼語句,批次執行。而不是每個循環都執行。
3.考慮是否可以用預存程序
並且因為循環次數多任務重需要很長時間才能執行完,會導致nginx出現504錯誤。
是即時計算?計算量大的任務是否考慮後台跑任務計算好寫入緩存,即時請求只讀快取。
這個問題有一點像我之前回答的那個並行執行提高效率的問題
本質就是針對讀取這個大數據進行分流,按ID進行取模並行執行,例如你的伺服器和資料庫能承受20個並發執行
那並行最簡單的方式就是開20個腳本進程執行
0.php -> select * from test where id%20=0;
1.php -> select * from test where id%20=1;
2 .php -> select * from test where id%20=2;
....
這個是拉的方式。
還有一種方式是推到隊列,隊列再調用woker進程去執行,這樣更規範化好管理,比如樓上有一個提到的gearman,我之前在做短信平台的時候也有每日定時任務,就是用的這個。
邏輯大概是你開一個定時任務腳本把所有查詢出來的資料透過呼叫gearman客戶端把資料任務發到gearman調度器,然後你開20個woker(可以在同一台伺服器也可以在區域網路內的不同伺服器),然後調度器就會被分配到這20個gearman woker腳本去執行,每個worker腳本代碼一樣,是一條數據一個任務的執行
用cli模式的PHP腳本處理即可,不要使用WEB方式,很容易超時