2012-10-08 29 views
8

Tôi có một bảng MySQL trông như thế này:cư một MySQL với một loạt lớn các hàng một cách nhanh chóng

MySQL Table: status

SQL để tạo ra cấu trúc là:

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; 

Nó lưu trữ một duy nhất id, responseCodelastUpdate. responseCode là mã phản hồi Yêu cầu HTTP: 404, 500, 503, 200, v.v.

Tôi có URL tương ứng với mỗi id mà tôi thực hiện yêu cầu HTTP và ghi lại trong bảng này khi tôi thực hiện yêu cầu và phản hồi nhận được.

Các kịch bản làm cho truy vấn này so với đồng bảng status:

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

đâu XXXX sẽ là một ngày mà tôi quyết định rằng bất cứ điều gì lớn hơn ngày mà cần phải được làm mới không phụ thuộc vào mã phản hồi. Hơn nữa, tôi muốn kiểm tra lại yêu cầu HTTP nếu tôi không nhận được 200 hoặc 404 bất kể ngày lastUpdate vừa qua. Tôi LIMIT đến 100 bởi vì tôi chỉ chạy 100 tại một thời điểm, và sau đó tôi có nó ngủ trong một thời gian và làm khác 100 sau đó, và như vậy.

Anyways, tất cả những gì tốt đẹp, nhưng những gì tôi muốn làm là cư bàn trước thời hạn với nói một loạt như thế này:

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

Chú ý, chỉ có ID được incrementing, nhưng nó có thể không nhất thiết phải bắt đầu từ 1 cho nhu cầu của tôi. Tôi muốn bảng được điền trước như thế này, bởi vì sau đó truy vấn ở trên có thể tiếp tục lấy các số id của những người chúng ta cần phải kiểm tra lại và tôi không muốn chèn thêm bất kỳ thứ gì vào bảng status dưới dạng id 's là hữu hạn và sẽ không thay đổi (nhưng có nhiều người trong số họ).

tôi đã cố gắng sử dụng JAVA, (mặc dù PHP, C#, hoặc bất cứ điều gì khác là khái niệm tương tự và không quan trọng với tôi mà ngôn ngữ tôi sử dụng ở đây):

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!"); 

này bắt đầu chèn, nhưng vấn đề là phải mất một số lượng thời gian bất thường để điền vào bảng (tôi không có thời gian chính xác, nhưng nó đã chạy hàng giờ). Vì vậy, câu hỏi của tôi sôi xuống: là có một cách dễ dàng và hiệu quả để cư một bảng MySQL với một số lượng lớn các hàng như thế này?

+0

Added một giải pháp sql tinh khiết để trả lời của tôi, cho tôi biết nếu bạn tìm thấy bất cứ điều gì nhanh hơn. – xception

Trả lời

11

Nói chung, bạn có thể sử dụng bất kỳ một hoặc nhiều điều sau đây:

  • Bắt đầu một giao dịch, đừng chèn, cam kết
  • Gói nhiều giá trị vào một chèn duy nhất vào truy vấn
  • Drop bất kỳ đồng nstraints trước khi làm chèn và phục hồi trở ngại sau khi chèn khối (trừ chủ chốt có thể ban đầu, không phải là rất chắc chắn về nó mặc dù)
  • Sử dụng insert into ... select nếu phù hợp

Đầu tiên (sử dụng giao dịch) có nhiều khả năng để giúp đỡ, nhưng Tôi không chắc chắn nếu nó hoạt động trên các bảng myisam, với innodb nó làm một công việc rất tốt - Tôi chỉ sử dụng những khi tôi buộc phải sử dụng mysql, tôi thích postgresql.

Trong trường hợp cụ thể của bạn, chèn 100000 dòng dữ liệu, bạn có thể làm như sau:

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; 

Tested này trên máy tính của tôi, có:

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

Tôi khá chắc chắn rằng bạn có thể không nhận được nhanh hơn nhiều so với 100000 hàng.

+2

Nếu bạn đang chạy nhiều câu lệnh chèn, nhóm chúng thành các giao dịch ngăn không cho db ghi vào đĩa sau mỗi lần, nó đảm bảo chúng được cam kết vào tất cả cùng một lúc vào cuối giao dịch. . –

+1

Điều này có vẻ khá nhanh, bạn có thể giải thích ngắn gọn những gì đang xảy ra trong truy vấn đó không? – user17753

+0

Tôi tạo các số liên tiếp, bằng cách nối 5 bảng chứa từ 0 đến 9 và sau đó chọn Số, hằng, hằng ... rất nhanh ... sau đó chèn tất cả 100000 mục vào trong một giao dịch đơn vì đó là một truy vấn đơn. – xception

1

Bạn đang tạo một bản sao kê LARGE được thực hiện. Hãy thử tách nó trong các gói nhỏ hơn bằng cách sử dụng, ví dụ như. gọi executeBatch() cứ mỗi 1000 gia số của i (sử dụng mod (i) yaddayadda) bên trong vòng lặp. Điều đó sẽ đẩy nhanh quá trình:

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

Tôi đã nhận thấy rằng việc thực hiện lô (như trong câu hỏi của tôi) vẫn còn phổ biến bảng (ví dụ: tôi có thể xem điền db) giống như đoạn mã của bạn ở đây. Mặc dù, tôi không gặp bất kỳ sự khác biệt lớn nào về hiệu suất của các miếng chèn. – user17753

8

Cách đặt AUTO_INCREMENT trên khóa chính.

Sau đó chèn hàng trăm (hoặc nghìn) hàng đầu tiên tùy theo cách bạn thích (ví dụ của bạn hoặc ví dụ DocJones đã cung cấp cho bạn).

Sau đó, sử dụng

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

...liên tục một vài lần. Điều này sẽ làm cho bảng có kích thước gấp đôi mỗi lần.

NULL trong khe đầu tiên của SELECT đảm bảo các cú đá AUTO_INCREMENT và gia số id.

Nếu bạn muốn phát triển bàn thậm chí faser bạn có thể làm

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

... lặp đi lặp lại một vài lần mà sẽ làm cho tăng bảng kích thước với quyền hạn của hai trong số những kích thước trước + kích thước trước đó (100^2 + 100).

này cũng cho phép bạn tùy biến các giá trị chèn ví dụ nếu bạn muốn tạo "ngẫu nhiên" responseCodes bạn có thể sử dụng giống như CONCAT(ROUND(1+RAND()*4), '0', ROUND(RAND()*5)) mà sẽ cung cấp cho bạn hưởng ứng mã khác nhau, từ 100 đến 505.

+0

Giải pháp tuyệt vời! – DocJones

+0

Tôi nghĩ ý tưởng này thực sự quan tâm. Tôi sẽ thử cái này. – user17753

+1

Cẩn thận với 'CROSS JOIN' bạn có thể nhập 10 giá trị theo cách thủ công rồi chạy' CROSS JOIN' và nhận 10 + 10^2 = 110, sau đó bạn sẽ lặp lại 'CROSS JOIN' và poof bạn có 110 + 110^2 = 12,210, tại sự lặp lại thứ ba bạn đã ở 149.096,310 - một trăm bốn mươi chín ** triệu ** mục, mà sẽ chow xuống trên một số đĩa-io - và mất một thời gian để viết. –

2

giải pháp PHP để tải chúng theo lô 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); 
} 
Các vấn đề liên quan