Cái nào nhanh hơn: nhiều INSERT đơn hoặc một INSERT nhiều hàng?


183

Tôi đang cố gắng tối ưu hóa một phần mã của mình để chèn dữ liệu vào MySQL. Tôi có nên xâu chuỗi các INSERT để tạo một INSERT nhiều hàng lớn hay nhiều INSERT riêng biệt nhanh hơn?

Câu trả lời:


286

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

Thời gian cần thiết để chèn một hàng được xác định bởi các yếu tố sau, trong đó các con số biểu thị tỷ lệ gần đúng:

  • Kết nối: (3)
  • Gửi truy vấn đến máy chủ: (2)
  • Truy vấn phân tích cú pháp: (2)
  • Chèn hàng: (kích thước 1 × của hàng)
  • Chèn chỉ mục: (1 × số chỉ mục)
  • Kết thúc: (1)

Từ điều này, rõ ràng là việc gửi một câu lệnh lớn sẽ giúp bạn tiết kiệm được 7 chi phí cho mỗi câu lệnh chèn, trong khi đọc thêm văn bản cũng cho biết:

Nếu bạn đang chèn nhiều hàng từ cùng một máy khách cùng một lúc, hãy sử dụng các câu lệnh INSERT với nhiều danh sách GIÁ TRỊ để chèn nhiều hàng cùng một lúc. Điều này nhanh hơn đáng kể (trong nhiều trường hợp nhanh hơn nhiều lần) so với sử dụng các câu lệnh INSERT một hàng riêng biệt.


27
Câu trả lời này được áp dụng như thế nào nếu nhiều INSERT duy nhất nằm trong cùng một giao dịch cơ sở dữ liệu?
Pinch

2
Có bao nhiêu hàng tôi có thể chèn cùng một lúc bằng cách sử dụng câu lệnh chèn. nó có cho phép tôi chèn 10000 hàng cùng một lúc không?
Naresh Ramoliya

10
@Pinch Sử dụng giao dịch trong khi thực hiện ~ 1,5k uperts (chèn / cập nhật) đã giảm thời gian thao tác mất từ ​​~ 1,5 giây xuống ~ 0,2 giây. Hay nói cách khác, nó làm cho nó nhanh hơn 86% so với chèn một hàng. Chỉ trích.
fgblomqvist

1
Lưu ý: Có vẻ khác nhiều trong MSSQL: stackoverflow.com/questions/8635818/NH
marsze

Làm thế nào về việc sử dụng Tuyên bố chuẩn bị để chèn nhiều lần chèn lặp lại?
Priyabagus

151

Tôi biết tôi đã trả lời câu hỏi này gần hai năm rưỡi sau khi được hỏi, nhưng tôi chỉ muốn cung cấp một số dữ liệu cứng từ một dự án mà tôi đang làm việc ngay bây giờ cho thấy rằng thực sự thực hiện nhiều khối GIÁ TRỊ cho mỗi lần chèn là NHIỀU nhanh hơn các câu lệnh INSERT khối đơn tuần tự.

Mã tôi đã viết cho điểm chuẩn này trong C # sử dụng ODBC để đọc dữ liệu vào bộ nhớ từ nguồn dữ liệu MSSQL (~ 19.000 hàng, tất cả đều được đọc trước khi bắt đầu viết) và trình kết nối MySql .NET (Mysql.Data. *) XÁC NHẬN dữ liệu từ bộ nhớ vào bảng trên máy chủ MySQL thông qua các câu lệnh được chuẩn bị. Nó được viết theo cách cho phép tôi điều chỉnh linh hoạt số lượng khối GIÁ TRỊ trên mỗi INSERT đã chuẩn bị (nghĩa là chèn n hàng tại một thời điểm, nơi tôi có thể điều chỉnh giá trị của n trước khi chạy.) Tôi cũng đã chạy thử nghiệm nhiều lần cho mỗi n.

Thực hiện các khối GIÁ TRỊ duy nhất (ví dụ: 1 hàng tại một thời điểm) mất 5,7 - 5,9 giây để chạy. Các giá trị khác như sau:

2 hàng cùng một lúc: 3,5 - 3,5 giây
5 hàng cùng một lúc: 2,2 - 2,2 giây
10 hàng một lần: 1,7 - 1,7 giây
50 hàng tại một thời điểm: 1,17 - 1,18 giây
100 hàng tại một thời điểm: 1,1 - 1,4 giây
500 hàng cùng một lúc: 1,1 - 1,2 giây
1000 hàng tại một thời điểm: 1,17 - 1,17 giây

