Hiệu suất của bcp / BULK INSERT so với các thông số được định giá bằng bảng


84

Tôi sắp phải viết lại một số mã khá cũ bằng lệnh của SQL Server BULK INSERTvì lược đồ đã thay đổi và tôi nghĩ rằng có lẽ tôi nên nghĩ đến việc chuyển sang một thủ tục được lưu trữ với TVP thay thế, nhưng tôi đang tự hỏi tác dụng nó có thể có hiệu suất.

Một số thông tin cơ bản có thể giúp giải thích lý do tại sao tôi hỏi câu hỏi này:

  • Dữ liệu thực sự đến thông qua một dịch vụ web. Dịch vụ web ghi một tệp văn bản vào một thư mục chia sẻ trên máy chủ cơ sở dữ liệu, đến lượt nó thực hiện một BULK INSERT. Quá trình này ban đầu được triển khai trên SQL Server 2000 và tại thời điểm đó, thực sự không có giải pháp thay thế nào khác ngoài việc ghim vài trăm INSERTcâu lệnh tại máy chủ, đây thực sự là quá trình ban đầu và là một thảm họa về hiệu suất.

  • Dữ liệu được chèn hàng loạt vào một bảng dàn cố định và sau đó được hợp nhất vào một bảng lớn hơn nhiều (sau đó nó bị xóa khỏi bảng dàn).

  • Số lượng dữ liệu cần chèn là "lớn", nhưng không "khổng lồ" - thường là vài trăm hàng, có thể là 5-10 nghìn hàng trong một số trường hợp hiếm hoi. Vì vậy, cảm giác ruột của tôi là BULK INSERThoạt động không ghi nhật ký sẽ không tạo ra sự khác biệt lớn như vậy (nhưng tất nhiên tôi không chắc chắn, do đó câu hỏi).

  • Việc chèn thực sự là một phần của quy trình hàng loạt có đường ống lớn hơn nhiều và cần phải diễn ra nhiều lần liên tiếp; do đó hiệu suất rất quan trọng.

Những lý do tôi muốn thay thế BULK INSERTbằng TVP là:

  • Việc ghi tệp văn bản qua NetBIOS có lẽ đã tốn một khoảng thời gian và nó khá khủng khiếp từ góc độ kiến ​​trúc.

  • Tôi tin rằng bảng dàn dựng có thể (và nên) bị loại bỏ. Lý do chính là dữ liệu được chèn vào cần được sử dụng cho một vài bản cập nhật khác cùng lúc với việc chèn và cố gắng cập nhật từ bảng sản xuất lớn sẽ tốn kém hơn nhiều so với việc sử dụng một dàn gần như trống rỗng bàn. Với TVP, tham số về cơ bản bảng dàn, tôi có thể làm bất cứ điều gì tôi muốn với nó trước / sau khi chèn chính.

  • Tôi có thể làm được nhiều việc với kiểm tra dupe, mã dọn dẹp và tất cả chi phí liên quan đến chèn hàng loạt.

  • Không cần phải lo lắng về việc khóa tranh chấp trên bảng dàn hoặc tempdb nếu máy chủ nhận được một vài giao dịch này cùng một lúc (chúng tôi cố gắng tránh điều đó, nhưng nó vẫn xảy ra).

Rõ ràng là tôi sẽ lập hồ sơ về vấn đề này trước khi đưa bất cứ thứ gì vào sản xuất, nhưng tôi nghĩ có thể là ý kiến ​​hay khi hỏi trước khi dành toàn bộ thời gian đó, xem có ai có bất kỳ cảnh báo nghiêm khắc nào về việc sử dụng TVP cho mục đích này không.

Vì vậy - đối với bất kỳ ai đủ nhiệt tình với SQL Server 2008 đã thử hoặc ít nhất là điều tra điều này, thì phán quyết là gì? Đối với các trường hợp chèn, giả sử, vài trăm đến vài nghìn hàng, xảy ra khá thường xuyên, các TVP có cắt cải không? Có sự khác biệt đáng kể về hiệu suất so với chèn số lượng lớn không?


Cập nhật: Bây giờ với ít hơn 92% dấu hỏi!

(AKA: Kết quả kiểm tra)

