SQL: Điều gì làm chậm INSERT nếu không phải CPU hoặc IO?


19

Chúng tôi có một cơ sở dữ liệu cho một sản phẩm nặng. Chúng tôi vừa mua một máy chủ mới có ổ SSD để trợ giúp. Trước sự ngạc nhiên của chúng tôi, việc chèn vào không nhanh hơn trên máy cũ của chúng tôi với dung lượng lưu trữ chậm hơn nhiều. Trong quá trình đo điểm chuẩn, chúng tôi nhận thấy rằng tốc độ IO được thể hiện bởi quy trình SQL Server là rất thấp.

Ví dụ: tôi đã chạy tập lệnh được tìm thấy trên trang này , ngoại trừ việc tôi đã thêm BEGIN TRAN và CAM KẾT xung quanh vòng lặp. Tốt nhất tôi có thể thấy mức sử dụng đĩa đạt tới 7bb / giây, trong khi CPU chỉ chạm mức 5%. Máy chủ đã cài đặt 64Gb và đang sử dụng 10. Tổng thời gian chạy là 2 phút 15 giây cho lần gọi đầu tiên xuống còn khoảng 1 phút cho các cuộc gọi tiếp theo. Cơ sở dữ liệu đang phục hồi đơn giản và không hoạt động trong quá trình thử nghiệm. Tôi đánh rơi bàn giữa mỗi cuộc gọi.

Tại sao một kịch bản đơn giản như vậy rất chậm? Phần cứng hầu như không được sử dụng. Cả hai công cụ đo điểm chuẩn dành riêng cho đĩa và SQLIO đều chỉ ra rằng SSD hoạt động chính xác với tốc độ lên tới 500Mb / giây cho cả đọc và ghi. Tôi hiểu rằng việc ghi ngẫu nhiên chậm hơn so với ghi tuần tự, nhưng tôi sẽ mong đợi một thao tác chèn đơn giản như thế này, vào một bảng không có lập chỉ mục cụm, sẽ nhanh hơn nhiều.

Cuối cùng, kịch bản của chúng tôi phức tạp hơn nhiều, nhưng tôi cảm thấy rằng trước tiên tôi cần phải hiểu một trường hợp đơn giản. Tóm lại, ứng dụng của chúng tôi xóa dữ liệu cũ, sau đó sử dụng SqlBulkCopy để sao chép dữ liệu mới vào bảng phân tầng, thực hiện một số bộ lọc và cuối cùng sử dụng MERGE và / hoặc INSERT INTO tùy theo trường hợp để sao chép dữ liệu vào các bảng cuối cùng.

-> EDIT 1: Tôi đã làm theo quy trình được liên kết bởi Martin Smith và tôi đã nhận được kết quả như sau:

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

Tôi thấy NETWORK_IO kỳ lạ mất phần lớn thời gian, vì không có kết quả nào để hiển thị và không có dữ liệu để chuyển bất kỳ nơi nào khác ngoài các tệp SQL. Loại NETWORK_IO có bao gồm tất cả IO không?

-> EDIT 2: Tôi đã tạo một đĩa RAM 20Gb và gắn cơ sở dữ liệu từ đó. Thời gian tốt nhất tôi có trên SSD là 48 giây, với đĩa RAM giảm xuống còn 37 giây. NETWORK_IO vẫn là sự chờ đợi lớn nhất. Tốc độ ghi tối đa vào đĩa RAM là khoảng 250Mb / giây trong khi nó có thể thực hiện nhiều gigabyte mỗi giây. Nó vẫn không sử dụng nhiều CPU, vậy cái gì đang giữ SQL?



3
những NETWORK_IOcó thể là từ "1 row (s) bị ảnh hưởng" 3 triệu tin nhắn được gửi trở lại. Bạn đã thử thêm SET NOCOUNT ONvào kịch bản?
Martin Smith

Có, tôi đã thêm NOCOUNT.
Djof

2
Lạ thật. Tôi sẽ không mong đợi nhiều vào cách thức hoạt động của mạng. Bạn đã xóa các tập tin sự kiện mở rộng cũ giữa các lần chạy? Kịch bản đọc chúng sử dụng một thẻ hoang dã EE_WaitStats*.xelđể những cái cũ sẽ làm ô nhiễm kết quả của bạn.
Martin Smith

Cuộc gọi tốt, tôi sẽ cập nhật kết quả vào ngày mai.
Djof

Câu trả lời:


9

Tôi biết đó là một Câu hỏi cũ nhưng điều này vẫn có thể giúp người tìm kiếm và nó là một vấn đề xuất hiện mọi lúc.

Lý do chính khiến bạn tăng trần hiệu suất mà không gặp bất kỳ tắc nghẽn tài nguyên nào là do bạn đã đạt đến giới hạn của những gì có thể xử lý trong một luồng duy nhất. Vòng lặp không được xử lý song song, nhưng tất cả các thao tác chèn đều được thực hiện một cách an toàn.

Trong trường hợp của tôi, phải mất 36 giây để chèn 3 triệu hàng. Điều đó có nghĩa là 36/30000000 = 0,000012 giây mỗi hàng. Điều đó khá nhanh. Trên hệ thống của tôi, chỉ cần 0,000012 để trải qua tất cả các bước cần thiết.

Cách duy nhất để hoàn thành nó nhanh hơn là bắt đầu một phiên thứ hai song song.

Nếu tôi bắt đầu 2 phiên song song cả hai thực hiện 15 triệu lần chèn. Cả hai kết thúc sau 18 giây. Tôi có thể mở rộng ra nhiều hơn, nhưng thiết lập thử nghiệm hiện tại của tôi đang đạt 95% cpu với hai phiên song song, do đó, làm 3 sẽ làm sai lệch kết quả vì tôi sẽ gặp phải tình trạng nghẽn cổ chai CPU.

Nếu tôi bắt đầu 2 phiên song song cả hai chèn 3 triệu hàng, cả hai đều kết thúc sau 39 giây. bây giờ là 6 triệu hàng trong 39 giây.

Được rồi, điều đó vẫn để lại cho chúng tôi với NETWORK_IO chờ đợi hiển thị.

Chờ đợi NETWORK_IO được thêm vào bởi thực tế là bạn đang sử dụng các sự kiện mở rộng để theo dõi chúng. Trong trường hợp của tôi, phần chèn mất 36 giây (trên avg). Khi sử dụng cách sự kiện mở rộng (từ liên kết ở trên trong bình luận đầu tiên), đây là những gì được đăng ký:

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

Bạn có thể thấy rằng 68 giây NETWORK_IO đã được đăng ký. Nhưng vì vòng lặp chèn là một hành động đơn luồng mất 36 giây, nên điều này là không thể. (Có, nhiều luồng được sử dụng, nhưng các thao tác là nối tiếp, không bao giờ song song, do đó bạn không thể đạt được nhiều thời gian chờ hơn so với tổng thời lượng của truy vấn)

Nếu tôi không sử dụng các sự kiện mở rộng mà chỉ các DMV thống kê chờ trong một trường hợp yên tĩnh (chỉ với tôi đang chạy chèn) tôi sẽ nhận được điều này:

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

Vì vậy, NETWORK_IO bạn đã thấy trong nhật ký sự kiện mở rộng, không liên quan đến vòng lặp chèn của bạn. (Nếu bạn không bật nocount, bạn sẽ có mạng không đồng bộ khổng lồ IO chờ, +1 Martin)

Tuy nhiên tôi không biết tại sao NETWORK_IO xuất hiện trong dấu vết sự kiện mở rộng. Chắc chắn việc ghi ra một mục tiêu tệp không đồng bộ của các sự kiện sẽ tích lũy ASYNC_NETWORK_IO, nhưng chắc chắn tất cả điều này được thực hiện trên một SPID khác biệt sau đó là một sự kiện chúng tôi đang lọc. Tôi có thể tự hỏi đây là một câu hỏi mới)


1
"Bạn đang chạm trần hiệu năng mà không gặp bất kỳ tắc nghẽn tài nguyên nào là do bạn đã đạt đến giới hạn của những gì có thể xử lý trong một luồng duy nhất": bạn đang mô tả nút cổ chai CPU 100% (trên một lõi). Nếu không có nút cổ chai, thì hệ thống sẽ hoạt động nhanh hơn, vì vậy cần phải có một cái gì đó khác.
Remus Rusanu

Câu trả lời của bạn rất nhiều thông tin Edward. Có vẻ như song song là giải pháp cho vấn đề của chúng tôi, chúng tôi đã làm việc về vấn đề đó, mặc dù nó đòi hỏi phải thay đổi bố cục cơ sở dữ liệu của chúng tôi. Tuy nhiên, giống như Remus, tôi vẫn tò mò tại sao máy dường như không sử dụng tất cả (của một) tài nguyên CPU hoặc đĩa.
Djof

9