Vì vậy, có, ngay cả việc kết hợp 2 hoặc 3 ghi lại với nhau cũng mang lại sự cải thiện đáng kể về tốc độ (thời gian chạy bị cắt bởi hệ số n), cho đến khi bạn đến một nơi nào đó giữa n = 5 và n = 10, tại đó điểm cải thiện giảm xuống rõ rệt, và ở đâu đó trong phạm vi n = 10 đến n = 50, sự cải thiện trở nên không đáng kể.

Hy vọng điều đó giúp mọi người quyết định (a) có nên sử dụng ý tưởng đa phương thức hay không và (b) có bao nhiêu khối GIÁ TRỊ để tạo cho mỗi câu lệnh (giả sử bạn muốn làm việc với dữ liệu có thể đủ lớn để đẩy truy vấn vượt quá kích thước truy vấn tối đa đối với MySQL, mà tôi tin là 16 MB theo mặc định ở nhiều nơi, có thể lớn hơn hoặc nhỏ hơn tùy thuộc vào giá trị của max_allowed_packet được đặt trên máy chủ.)


1
Yêu cầu làm rõ: là thời gian của bạn "giây trên mỗi hàng" hoặc "tổng số giây".
EngrStudent

3
Tổng số giây - vì vậy giây trên mỗi hàng được chia cho ~ 19.000 hàng. Mặc dù đó là một con số nhỏ, nhưng có lẽ hàng / giây là một số liệu tốt hơn nếu bạn đang tìm kiếm một số dễ so sánh.
Jon Kloske

Tình cờ, có một số ví dụ mã .NET cho cách tiếp cận mà tôi mô tả ở trên về câu trả lời liên quan này của tôi: stackoverflow.com/questions/25377357/
Kẻ

18

Một yếu tố chính sẽ là liệu bạn có đang sử dụng một công cụ giao dịch hay không và liệu bạn có tự động truy cập hay không.

Autocommit được bật theo mặc định và bạn có thể muốn để nó trên; do đó, mỗi lần chèn mà bạn thực hiện giao dịch riêng. Điều này có nghĩa là nếu bạn thực hiện một lần chèn mỗi hàng, bạn sẽ thực hiện giao dịch cho mỗi hàng.

Giả sử một luồng duy nhất, điều đó có nghĩa là máy chủ cần đồng bộ hóa một số dữ liệu vào đĩa cho MERYI ROW. Nó cần đợi dữ liệu đến một vị trí lưu trữ liên tục (hy vọng ram được hỗ trợ bằng pin trong bộ điều khiển RAID của bạn). Điều này vốn đã khá chậm và có lẽ sẽ trở thành yếu tố hạn chế trong những trường hợp này.

Tất nhiên tôi giả sử rằng bạn đang sử dụng một công cụ giao dịch (thường là innodb) VÀ rằng bạn đã không điều chỉnh các cài đặt để giảm độ bền.

Tôi cũng giả sử rằng bạn đang sử dụng một luồng duy nhất để thực hiện các thao tác chèn này. Sử dụng nhiều luồng làm vướng víu một chút vì một số phiên bản của MySQL có cam kết nhóm hoạt động trong innodb - điều này có nghĩa là nhiều luồng thực hiện cam kết của riêng chúng có thể chia sẻ một lần ghi vào nhật ký giao dịch, điều này tốt vì điều đó có nghĩa là ít đồng bộ hóa hơn với lưu trữ liên tục .

Mặt khác, kết quả cuối cùng là, bạn THỰC SỰ MUỐN SỬ DỤNG các phần chèn nhiều hàng.

Có một giới hạn mà nó phản tác dụng, nhưng trong hầu hết các trường hợp, nó có ít nhất 10.000 hàng. Vì vậy, nếu bạn bó chúng lên tới 1.000 hàng, có lẽ bạn an toàn.

Nếu bạn đang sử dụng MyISAM, sẽ có vô số thứ khác, nhưng tôi sẽ không làm bạn chán với những thứ đó. Sự thanh bình.


1
Có bất kỳ lý do nó được phản tác dụng sau một điểm? Tôi đã thấy nó xảy ra trước đây nhưng không chắc tại sao.
Dhruv Gairola