Kết quả cuối cùng bây giờ là trong quá trình sản xuất sau một quá trình triển khai 36 giai đoạn. Cả hai giải pháp đã được thử nghiệm rộng rãi:

  • Xé ra mã thư mục chia sẻ và sử dụng SqlBulkCopylớp học trực tiếp;
  • Chuyển sang Quy trình đã Lưu trữ với TVP.

Để người đọc có thể biết chính xác những gì đã được kiểm tra, để giảm bớt bất kỳ nghi ngờ nào về độ tin cậy của dữ liệu này, đây là giải thích chi tiết hơn về những gì quá trình nhập này thực sự làm :

  1. Bắt đầu với một chuỗi dữ liệu tạm thời thường là khoảng 20-50 điểm dữ liệu (mặc dù đôi khi có thể lên đến vài trăm);

  2. Thực hiện một loạt các xử lý điên rồ trên nó hầu như độc lập với cơ sở dữ liệu. Quá trình này diễn ra song song, do đó, khoảng 8-10 trình tự trong (1) đang được xử lý cùng một lúc. Mỗi quá trình song song tạo ra 3 chuỗi bổ sung.

  3. Lấy tất cả 3 trình tự và trình tự ban đầu và kết hợp chúng thành một lô.

  4. Kết hợp các lô từ tất cả 8-10 tác vụ xử lý hiện đã hoàn thành thành một siêu lô lớn.

  5. Nhập nó bằng cách sử dụng BULK INSERTchiến lược (xem bước tiếp theo) hoặc chiến lược TVP (chuyển sang bước 8).

  6. Sử dụng SqlBulkCopylớp để kết xuất toàn bộ siêu lô vào 4 bảng dàn cố định.

  7. Chạy Thủ tục đã lưu trữ (a) thực hiện một loạt các bước tổng hợp trên 2 trong số các bảng, bao gồm một số JOINđiều kiện, và sau đó (b) thực hiện a MERGEtrên 6 bảng sản xuất bằng cách sử dụng cả dữ liệu tổng hợp và không tổng hợp. (Đã kết thúc)

    HOẶC LÀ

  8. Tạo 4 DataTableđối tượng chứa dữ liệu được hợp nhất; 3 trong số đó chứa các loại CLR không được hỗ trợ đúng cách bởi ADO.NET TVP, vì vậy chúng phải được đưa vào dưới dạng biểu diễn chuỗi, điều này làm ảnh hưởng đến hiệu suất một chút.

  9. Cung cấp các TVP vào một Quy trình đã lưu trữ, về cơ bản thực hiện cùng một quy trình xử lý như (7), nhưng trực tiếp với các bảng đã nhận. (Đã kết thúc)

Các kết quả gần nhau một cách hợp lý, nhưng phương pháp TVP cuối cùng hoạt động trung bình tốt hơn, ngay cả khi dữ liệu vượt quá 1000 hàng một lượng nhỏ.

Lưu ý rằng quá trình nhập này được chạy nhiều nghìn lần liên tiếp, vì vậy rất dễ dàng để có được thời gian trung bình chỉ đơn giản bằng cách đếm xem đã mất bao nhiêu giờ (vâng, giờ) để hoàn thành tất cả các hợp nhất.

Ban đầu, một quá trình hợp nhất trung bình mất gần đúng 8 giây để hoàn thành (trong điều kiện tải bình thường). Loại bỏ k bùn NetBIOS và chuyển sang SqlBulkCopygiảm thời gian xuống gần chính xác 7 giây. Việc chuyển sang TVP tiếp tục giảm thời gian xuống còn 5,2 giây mỗi đợt. Đó là sự cải thiện 35% về thông lượng cho một quy trình có thời gian chạy được tính bằng giờ - vì vậy không tệ chút nào. Nó cũng được cải thiện ~ 25% SqlBulkCopy.

Tôi thực sự khá tự tin rằng sự cải thiện thực sự còn nhiều hơn thế này. Trong quá trình thử nghiệm, rõ ràng là sự hợp nhất cuối cùng không còn là đường dẫn quan trọng nữa; thay vào đó, Dịch vụ Web đang thực hiện tất cả quá trình xử lý dữ liệu đang bắt đầu bị giới hạn bởi số lượng yêu cầu đến. Cả CPU và I / O cơ sở dữ liệu đều không thực sự hoạt động tối đa và không có hoạt động khóa đáng kể nào. Trong một số trường hợp, chúng tôi thấy khoảng cách một vài giây nhàn rỗi giữa các lần hợp nhất liên tiếp. Có một khoảng cách nhỏ, nhưng nhỏ hơn nhiều (nửa giây hoặc lâu hơn) khi sử dụng SqlBulkCopy. Nhưng tôi cho rằng điều đó sẽ trở thành một câu chuyện cho một ngày khác.

Kết luận: Các tham số được định giá bằng bảng thực sự hoạt động tốt hơn các BULK INSERThoạt động đối với các quy trình nhập + chuyển đổi phức tạp hoạt động trên các tập dữ liệu cỡ trung bình.


Tôi muốn nói thêm một điểm khác, chỉ để xoa dịu bất kỳ sự e ngại nào của một bộ phận những người là chuyên gia về bàn dàn dựng. Theo một cách nào đó, toàn bộ dịch vụ này là một quá trình dàn dựng khổng lồ. Mỗi bước của quy trình đều được kiểm tra kỹ lưỡng, vì vậy chúng tôi không cần một bảng phân tích để xác định lý do tại sao một số hợp nhất cụ thể không thành công (mặc dù trong thực tế, điều này hầu như không bao giờ xảy ra). Tất cả những gì chúng ta phải làm là đặt một cờ gỡ lỗi trong dịch vụ và nó sẽ phá vỡ trình gỡ lỗi hoặc kết xuất dữ liệu của nó vào một tệp thay vì cơ sở dữ liệu.

Nói cách khác, chúng ta đã có quá đủ cái nhìn sâu sắc về quy trình và không cần đến sự an toàn của một bảng dàn dựng; lý do duy nhất mà chúng tôi có bảng dàn dựng ngay từ đầu là để tránh làm hỏng tất cả các câu lệnh INSERTUPDATEmà chúng tôi sẽ phải sử dụng nếu không. Trong quá trình ban đầu, dữ liệu phân đoạn chỉ tồn tại trong bảng phân đoạn trong một phần giây, do đó, nó không có giá trị gì về mặt bảo trì / bảo trì.

Cũng xin lưu ý rằng chúng tôi không thay thế mọi BULK INSERTthao tác bằng TVP. Một số thao tác xử lý lượng dữ liệu lớn hơn và / hoặc không cần thực hiện bất kỳ điều gì đặc biệt với dữ liệu ngoài việc ném nó vào DB vẫn được sử dụng SqlBulkCopy. Tôi không gợi ý rằng TVP là một liều thuốc chữa bách bệnh về hiệu suất, chỉ là chúng đã thành công SqlBulkCopytrong trường hợp cụ thể này liên quan đến một số chuyển đổi giữa giai đoạn ban đầu và hợp nhất cuối cùng.

Vì vậy, bạn có nó. Point chuyển đến TToni để tìm liên kết phù hợp nhất, nhưng tôi cũng đánh giá cao các phản hồi khác. Cảm ơn một lần nữa!


Đây là một câu hỏi tuyệt vời của bản thân, tôi cảm thấy phần cập nhật phải ở trong một câu trả lời;)
Marc.2377

Câu trả lời:


10

Tôi thực sự chưa có kinh nghiệm với TVP, tuy nhiên, có một biểu đồ so sánh hiệu suất tuyệt vời so với BULK INSERT trong MSDN ở đây .

Họ nói rằng BULK INSERT có chi phí khởi động cao hơn, nhưng nhanh hơn sau đó. Trong một kịch bản máy khách từ xa, họ vẽ dòng khoảng 1000 hàng (đối với logic máy chủ "đơn giản"). Đánh giá từ mô tả của họ, tôi sẽ nói rằng bạn sẽ ổn khi sử dụng TVP. Hiệu suất đạt được - nếu có - có lẽ là không đáng kể và lợi ích kiến ​​trúc có vẻ rất tốt.

Chỉnh sửa: Một lưu ý nhỏ là bạn có thể tránh tệp cục bộ máy chủ và vẫn sử dụng bản sao hàng loạt bằng cách sử dụng đối tượng SqlBulkCopy. Chỉ cần điền một DataTable và đưa nó vào "WriteToServer" -Method của một phiên bản SqlBulkCopy. Dễ sử dụng và rất nhanh chóng.


Cảm ơn liên kết, điều đó thực sự khá hữu ích vì MS dường như đề xuất TVP khi dữ liệu cung cấp nguồn cấp dữ liệu logic phức tạp (mà nó làm) và chúng tôi cũng có khả năng quay số tăng hoặc giảm kích thước lô để chúng tôi không đi quá xa Điểm đau hàng 1k. Dựa trên điều này, có thể đáng giá thời gian để ít nhất hãy thử và xem, ngay cả khi nó kết thúc quá chậm.
Aaronaught

Vâng, liên kết là thú vị. @Aaronaught - trong những tình huống như thế này, luôn đáng để khám phá và phân tích hiệu suất của các phương pháp tiếp cận tiềm năng, vì vậy tôi rất muốn nghe phát hiện của bạn!
AdaTheDev

7

Biểu đồ được đề cập liên quan đến liên kết được cung cấp trong câu trả lời của @ TToni cần được đưa vào ngữ cảnh. Tôi không chắc có bao nhiêu nghiên cứu thực tế về các khuyến nghị đó (cũng lưu ý rằng biểu đồ dường như chỉ có sẵn trong các phiên bản 20082008 R2tài liệu đó).

Mặt khác, có báo cáo chính thức này từ Nhóm tư vấn khách hàng của SQL Server: Tối đa hóa thông lượng với TVP

Tôi đã sử dụng TVP từ năm 2009 và ít nhất theo kinh nghiệm của tôi nhận thấy rằng đối với bất kỳ thứ gì khác ngoài việc chèn đơn giản vào bảng đích mà không cần thêm logic nào (hiếm khi xảy ra trường hợp này), thì TVP thường là lựa chọn tốt hơn.

Tôi có xu hướng tránh các bảng sắp xếp vì việc xác thực dữ liệu nên được thực hiện ở lớp ứng dụng. Bằng cách sử dụng TVP, điều đó có thể dễ dàng được điều chỉnh và Biến bảng TVP trong quy trình được lưu trữ, về bản chất, là một bảng dàn được cục bộ hóa (do đó không có xung đột với các quá trình khác đang chạy cùng lúc như khi bạn sử dụng bảng thực để dàn ).

