2015-11-06 38 views
1

我試圖在一個「事務」中插入很多(從數十到數百)類似的記錄到一個表中。記錄僅因主鍵和另外一個字段而有所不同。該表是一個MEMORY表,插入的記錄經常更新並經常刪除,但有些可以留在表中一天。表結構:向MySQL高效地插入很多類似的記錄

id  BIGINT 
sid CHAR 
pid INT 
mask INT 
param1 INT 
param2 INT 
... INT 
paramN INT 

PRIMARY (id, sid), 
KEY [BTREE] (sid), (param1), (param2), (...), (paramN) 

眼下插入發生過一份準備好的聲明:

INSERT INTO object_subscriptions (id, sid, pid, mask, ...) 
    VALUES (:id, :sid, :pid, :mask, ...) 
    ON DUPLICATE KEY UPDATE mask = mask | VALUES(mask) 

記錄樣本:

13194140000467 | 'fBF8OfQlAjSS8uXsAAzx' | 7 | 22 | 3 | 0 | 188 | 5123 | 1 
18392199238192 | 'fBF8OfQlAjSS8uXsAAzx' | 7 | 22 | 3 | 0 | 188 | 5123 | 1 
26342478965721 | 'fBF8OfQlAjSS8uXsAAzx' | 7 | 22 | 3 | 0 | 188 | 5123 | 1 
64322445645318 | 'fBF8OfQlAjSS8uXsAAzx' | 7 | 22 | 3 | 0 | 188 | 5123 | 1 
13194140000467 | 'n2pFykYGNnsp-JfCAeJO' | 5 | 97 | 1293 | 0 | 188 | 5123 | 0 
18392199238192 | 'n2pFykYGNnsp-JfCAeJO' | 5 | 97 | 1293 | 0 | 188 | 5123 | 0 
26342478965721 | 'n2pFykYGNnsp-JfCAeJO' | 5 | 97 | 1293 | 0 | 188 | 5123 | 0 
64322445645318 | 'n2pFykYGNnsp-JfCAeJO' | 5 | 97 | 1293 | 0 | 188 | 5123 | 0 

這在幾十每批次記錄的偉大工程。每批有數百個,大幅減速。一個顯而易見的優化可以使性能提高一倍或三倍,即在一個語句中有多個VALUE集合,但這仍然留下發送N params以及sidpid與每條記錄的開銷,即使只有idmask在一個批量。

我在考慮在準備好的語句中包含這些「固定」值,但是這些'params'是從用戶輸入生成的,並且不可信。由於數據發生變化的頻率以及需要執行多少次索引查找,我對MySQL和MEMORY存在困惑。否則,我會規範化數據或將所有內容移至鍵值存儲。

+1

你可以將它寫入文件並使用'LOAD DATA INFILE'? – Barmar

+0

我可以想到避免重複常見元素的方法,但它們最終會變得非常冗長,所以它可能無法真正解決問題。 – Barmar

+0

@Barmar對於只有幾十條記錄的批處理來說,訪問文件系統的開銷可能太大。無論如何,MySQL服務器可能會或可能不在本地主機上,因此寫入文件並不總是一種選擇。此外,文件本身必須全部讀取,讀取所有重複值。但是,連續讀取文件比從外部客戶端一個接一個地發送準備好的語句要快得多。 – Xeos

回答

0

使用多行插入:

INSERT INTO object_subscriptions (id, sid, pid, mask, ...) 
    VALUES (:id[0], :sid[0], :pid[0], :mask[0], ...), 
    (:id[1], :sid[1], :pid[`], :mask[1], ...), ... 
    ON DUPLICATE KEY UPDATE mask = mask | VALUES(mask) 

這將減少客戶端的服務器上來回交通,並允許MySQL使用一些內部批量裝載優化。

+0

他在他的問題中提到過。問題是每行中所有重複的值都是相同的。 – Barmar

1

以下是我能想到的避免重複常見元素的唯一方法。不幸的是,它可能如此冗長,以至於它不如你正在取代的更好。

INSERT INTO object_subscriptions (id, sid, pid, mask, ...) 
SELECT t1.id, t2.sid, t2.pid, t1.mask, ... 
FROM (SELECT :id1 id, :mask1 mask 
     UNION 
     SELECT :id2, :mask2 
     UNION 
     SELECT :id3, :mask3 
     ... 
    ) t1 
JOIN (SELECT :sid sid, :pid pid, ...) t2 

這只是一個批次。要在單個查詢中執行多個批次,可以將UNION添加到頂級SELECT

INSERT INTO object_subscriptions (id, sid, pid, mask, ...) 
SELECT t1.id, t2.sid, t2.pid, t1.mask, ... 
FROM (SELECT :batch1_id1 id, :batch1_mask1 mask 
     UNION 
     SELECT :batch1_id2, :batch1_mask2 
     UNION 
     SELECT :batch1_id3, :batch1_mask3 
     ... 
    ) t1 
JOIN (SELECT :batch1_sid sid, :batch1_pid pid, ...) t2 
UNION 
SELECT t1.id, t2.sid, t2.pid, t1.mask, ... 
FROM (SELECT :batch2_id1 id, :batch2_mask1 mask 
     UNION 
     SELECT :batch2_id2, :batch2_mask2 
     UNION 
     SELECT :batch2_id3, :batch2_mask3 
     ... 
    ) t1 
JOIN (SELECT :batch2_sid sid, :batch2_pid pid, ...) t2 
UNION 
... 
+0

哇。我不得不說,這是令人印象深刻的。從外觀上看,所有額外的SELECT和UNION仍然比每個查詢發送所有參數要短。絕對值得一試,特別是在大延遲的情況下。 – Xeos