1
Bạn có biết nếu có bất kỳ điểm nào trong việc chèn khối MySQL khi sử dụng giao dịch . Tôi chỉ tự hỏi liệu tôi có thể tự cứu mình khỏi rắc rối khi phải tạo lệnh SQL đa giá trị hay không nếu thư viện cơ bản của tôi (Java JDBC - mysql-Connector-java-5.1.30) không thực sự cam kết cho đến khi tôi nói với nó.
RTF

@RTF Tôi nghĩ rằng bạn sẽ cần phải thực hiện một thử nghiệm nhỏ để xác định hành vi đó trong tình huống của mình vì đó là hành vi cụ thể có tính thực thi cao, nhưng trong nhiều trường hợp, các giao dịch sẽ cung cấp hiệu suất tương tự.
Jasmine Hegman

9

Gửi càng nhiều lần chèn trên dây cùng một lúc càng tốt. Tốc độ chèn thực tế phải giống nhau, nhưng bạn sẽ thấy hiệu suất tăng từ việc giảm chi phí mạng.


7

Nói chung, số lượng cuộc gọi đến cơ sở dữ liệu càng ít càng tốt (nghĩa là nhanh hơn, hiệu quả hơn), vì vậy hãy thử mã hóa các phần chèn theo cách sao cho tối thiểu hóa truy cập cơ sở dữ liệu. Hãy nhớ rằng, trừ khi bạn sử dụng nhóm kết nối, mỗi lần truy cập cơ sở dữ liệu phải tạo kết nối, thực thi sql và sau đó phá bỏ kết nối. Một chút chi phí!


Điều gì nếu kết nối liên tục được sử dụng?
dusoft

6
Vẫn còn trên đầu. Thời gian vận chuyển một mình (đến và đi cho mỗi lần chèn riêng biệt) sẽ nhanh chóng được nhận biết nếu bạn đang thực hiện hàng ngàn lần chèn.
RC.

4

Bạn có thể muốn :

  • Kiểm tra xem cam kết tự động đã tắt chưa
  • Kết nối mở
  • Gửi nhiều lô chèn trong một giao dịch (kích thước khoảng 4000-10000 hàng? Bạn thấy)
  • Đóng kết nối

Tùy thuộc vào quy mô như thế nào máy chủ của bạn (nó dứt khoát ok với PostgreSQl, OracleMSSQL), làm điều trên với nhiều chủ đề và nhiều kết nối.


3

Nói chung, nhiều lần chèn sẽ chậm hơn do chi phí kết nối. Thực hiện nhiều lần chèn cùng một lúc sẽ giảm chi phí cho mỗi lần chèn.

Tùy thuộc vào ngôn ngữ bạn đang sử dụng, bạn có thể tạo một lô trong ngôn ngữ lập trình / kịch bản của mình trước khi đi tới db và thêm từng ngôn ngữ vào lô. Sau đó, bạn sẽ có thể thực hiện một lô lớn bằng một thao tác kết nối. Đây là một ví dụ trong Java.


3

MYSQL 5.5 Một câu lệnh chèn sql mất ~ 300 đến ~ 450ms. trong khi các số liệu thống kê dưới đây dành cho nhiều số liệu chèn nội tuyến.

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

Tôi sẽ nói nội tuyến là cách để đi :)


0

Thật nực cười khi Mysql và MariaDB xấu được tối ưu hóa như thế nào khi chèn. Tôi đã thử nghiệm mysql 5.7 và mariadb 10.3, không có sự khác biệt thực sự trên những cái đó.

Tôi đã thử nghiệm điều này trên máy chủ với các đĩa NVME, 70.000 IOPS, thông lượng seq 1.1 GB / giây và đó là khả năng song công hoàn toàn (đọc và ghi).
Các máy chủ là một máy chủ hiệu suất cao là tốt.
Đã cho nó 20 GB ram.
Cơ sở dữ liệu hoàn toàn trống rỗng.

Tốc độ tôi nhận được là 5000 lần chèn mỗi giây khi thực hiện chèn nhiều hàng (đã thử với khối dữ liệu 1MB lên đến 10 MB)

Bây giờ đầu mối:
Nếu tôi thêm một chủ đề khác và chèn vào các bảng CÙNG tôi đột nhiên có 2x5000 / giây. Thêm một chủ đề và tôi có tổng số 15000 / giây

Xem xét điều này: Khi thực hiện MỘT luồng chèn, điều đó có nghĩa là bạn có thể ghi tuần tự vào đĩa (ngoại trừ các chỉ mục). Khi sử dụng các chủ đề, bạn thực sự làm giảm hiệu suất có thể bởi vì bây giờ nó cần phải thực hiện nhiều truy cập ngẫu nhiên hơn. Nhưng kiểm tra thực tế cho thấy mysql được tối ưu hóa rất tệ đến mức các chủ đề giúp ích rất nhiều.

Hiệu suất thực sự có thể với một máy chủ như vậy có thể là hàng triệu mỗi giây, CPU không hoạt động, đĩa không hoạt động.
Lý do khá rõ ràng là mariadb cũng giống như mysql có sự chậm trễ nội bộ.


@Craftables bạn cần phát triển bên ngoài, nó không thể được thực hiện trong mysql. Chủ đề có nghĩa là bạn sử dụng nhiều kết nối đến máy chủ, bạn chia truy vấn thành nhiều phần (ví dụ: bằng cách chia nó thành các phần chẵn bằng khóa chính). Tôi đã quản lý để có được hiệu suất lên tới 10.000 lần bằng phương pháp này trên các bảng rất lớn. Các truy vấn sẽ chạy trong 40.000 giây có thể kết thúc sau 2-3 phút NẾU bạn sử dụng nhiều luồng và mysql của bạn được tối ưu hóa cao.
John

@John Thú vị và có thể có một số ứng dụng thực sự hay ... nhưng ... Nếu bạn chia truy vấn thành nhiều phần, làm thế nào để bạn xử lý các giao dịch? Và cũng xem xét kịch bản sau: Bảng x có cột 'Parent_id' liên quan đến cùng một bảng 'id'. Ở đâu đó trong dữ liệu của bạn, bạn có GIÁ TRỊ VÀO x ( id, parent_id) GIÁ TRỊ (1, NULL). Một trong những bộ giá trị tiếp theo liên kết đến hàng đó. Nếu bạn chia thành các phần và tập hợp đó được chuyển sang phần khác, nó có thể được xử lý trước phần đầu tiên, làm hỏng toàn bộ quá trình. Bất cứ ý tưởng làm thế nào để đối phó với điều đó?
zozo

@zozo điều này rất hữu ích cho việc chèn số lượng lớn và truy vấn hàng loạt. Các giao dịch sẽ làm hỏng hiệu suất dù sao vì chúng bao gồm nhiều bộ đệm dữ liệu. Nhưng bạn cũng có thể sử dụng các giao dịch trong các chèn hoặc truy vấn đa luồng.
Giăng

-2

nhiều chèn nhanh hơn nhưng nó có thredshould. một thrik khác đang vô hiệu hóa các ràng buộc kiểm tra temprorary làm cho việc chèn nhanh hơn nhiều. Nó không quan trọng bàn của bạn có hay không. Ví dụ: kiểm tra vô hiệu hóa khóa ngoại và tận hưởng tốc độ:

SET FOREIGN_KEY_CHECKS=0;

bạn nên bật lại sau khi chèn bằng cách:

SET FOREIGN_KEY_CHECKS=1;

Đây là cách phổ biến để chèn dữ liệu lớn. tính tích hợp dữ liệu có thể bị phá vỡ vì vậy bạn nên quan tâm đến điều đó trước khi vô hiệu hóa kiểm tra khóa ngoại.


1
Không biết tại sao ppl lại đưa ra điều này vì hai lý do: 1. Nó không liên quan gì đến câu hỏi 2. Đó là một ý tưởng thực sự tồi tệ (với một vài ngoại lệ - như bán phá giá hoặc thay đổi tạm thời cấu trúc - nhưng nói chung là xấu). Việc kiểm tra là có lý do: Chúng ở đó để đảm bảo tính thống nhất của dữ liệu. Họ làm mọi thứ chậm lại là vì họ đảm bảo bạn không chèn hoặc thay đổi dữ liệu mà bạn không nên. Cố gắng tối ưu hóa các truy vấn đúng cách; trong bất kỳ môi trường quan trọng nào trong kinh doanh, điều này có nghĩa là cái chết của ứng dụng vì bất kể bạn cẩn thận đến mức nào, mọi thứ sẽ thất bại vào một lúc nào đó.
zozo

1
có thể nhưng tùy chọn này cực kỳ hiệu quả trong việc nhập các bảng lớn và rất thực tế và nó có thể cho một số người biết làm thế nào họ có thể chèn dữ liệu nhanh hơn nhiều.
MSS
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.