Về thử nghiệm được thực hiện trong Câu hỏi, tôi nghĩ rằng nó có thể được chứng minh là còn nhanh hơn những gì được tìm thấy ban đầu:

  1. Bạn không nên sử dụng DataTable, trừ khi ứng dụng của bạn sử dụng nó ngoài việc gửi các giá trị đến TVP. Sử dụng IEnumerable<SqlDataRecord>giao diện nhanh hơn và sử dụng ít bộ nhớ hơn vì bạn không sao chép bộ sưu tập trong bộ nhớ chỉ để gửi nó đến DB. Tôi có tài liệu này ở những nơi sau:
  2. TVP là các biến trong bảng và do đó không duy trì thống kê. Có nghĩa là, chúng chỉ báo cáo có 1 hàng cho Trình tối ưu hoá Truy vấn. Vì vậy, trong chương trình của bạn, hãy:
    • Sử dụng biên dịch lại cấp câu lệnh cho bất kỳ truy vấn nào bằng TVP cho bất kỳ thứ gì khác ngoài một SELECT đơn giản: OPTION (RECOMPILE)
    • Tạo bảng tạm thời cục bộ (tức là bảng đơn #) và sao chép nội dung của TVP vào bảng tạm thời

4

Tôi nghĩ rằng tôi vẫn sẽ gắn bó với cách tiếp cận chèn hàng loạt. Bạn có thể thấy rằng tempdb vẫn bị ảnh hưởng khi sử dụng TVP có số hàng hợp lý. Đây là cảm giác ruột của tôi, tôi không thể nói rằng tôi đã kiểm tra hiệu suất của việc sử dụng TVP (mặc dù tôi cũng muốn nghe người khác đóng góp)

Bạn không đề cập đến việc bạn có sử dụng .NET hay không, nhưng cách tiếp cận mà tôi đã thực hiện để tối ưu hóa các giải pháp trước đó là thực hiện tải hàng loạt dữ liệu bằng cách sử dụng lớp SqlBulkCopy - trước đó bạn không cần phải ghi dữ liệu vào tệp. đang tải, chỉ cần cung cấp cho lớp SqlBulkCopy (ví dụ) một DataTable - đó là cách nhanh nhất để chèn dữ liệu vào DB. 5-10K hàng không phải là nhiều, tôi đã sử dụng điều này cho tối đa 750 nghìn hàng. Tôi nghi ngờ rằng nói chung, với vài trăm hàng, nó sẽ không tạo ra sự khác biệt lớn khi sử dụng TVP. Nhưng việc mở rộng quy mô sẽ bị giới hạn IMHO.

Có lẽ chức năng MERGE mới trong SQL 2008 sẽ mang lại lợi ích cho bạn?

Ngoài ra, nếu bảng dàn hiện tại của bạn là một bảng duy nhất được sử dụng cho mỗi trường hợp của quá trình này và bạn lo lắng về sự tranh chấp, v.v., bạn đã cân nhắc tạo một bảng dàn "tạm thời" nhưng vật lý mới mỗi lần, sau đó bỏ nó khi kết thúc với?

Lưu ý rằng bạn có thể tối ưu hóa việc tải vào bảng dàn dựng này, bằng cách điền nó mà không có bất kỳ chỉ mục nào. Sau đó, khi đã điền, hãy thêm bất kỳ chỉ mục bắt buộc nào vào thời điểm đó (FILLFACTOR = 100 để có hiệu suất đọc tối ưu, vì tại thời điểm này, nó sẽ không được cập nhật).


Tôi sử dụng .NET, và quá trình xảy ra trước đó SqlBulkCopyvà đơn giản là chưa bao giờ được thay đổi. Cảm ơn vì đã nhắc tôi về điều đó, nó có thể đáng để xem lại. MERGEcũng đã được sử dụng rộng rãi và các bảng tạm thời đã được thử một lần trước đó nhưng bị chậm hơn và khó quản lý hơn. Cảm ơn các đầu vào!
Aaronaught

-2

Bảng dàn dựng là tốt! Thực sự tôi không muốn làm theo cách nào khác. Tại sao? Vì quá trình nhập dữ liệu có thể thay đổi bất ngờ (Và thường theo những cách bạn không thể lường trước được, chẳng hạn như thời gian các cột vẫn được gọi là tên và họ nhưng có dữ liệu tên đầu tiên trong cột họ chẳng hạn, để chọn một ví dụ không một cách ngẫu nhiên.) Dễ dàng nghiên cứu vấn đề với bảng phân giai đoạn để bạn có thể xem chính xác dữ liệu nào trong các cột mà quá trình nhập đã xử lý. Tôi nghĩ khó tìm hơn khi bạn sử dụng bảng trong bộ nhớ. Tôi biết rất nhiều người làm công việc nhập khẩu để kiếm sống như tôi và tất cả họ đều khuyên bạn nên sử dụng bảng dàn. Tôi nghi ngờ có một lý do cho điều này.

Việc sửa một thay đổi giản đồ nhỏ đối với một quy trình làm việc sẽ dễ dàng hơn và ít tốn thời gian hơn so với việc thiết kế lại quy trình. Nếu nó đang hoạt động và không ai sẵn sàng trả hàng giờ để thay đổi nó, thì chỉ sửa những gì cần sửa do thay đổi lược đồ. Bằng cách thay đổi toàn bộ quy trình, bạn tạo ra nhiều lỗi mới tiềm năng hơn nhiều so với việc thực hiện một thay đổi nhỏ đối với quy trình làm việc đã được thử nghiệm, hiện có.

Và bạn sẽ thực hiện như thế nào với tất cả các nhiệm vụ dọn dẹp dữ liệu? Bạn có thể làm chúng theo cách khác, nhưng chúng vẫn cần phải được thực hiện. Một lần nữa, việc thay đổi quy trình theo cách bạn mô tả là rất rủi ro.

Cá nhân tôi nghe có vẻ như bạn chỉ bị xúc phạm khi sử dụng các kỹ thuật cũ hơn là có cơ hội chơi với đồ chơi mới. Bạn dường như không có cơ sở thực sự để muốn thay đổi khác ngoài chèn số lượng lớn là 2000.


27
SQL 2008 đã tồn tại được 2 năm và quá trình này đã tồn tại lâu đời, và đây là lần đầu tiên tôi nghĩ đến việc thay đổi nó. Nhận xét khó hiểu ở cuối có thực sự cần thiết không?
Aaronaught
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.