簡體   English   中英

減少復雜自定義應用程序中的數據庫查詢數量

[英]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領域,並且這些調用中有多少是多次執行的。 僅這一點就會減少你的數字。

除此之外,你可以做兩件事。

  1. 如果您有多個使用相同參數的查詢(例如productId,並且您帶有產品表,產品類別表等等),您始終可以加入這些查詢並帶來您需要的所有結果(或者查看最常見的查詢加入並使用它)
  2. 如果您有一個設置表,最好的辦法是將所有表加載到內存中並從那里讀取所有值,這樣您就不必在需要設置時隨時轉到數據庫

我認為你是對的。 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM