2012-10-08 34 views
8

我有一個MySQL表,看起來像這樣:填充一個MySQL與大型系列行迅速

MySQL Table: status

創建結構的SQL是:

CREATE TABLE `status` (
`id` INT(11) NOT NULL, 
`responseCode` INT(3) NOT NULL DEFAULT '503', 
`lastUpdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 
PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

它存儲獨特idresponseCodelastUpdateresponseCode是一個HTTP請求響應代碼:404,500,503,200等

我有一個URL對應於每個id我爲其發出一個HTTP請求並在該表中記錄了我發出請求的時間並收到回覆。

腳本使得這個查詢對status表:

SELECT id FROM status WHERE lastUpdate < 'XXXX' OR 
(responseCode != 200 AND responseCode != 404) 
ORDER BY id DESC LIMIT 100 

XXXX將在那裏我決定什麼比這日期的需要,無論響應代碼被刷新的日期。此外,我想重新嘗試HTTP請求,如果我沒有得到200404不論上次lastUpdate日期。我LIMIT到100,因爲我一次只跑100,然後我睡了一會兒再做100,等等。

不管怎麼說,所有這很好,但我想要做的就是填充表的時間提前與說一系列這樣的:

(1, 503, NOW()), (2, 503, NOW()), (3, 503, NOW()) ... (100000, 503, NOW()) 

注意,只有ID是遞增的,但它可能不一定從我的需求開始。我想要這樣預先填充表格,因爲那麼上面的查詢可以繼續抓取id以便我們需要重新嘗試,並且我不希望在status表格中插入任何其他內容作爲id的是有限的,不會改變(但有很多)。

我試着用JAVA,(雖然PHP,C#,或任何其他相同概念,不要緊,我,我使用的語言在這裏):

PreparedStatement st = conn.prepareStatement("INSERT INTO status VALUES (?,default,default)"); 

for(int i = 1; i <= 100000; i++) { 
    st.setInt(1,i); 
    st.addBatch(); 
} 

System.out.println("Running batch..."); 
st.executeBatch(); 
System.out.println("Batch done!"); 

這將啓動插入,但問題在於需要花費大量的時間來填充表格(我沒有確切的時間,但它運行了幾個小時)。所以,我的問題歸結爲:是否有一種容易和有效的方式來填充一個像這樣的大量行的MySQL表?

+0

增加了一個純粹的SQL解決方案,我的答案,讓你若有發現什麼更快的我知道。 – xception

回答

11

一般來說定製插入例如價值觀,你可以使用任何一個或多個以下:

  • 開始事務,執行插入,提交
  • 包多個值到一個單一的INSERT INTO查詢
  • 降大任於合作做插入之前nstraints和恢復大衆插入後的限制(可能除了主鍵,不是很肯定,雖然)
  • 使用insert into ... select如果合適

第一個(使用事務處理)是最有可能幫助,但我不確定它是否適用於myisam表,它與innodb它做了一個非常好的工作 - 我只使用那些當我被迫使用MySQL,我更喜歡postgresql。

在特定情況下,將10萬行數據,你可以做到以下幾點:

INSERT INTO status(id, responseCode, lastUpdate) SELECT @row := @row + 1 as row, 503, NOW() FROM 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t5, 
(SELECT @row:=0) t6; 

測試了我的機器上,有:

Query OK, 100000 rows affected (0.70 sec) 
Records: 100000 Duplicates: 0 Warnings: 0 

我敢肯定,你可以比100000行的速度要快得多。

+2

如果您正在運行許多插入語句,將它們分組到事務中會阻止數據庫在每次寫入後寫入磁盤,這可確保它們在事務結束時一次性提交到磁盤。 –

+1

這看起來確實很快,你能簡單地解釋一下查詢中發生了什麼嗎? – user17753

+0

我通過連接5個包含0到9的表來創建連續的數字,然後選擇Number,constant,constant ...這是非常快的...然後在單個事務中插入所有100000個條目,因爲它是單個查詢。 – xception

1

您正在創建一個LARGE批處理語句。嘗試使用比較小的包來分割它,例如。在循環內調用executeBatch()每1000個增量(使用mod(i)yaddayadda)。這應該加快過程:

for(int i = 1; i <= 100000; i++) { 
    st.setInt(1,i); 
    st.addBatch(); 
    if (mod(i,1000)=0) { 
     st.executeBatch(); 
    } 
} 
+0

我注意到執行批處理(如我的問題)仍然積極地填充表(例如我可以觀察數據庫填充)與您的代碼段相同。儘管如此,我並沒有在插入物的性能方面遇到任何重大差異。 – user17753

8

如何在主鍵上設置AUTO_INCREMENT。然後插入第一百(或千)行,無論你喜歡什麼方式(你的例子或DocJones的例子給你)。

然後使用

INSERT INTO table SELECT NULL, '503', NOW() FROM table; 

...連續幾次。這應該使桌子每次都是雙倍大小。

SELECT的第一個插槽中的NULL確保AUTO_INCREMENT啓動並增量id

如果你想長大的表,甚至faser你可以做

INSERT INTO table SELECT NULL, '503', NOW() FROM table AS t1 CROSS JOIN table t2; 

......反覆幾次,這會使得在兩個以前的大小+以前的大小的權力大小的表增加(100^2 + 100)。

這也可以讓你,如果你想創建「隨機」 responseCodes,你可以使用類似CONCAT(ROUND(1+RAND()*4), '0', ROUND(RAND()*5))它會給你響應代碼從100到505

+0

優秀的解決方案! – DocJones

+0

我認爲這個想法非常熱衷。我會試試這個。 – user17753

+1

小心使用'CROSS JOIN',你可以手動輸入10個數值,然後運行'CROSS JOIN'並得到10 + 10^2 = 110,然後你會重複'CROSS JOIN',並且你有110 + 110^2 = 12,210,在第三次重複時,你已經在149,096,310 - 一百四十九個**百萬**條目,這將在一些磁盤IO上下載 - 並花一些時間來寫。 –

2

PHP解決方案加載它們在100批:

for ($i = 0; $i < 100000; $i+=100) { 
    $vals = implode(', ', 
        array_map(function($j) { return "($j, default, default)";}, 
          range($i, $i+100))); 
    mysqli_query($dbh, 'insert into status values ' . $vals) or die mysqli_error($dbh); 
}