2013-12-13 85 views
1

我正在更新從MySQL數據庫中提取數據的現有基於Web的庫存系統。存儲數據的主要結構是具有一對多關係的「項目」和「標籤」(項目可以有多個相應的標籤)MySQL外部左連接性能

數據的現有前端系統是Backbone.js應用程序在登錄時拉取整個數據存儲並操縱內存中的數據,並在必要時通過RESTful接口提交回數據庫。 (這不是我設計系統的方式,但它現在是Backbone和Spine應用程序中的常見模式,以及大多數教程和書籍如何教授這些框架)。

爲了提供捕獲整個數據集的前端(此時大約有1000個項目和10,000個項目標籤)執行的初始提取,後端對項目表執行SELECT查詢,然後執行後續爲每個取得的項目查詢標籤表的查詢。性能很糟糕,顯然。我認爲這可以通過JOIN進行改進,計算出一個select查詢優於1000.以下查詢獲取我需要的數據,但超過15秒即可在本地開發服務器上執行。是什麼賦予了?我們可以在不設置緩存鍵值存儲等額外基礎架構的情況下改進此係統或查詢嗎?

SELECT items.*, itemtags.id as `tag_id`, itemtags.tag, itemtags.type 
FROM items LEFT OUTER JOIN 
    itemtags 
    ON items.id = itemtags.item_id 
ORDER BY items.id; 

下面是表的結構:

CREATE TABLE `items` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `num` int(11) NOT NULL, 
    `title` varchar(100) NOT NULL, 
    `length_inches` int(10) unsigned DEFAULT NULL, 
    `length_feet` int(10) unsigned DEFAULT NULL, 
    `width_inches` int(10) unsigned DEFAULT NULL, 
    `width_feet` int(10) unsigned DEFAULT NULL, 
    `height_inches` int(10) unsigned DEFAULT NULL, 
    `height_feet` int(10) unsigned DEFAULT NULL, 
    `depth_inches` int(10) unsigned DEFAULT NULL, 
    `depth_feet` int(10) unsigned DEFAULT NULL, 
    `retail_price` int(10) unsigned DEFAULT NULL, 
    `discount` int(10) unsigned DEFAULT NULL, 
    `decorator_price` int(10) unsigned DEFAULT NULL, 
    `new_price` int(10) unsigned DEFAULT NULL, 
    `sold` int(10) unsigned NOT NULL, 
    `push_date` int(10) unsigned DEFAULT NULL, 
    `updated` int(10) unsigned NOT NULL, 
    `created` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM AUTO_INCREMENT=1747 DEFAULT CHARSET=latin1; 

CREATE TABLE `itemtags` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `item_id` int(10) unsigned NOT NULL, 
    `tag` varchar(100) NOT NULL, 
    `type` varchar(100) NOT NULL, 
    `created` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM AUTO_INCREMENT=61474 DEFAULT CHARSET=latin1; 

回答

1

我認爲你可以這樣做:

SELECT *, a.id as `tag_id`, a.tag, a.type 
FROM items LEFT OUTER JOIN 
    (SELECT id, item_id, tag, type from itemtags ORDER BY 1,2,3) a 
    ON items.id = a.item_id 
ORDER BY items.id; 

我真的沒有太大變化,只是別名。 a並不意味着任何重要的東西。

我沒有填表,但你的原始查詢需要4ms,我花了1ms。

http://sqlfiddle.com/#!2/b9551/6

您的應用程序可以拉動整個數據存儲,你在你的數據集是什麼irregardless。由於數據存儲和數據集不是同義詞。

您還沒有任何indexes。您應該在ID, ITEM_ID上放置索引,以優化表格以更快地返回結果。我在order by的子查詢中創建了一個索引。希望這可以幫助。

+0

哇!最初的測試把我的執行從17秒降到了0.88秒!感謝那。所以我試圖阻止大幅提升。這是否僅僅是因爲你使用SELECT子查詢創建的表加入項目表?爲什麼它比將它連接到現有的itemtags表格要快得多? – BBnyc

+1

INDEXES先生。我的子查詢只選擇你需要的列,而不是表中的所有內容。您通過在嵌套SELECT的末尾放置一個訂單來基本創建您自己的索引。很高興幫助。 – Hituptony

1

在性能方面,你可能不喜歡比較對等。通過items.id

    • 連接兩個表一起
    • 排序結果返回的所有結果

    是原始版本:

    SQL查詢完全做下面的事情做這三件事,等待他們完成?

    我的猜測是,原始代碼是按照您希望的順序將項目拉回來,然後只需拉扯任何給定時間實際需要的一小撮標籤即可。

    此外,目前還不清楚items.*數據有多大。查詢制定的方式,您將每個項目拉大約10次 - 可能比原始數據的回報集大得多。

    真正的問題是爲什麼你需要在應用程序的內存中的所有這些信息。您擁有數據庫,只需在需要時撤回所需的數據。您是否熟悉limitoffset - 這些可能是您真正想要的。

  • +0

    謝謝戈登,我完全同意。一次只提取大量數據(使用LIMIT)將是一種更具可擴展性的方法。不幸的是,我這樣做需要對應用程序前端進行重大的重構,因爲Backbone.js應用程序是圍繞內存中的整個數據集構建的。這意味着搜索項目,過濾項目和路由都在前端處理,而不是在後端處理。這種重構可能是軟件長期健康的最佳行動,但我不確定我能否說服客戶承擔這個昂貴的項目。 – BBnyc

    +0

    似乎很差的設計 – Hituptony