[英]Reducing the number of database queries in a complex custom app
我繼承了一個用PHP編寫的電子商務軟件項目。 當我檢查代碼庫時,我在代碼中發現了很多SQL語句。 有很多類,如產品,類別,用戶,客戶等,每個類都有很多數據庫查詢。
我不知道如何處理這種情況,並決定計算單頁訪問的總查詢數。 我封裝了MySQL查詢功能並增加了一個計數器。
我對結果感到有些震驚。 要單獨訪問索引頁面,執行了1633(!)個MySQL選擇查詢。 列出類別的產品會觸發近2000個查詢。
我將查詢傳輸到文本文件中進行分析。 超過90%是可能有一個或兩個值的單選語句。 現在我該怎么做來清理這個爛攤子? 你有什么建議? 我在MySQL服務器上啟用了緩存。 加載頁面大約需要490毫秒。
例如,有一個名為Product的類。 在這個類中有8個單個小的SQL select語句。
當您現在打開類別列表以顯示產品時,原始程序員使用一個select語句來獲取所需產品的列表,然后為每個產品創建一個產品對象。
假設這個結果給了我們20個產品:
select id from products where price <= 10;
然后他遍歷結果並為每個條目創建一個產品對象:
$qresult = query("select id from products where price <= 10");
$products = array();
foreach ($qresult as $prod) {
$products[] = new Product($prod['id']);
}
僅此一項就可以為產品生成20 * 8個SQL查詢。 同樣的方法也用於其他類(用戶,客戶,類別等)。
前一段時間
現在,經過幾周/幾個月后,我想分享我到目前為止所做的解決方案。
我可以將查詢減少到每頁訪問量<~50,並將頁面加載時間減少到400毫秒以下。
我很容易做到了。 我試圖確定熱點並構建一個表緩存類。 每次訪問此靜態類都會將整個表內容加載到內存中,從現在開始,每個表請求都將從靜態類中提取出內存。 非常臟,不是很好,但它工作,更快,減少總查詢和備件服務器硬件。
我想我們也會拋出硬件來解決這個問題,只要用戶數量增加,就像現在一樣。
如果我們要用另一個替換應用程序,我們肯定會尋求一個數據庫查詢 - 好的解決方案
謝謝大家的建議
好吧,對於初學者來說,你需要識別的第一件事就在於php領域,並且這些調用中有多少是多次執行的。 僅這一點就會減少你的數字。
除此之外,你可以做兩件事。
我認為你是對的。 2,000個查詢似乎很多。
您已經確定了重構的兩個很好的理由,查詢量和頁面響應時間 - 既可以測量又適合重構。
盡管MySQL確實存在查詢緩存,但每次查詢基礎數據時,如果它能夠從緩存中完成查詢,則仍然可能存在與MySQL通信的網絡成本。
您是否考慮過將值保存到內存或使用會話變量?
<?php
session_start();
// store session data
$_SESSION['sharedvalue']= "result of common MySQL query"
?>
< html>
< body>
< ?php
//retrieve session data - now each time you do this it isn't asking MySQL again
echo "Common Value=". $_SESSION['sharedvalue'];
?>
< /body>
< /html>
另一個解決方案是擁有自己的緩存並從中查詢常用值,更改或過期它們以刷新舊數據。 這實際上取決於您的應用程序和用戶群的大小。
我使用的遺留應用程序遇到了同樣的問題,同時查詢效率也相同! 在短期內,我們只是拋出硬件問題,但至少任何新的工作都會寫得更好。 聽起來像沒有必要功能的自定義ORM,或者沒有以最佳方式使用的ORM。
嘗試重構這個:
$products = array();
foreach ($products as $prod) {
$products[] = new Products($prod['id']);
}
進入:
// Assuming products is an array of arrays, with each inner array
// containing all the values that make up an array
$products = array();
foreach ($products as $prod) {
$products[] = Products::convertToObject($prod['id']);
}
那將在每個循環中為你節省N個查詢。 如果您確保每個ORM類( Products
等)都繼承自BaseORM
類的東西,那么執行此操作的公共代碼只能寫入一次。
如果您發現自己沒有太多的連接方式,那么請對當前代碼進行分支,並使用Propel或Doctrine等替換您自己開發的ORM。 請注意,使用您不知道的ORM系統存在學習曲線,但最終它們的回報通常比自己編寫整個項目更好。
可能有一個(或多個)循環(foreach或while等)為每次迭代調用mysql_query。
一旦找到該循環,您就可以決定如何優化它 - 例如,您可以將所有記錄一次性加載到數組中,然后循環在數組上工作,而不是每次都調用數據庫。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.