Thông thường bạn bắt đầu bằng cách nhìn vào sys.dm_exec_requests, đặc biệt là tại wait_time, wait_typewait_resourcecho yêu cầu INSERT của bạn (s). Điều này sẽ đưa ra một dấu hiệu rõ ràng những gì đang chặn INSERT của bạn. Các kết quả sẽ cho biết liệu có tranh chấp khóa, sự kiện tăng trưởng tệp, chờ đợi đăng nhập, tranh chấp phân bổ (biểu hiện như tranh chấp chốt trang PFS), v.v. Sau khi bạn đo lường, hãy cập nhật câu hỏi của bạn cho phù hợp. Tôi mạnh mẽ yêu cầu bạn dừng lại và đọc Waits và Queues xử lý sự cố phương pháp trước khi bạn tiến hành.


3

Tôi đã chạy tập lệnh thử nghiệm tại trang được liên kết trong OP với BEGIN TRAN / CAMIT xung quanh vòng lặp. Trên máy của tôi, phải mất 1:28 để hoàn thành lần đầu tiên.

Sau đó, tôi di chuyển hai lệnh này bên ngoài vòng lặp:

SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

Nó hoàn thành trong 28 giây sau đó.

Tôi không biết chắc chắn điều gì đang xảy ra, nhưng tôi đoán có thể có một loại ngủ nào đó trong RAND()mã, có lẽ là một phần của thuật toán họ đang sử dụng để tạo entropy (số ngẫu nhiên tốt hơn).

FWIW, SSD không phải lúc nào cũng là công nghệ tốt nhất cho các ứng dụng nặng. Để có hiệu suất tốt nhất, hãy đảm bảo nhật ký DB của bạn nằm trên một ký tự ổ đĩa khác với dữ liệu DB, tệp nhật ký được phát triển trước với kích thước tối đa của nó và không bao giờ cắt bớt nhật ký.


Cảm ơn bạn đã nhập RickNZ. Tôi đã không nhận được kết quả nhanh hơn bằng cách di chuyển mã ra khỏi vòng lặp. Đợi tôi đã quan sát là nếu bạn chạy nó nhiều lần thì nó sẽ nhanh hơn, đó có thể là những gì bạn đã trải nghiệm. Tôi biết SSD không phải là đạn bạc, nhưng tôi vẫn cảm thấy hiệu suất không như mong đợi.
Djof

1

Một DMV khác mà tôi sử dụng để xác định sự chậm chạp là sys.dm_os_waiting_t Nhiệm vụ . Nếu truy vấn của bạn không cần nhiều CPU, thì bạn có thể tìm thêm thông tin về sự chờ đợi từ DMV này.


0

Tôi đang kiểm tra danh sách các sự kiện chờ cho sql 2008 và tôi không thấy NETWORK_IO được liệt kê: http://technet.microsoft.com/en-us/l Library / ms179984 (v = sql.100) .aspx

Tôi nghĩ rằng NETWORK_IO hiện chỉ được liệt kê là ASYNC_NETWORK_IO, vì vậy tôi muốn hỏi liệu bạn có thể kiểm tra lại phiên bản SQL của mình không, vì tôi chỉ tò mò về việc tại sao sự kiện chờ đợi đó lại xuất hiện cho phiên bản đó.

Đối với mạng chờ xuất hiện ở tất cả, có, điều đó có thể xảy ra ngay cả khi bạn đang làm việc trên một máy chủ độc lập. Bạn đã kiểm tra các cài đặt cho card mạng của bạn chưa? Tôi tự hỏi nếu họ là một vấn đề.

Vào cuối ngày, chỉ có một vài tắc nghẽn tài nguyên có thể xảy ra: bộ nhớ, CPU, I / O đĩa, mạng và khóa. Bạn đã chỉ ra rằng CPU và I / O không phải là vấn đề và bạn có một sự kiện chờ đợi của NETWORK_IO, vì vậy tôi khuyên bạn nên xem xét các thẻ NIC đó trước.


1
Điều NETWORK_IOnày được hiển thị bởi vì OP đang sử dụng các sự kiện mở rộng. Nó không bao giờ được cập nhật trongsys.dm_xe_map_values
Martin Smith

Tôi đang nghĩ cùng một SQLRockstar, chỉ là những gì có thể xảy ra. Tôi đã cố gắng vô hiệu hóa các card mạng hoàn toàn. Martin chỉ ra rằng một số tập tin cũ có thể vẫn còn ở đó, tôi sẽ cập nhật kết quả vào ngày mai để xem nó có thay đổi gì không.
Djof

ngoài ra, nó có thể hữu ích nếu chúng ta có thể thấy các kế hoạch thực hiện cho các câu lệnh.
SQLRockstar
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.