2013-06-02 14 views
5

Tôi cần có thể chèn từ yêu cầu biểu mẫu 10'000 + hàng tương tự cùng một lúc. Hiện tại tôi đã thực hiện nó với một tuyên bố chuẩn bị một hàng looped 10'000 lần, nơi tôi tái bindParam mỗi var.PHP PDO chèn nhiều (10000+) hàng cùng sử dụng bindParam. Thực hành tốt?

for ($i=0; $i < intval($cloneCount); $i++) 
{ 
    ... 9 other bindParam 
    $insertG->bindParam(':v1', $v1, PDO::PARAM_STR); 
    $insertG->bindParam(':v2', $v2, PDO::PARAM_INT); 
    $insertG->execute(); 
} 

Mất gần 30 giây để đạt được và chắc chắn không phải là thực tiễn tốt. Hôm nay là 10'000 nhưng có thể là 100'000 vào ngày mai.

Nếu tôi chèn hàng bội số trong một truy vấn với (v1,v2),(v1,v2)... tôi cần phải ràng buộc từng giá trị với một tham số mới, do đó tôi tin rằng tôi cần phải có gần 100'000 bindedParam trong một truy vấn. Nếu đó là UTF-8 và tôi đếm khoảng 2 Byte (tôi biết nó có thể lên đến 4) cho mỗi char truy vấn của tôi sẽ được khoảng 10-20 MB và máy chủ mysql là trên máy khác. Nói điều này tôi ngạc nhiên rằng nó chỉ mất 30 giây cho yêu cầu thiết kế kém của tôi để thành công.

Có cách nào để gửi chỉ một dòng và cho máy chủ mysql sao chép hàng cuối cùng 10'000 lần không?

EDIT MỘT PHẦN GIẢI PHÁP

Sau Bill Karwin và Zsolt Szilágy lời khuyên. Tôi quản lý để có được xuống đến 5-6 giây với các tinh chỉnh sau đây cho một chèn 10'000 đến một máy chủ mysql từ xa:

$dataBase->beginTransaction(); 

$insertG = $dataBase->prepare('INSERT INTO G...) 
... 
10 * bindParam of all kinds 

for ($i=0; $i < 10000; ++$i) 
{ 
    $hashKey = sha1(uniqid().$i); //$hashKey is a binded param 
    $insertG->execute(); 
} 
$dataBase->commit(); 

Trả lời

12

Bạn không cần bindParam() trong mỗi lần lặp của vòng lặp. Hàm bindParam() làm cho các biến $ v1, $ v2, v.v. bị ràng buộc bằng tham chiếu, vì vậy tất cả những gì bạn cần làm là thay đổi giá trị của các biến này và sau đó thực hiện lại truy vấn. Điều đó có thể cắt giảm chi phí.

Ngoài ra, bạn có thể tránh gọi hàm intval() mỗi lần qua vòng lặp. Chỉ cần đảm bảo $ cloneCount bị ép buộc thành số nguyên một lần, trước vòng lặp. Đó là một cải tiến rất nhỏ, nhưng đó là thực hành tốt.

$cloneCount = (int) $cloneCount; 

... 9 other bindParam 
$insertG->bindParam(':v1', $v1, PDO::PARAM_STR); 
$insertG->bindParam(':v2', $v2, PDO::PARAM_INT); 

for ($i=0; $i < $cloneCount; $i++) 
{ 
    $v1 = /* something */ 
    $v2 = /* something */ 
    $insertG->execute(); 
} 

Bạn cũng nên tránh tự động. Giảm chi phí giao dịch của MySQL cho mỗi lần thực hiện câu lệnh bằng cách starting an explicit transaction, chèn hàng nghìn hàng và sau đó cam kết giao dịch.

Nhưng cách tốt nhất để tăng tốc số lượng lớn INSERT của hàng nghìn hàng tương tự với một bảng duy nhất là sử dụng LOAD DATA LOCAL INFILE thay vì INSERT.Điều này chạy nhanh hơn 10-20x so với hàng INSERT theo hàng, ngay cả khi bạn sử dụng các tham số, giao dịch, chèn nhiều hàng và bất kỳ mẹo nào khác mà bạn có thể nghĩ đến.

Thậm chí nếu bạn phải sử dụng PHP để ghi dữ liệu của bạn vào tệp .CSV vào đĩa và sau đó sử dụng LOAD DATA LOCAL INFILE trên tệp đó, nó vẫn nhanh hơn nhiều.

Xem thêm Speed of INSERT Statements trong hướng dẫn sử dụng MySQL để biết thêm mẹo.

+0

Việc di chuyển phần count() ra khỏi vòng lặp không chỉ là một cải tiến nhỏ, vì PHP xử lý đếm khá chậm. Điểm tốt! Một cách thông thường để làm điều này là cho ($ i = 0, $ max = count ($ array); $ i <$ max; $ i ++) –

+0

@ZsoltSzilagy, đúng vậy, nhưng OP không làm đếm() trong vòng lặp, ông đã làm intval(). Không gần như tốn kém như đếm(), nhưng nó vẫn là một cuộc gọi chức năng và bất kỳ cải tiến nhỏ giúp nếu bạn đang đi vòng lặp hơn 100.000 lần. –

+0

Eah bạn đúng, tôi nên cuộn lên lần nữa. :) –

4

Xây dựng một đối tượng bao bọc để chèn số lượng lớn.

Bạn muốn có một cái gì đó như $bulkinsert->add($street,$zip); trong vòng lặp của mình. Nó nội bộ nên xây dựng một chuỗi truy vấn với nhiều chèn:

insert into table1 (First,Last) values 
    ("Fred","Smith"), 
    ("John","Smith"), 
    ("Michael","Smith"), 
    ("Robert","Smith") 
    ...; 

tôi sẽ thực hiện nó một lần sau mỗi 100-1000 cuộc gọi của add(). 500 là sự cân bằng tốt giữa kích thước truy vấn và thời gian thực hiện. Bằng cách đó, bạn dành 99,8% các truy vấn bạn hiện đang sử dụng.

EDIT: Như được đề xuất trong câu trả lời khác, hãy di chuyển số đếm() ot trong vòng lặp của bạn. Ngoài ra, hãy sử dụng ++$i thay vì $i++. (Long câu chuyện ngắn, $ i + + tạo ra một cuộc gọi ngăn xếp trên cao thường được bỏ qua, nhưng bạn đang ở trong một vòng lặp chặt chẽ nơi microoptimisations vấn đề.)

+0

++ $ i Tôi sẽ nhớ! –

+1

Tôi đã từng hỏi một nhà phát triển trình biên dịch về sự khác biệt giữa i ++ và ++ i. Anh ta nói bằng cách gửi email cho anh ta câu hỏi này, tôi đã lãng phí nhiều tài nguyên máy tính hơn tôi sẽ tiết kiệm được bằng cách chọn một kiểu tăng dần lên một cái khác trong toàn bộ sự nghiệp của tôi. :-) –

+0

@BillKarwin Điều đó phụ thuộc vào những gì bạn đang làm với nó. Như tôi đã nói, nó chỉ tạo ra sự khác biệt trong các vòng lặp ** thực sự ** chặt chẽ. Và tôi cho rằng anh chàng biên dịch không phải là một ngôn ngữ kịch bản. Nhưng bạn có thể đúng là bình luận của bạn không xứng đáng với thời gian :-P (không có lý do vi phạm) –

1

Nếu tôi hiểu chính xác câu hỏi của bạn Có cách nào để chỉ gửi một dòng và cho máy chủ mysql sao chép hàng cuối cùng 10'000 lần không? bạn cần sao chép cùng một hàng nhiều lần.

Đối với điều đó, đặc biệt nếu bạn thường xuyên làm như vậy, một bảng kiểm đếm (có nhiều hàng như bạn mong đợi là giới hạn 100000) và CROSS JOIN có thể giúp thực hiện điều đó trên mặt db và với các bộ chứ không phải vòng lặp .

Tạo một bảng tổng sắp

CREATE TABLE tally(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY); 

DELIMITER $$ 
CREATE PROCEDURE sp_populate_tally(IN n INT) 
BEGIN 
    DECLARE i INT DEFAULT 1; 
    WHILE i <= n DO 
     INSERT INTO tally VALUES (NULL); 
     SET i = i + 1; 
    END WHILE; 
END$$ 
DELIMITER ; 

CALL sp_populate_tally(100000); 

Bây giờ lặp lại liên tiếp 10000 lần làm

INSERT INTO table_name (n1, n2, ...) 
SELECT n1, n2, ... 
    FROM 
(
    SELECT 1 n1, 'TextValue1' n2, ... 
) a CROSS JOIN tally t 
WHERE t.id <= 10000; 

Đây là SQLFidlle demo (CẬP NHẬT).

+0

Vâng đó cũng là một phần của câu hỏi. Tôi đã cố gắng để tìm cách để làm điều đó nhưng tôi không thể thực sự sao chép chính xác mỗi hàng. (Tôi nghĩ rằng tôi có thể và sau đó sửa đổi mỗi hàng nhưng nó sẽ không hiệu quả). –

+0

@NicolasManzini Vui lòng giải thích bản chất của các sửa đổi. Có thể họ có thể được thực hiện với SQL. – peterm

+0

Tôi cần chèn 10 000 hàng có nội dung tương tự, ngoại trừ mỗi giá trị phải có giá trị khác nhau cho cột mã băm và cột định danh là cột khóa tự động tăng dần chính. –

Các vấn đề liên quan