Cập nhật SQL mất một thời gian rất dài / sử dụng đĩa cao trong nhiều giờ


8

Vâng, nó có vẻ như là một vấn đề rất chung chung, nhưng tôi chưa thể thu hẹp nó xuống nhiều.

Vì vậy, tôi có một câu lệnh CẬP NHẬT trong tệp bó sql:

UPDATE A
SET A.X = B.X
FROM A JOIN B ON A.B_ID = B.ID

B có 40k hồ sơ, A có 4M hồ sơ và chúng có liên quan 1 đến n thông qua A.B_ID, mặc dù không có FK giữa hai.

Vì vậy, về cơ bản tôi đang tính toán trước một trường cho mục đích khai thác dữ liệu. Mặc dù tôi đã thay đổi tên của các bảng cho câu hỏi này, tôi đã không thay đổi tuyên bố, nó thực sự đơn giản.

Điều này mất nhiều giờ để chạy, vì vậy tôi quyết định hủy bỏ mọi thứ. DB đã bị hỏng, vì vậy tôi đã xóa nó, khôi phục bản sao lưu mà tôi đã làm ngay trước khi chạy câu lệnh và quyết định đi sâu vào chi tiết bằng một con trỏ:

DECLARE CursorB CURSOR FOR SELECT ID FROM B ORDER BY ID DESC -- Descending order
OPEN CursorB 
DECLARE @Id INT
FETCH NEXT FROM CursorB INTO @Id

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @Msg VARCHAR(50) = 'Updating A for B_ID=' + CONVERT(VARCHAR(10), @Id)
    RAISERROR(@Msg, 10, 1) WITH NOWAIT

    UPDATE A
    SET A.X = B.X
    FROM A JOIN B ON A.B_ID = B.ID
    WHERE B.ID = @Id

    FETCH NEXT FROM CursorB INTO @Id
END

Bây giờ tôi có thể thấy nó chạy với một tin nhắn với id giảm dần. Điều gì xảy ra là mất khoảng 5 phút để đi từ id = 40k đến id = 13

Và sau đó tại id 13, vì một số lý do, nó dường như bị treo. DB không có bất kỳ kết nối nào với nó ngoài SSMS, nhưng nó không thực sự bị treo:

  • ổ cứng đang chạy liên tục nên chắc chắn nó đang làm gì đó (tôi đã kiểm tra trong Process Explorer rằng đó thực sự là quá trình sqlserver.exe sử dụng nó)
  • Tôi đã chạy sp_who2, tìm thấy SPID (70) của phiên SUSPENDED sau đó chạy tập lệnh sau:

    chọn * từ sys.dm_exec_Vquests r tham gia sys.dm_os_t Nhiệm vụ t trên r.session_id = t.session_id trong đó r.session_id = 70

Điều này mang lại cho tôi Wait_type, PAGEIOLATCH_SH hầu hết thời gian nhưng đôi khi thực sự thay đổi thành WRITE_COMPLETION, điều mà tôi đoán xảy ra khi nó xóa nhật ký

  • tệp nhật ký là 1.6GB khi tôi khôi phục DB (và khi nó chuyển sang id 13), giờ là 3,5GB

Thông tin khác có thể hữu ích:

  • số lượng bản ghi trong bảng A cho B_ID 13 không lớn (14)
  • Đồng nghiệp của tôi không gặp vấn đề tương tự trên máy của cô ấy, với một bản sao của DB này (từ một vài tháng trước) với cấu trúc tương tự.
  • bảng A là bảng lớn nhất trong DB
  • Nó có một số chỉ mục và một số khung nhìn được lập chỉ mục sử dụng nó.
  • Không có người dùng nào khác trên DB, đó là cục bộ và không có ứng dụng nào đang sử dụng nó.
  • Tệp LDF không bị giới hạn về kích thước.
  • Mô hình khôi phục là SIMPLE, mức độ tương thích là 100
  • Procmon không cung cấp cho tôi nhiều thông tin: sqlserver.exe đang đọc và viết rất nhiều từ các tập tin MDF và LDF.

Tôi vẫn đang chờ nó kết thúc (đã 1h30) nhưng tôi đã hy vọng rằng có lẽ ai đó sẽ cho tôi một số hành động khác mà tôi có thể cố gắng khắc phục sự cố này.

Đã chỉnh sửa: thêm trích xuất từ ​​nhật ký procmon

15:24:02.0506105    sqlservr.exe    1760    ReadFile    C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TA.mdf  SUCCESS Offset: 5,498,732,544, Length: 8,192, I/O Flags: Non-cached, Priority: Normal
15:24:02.0874427    sqlservr.exe    1760    WriteFile   C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TA.mdf  SUCCESS Offset: 6,225,805,312, Length: 16,384, I/O Flags: Non-cached, Write Through, Priority: Normal
15:24:02.0884897    sqlservr.exe    1760    WriteFile   C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TA_1.LDF    SUCCESS Offset: 4,589,289,472, Length: 8,388,608, I/O Flags: Non-cached, Write Through, Priority: Normal

Từ việc sử dụng TRANG DBCC, dường như bạn đang đọc và ghi vào các trường trông giống như bảng A (hoặc một trong các chỉ mục của nó), nhưng đối với B_ID khác nhau thì 13. Có thể xây dựng lại các chỉ mục không?

Chỉnh sửa 2: kế hoạch thực hiện

Vì vậy, tôi đã hủy truy vấn (thực sự đã xóa DB và các tệp của nó sau đó khôi phục nó) và kiểm tra kế hoạch thực hiện cho:

UPDATE A
SET A.X = B.X
FROM A JOIN B ON A.B_ID = B.ID
WHERE B.ID = 13

Kế hoạch thực hiện (ước tính) là giống với bất kỳ B.ID nào và có vẻ khá đơn giản. Mệnh đề WHERE sử dụng một tìm kiếm chỉ mục trên một chỉ mục không được nhóm của B, THAM GIA sử dụng một tìm kiếm chỉ mục được nhóm trên cả hai PK của các bảng. Chỉ mục cụm tìm kiếm trên A sử dụng song song (x7) và chiếm 90% thời gian của CPU.

Quan trọng hơn, thực sự thực hiện truy vấn với ID 13 là ngay lập tức.

Chỉnh sửa 3: phân mảnh chỉ số

Cấu trúc của các chỉ mục như sau:

B có một PK cụm (không phải trường ID) và một chỉ mục duy nhất không phân cụm, trường đầu tiên là B.ID - chỉ mục thứ hai này dường như luôn được sử dụng.

A có một cụm PK (trường không liên quan).

Ngoài ra còn có 7 chế độ xem trên A (tất cả bao gồm trường AX), mỗi chế độ có PK riêng và một chỉ mục khác cũng bao gồm trường AX

Các khung nhìn được lọc (với các trường không nằm trong phương trình này), vì vậy tôi nghi ngờ có bất kỳ cách nào CẬP NHẬT A sẽ sử dụng chính các khung nhìn. Nhưng họ có một chỉ mục bao gồm AX, vì vậy thay đổi AX có nghĩa là viết 7 lượt xem và 7 chỉ mục họ có bao gồm trường.

Mặc dù CẬP NHẬT dự kiến ​​sẽ chậm hơn cho việc này, nhưng không có lý do tại sao một ID cụ thể sẽ dài hơn nhiều so với các ID khác.

Tôi đã kiểm tra sự phân mảnh cho tất cả các chỉ mục, tất cả đều ở mức <0,1%, ngoại trừ các chỉ số phụ của các lượt xem , tất cả nằm trong khoảng từ 25% đến 50%. Các yếu tố điền cho tất cả các chỉ mục có vẻ ổn, từ 90% đến 95%.

Tôi sắp xếp lại tất cả các chỉ mục phụ và chạy lại kịch bản của mình.

Nó vẫn bị treo, nhưng ở một điểm khác:

...
(0 row(s) affected)

        Updating A for B_ID=14

(4 row(s) affected)

Trong khi trước đây, nhật ký tin nhắn trông như thế này:

...
(0 row(s) affected)

        Updating A for B_ID=14

(4 row(s) affected)

        Updating A for B_ID=13

Điều này thật kỳ lạ, bởi vì nó có nghĩa là nó thậm chí không bị treo ở cùng một điểm trong WHILEvòng lặp. Phần còn lại trông giống nhau: cùng một dòng CẬP NHẬT đang chờ trong sp_who2, cùng loại chờ PAGEIOLATCH_EX và cùng sử dụng HD nặng từ sqlserver.exe.

Bước tiếp theo là xóa tất cả các chỉ mục và chế độ xem và tạo lại chúng tôi nghĩ.

Chỉnh sửa 4: xóa sau đó xây dựng lại các chỉ mục

Vì vậy, tôi đã xóa tất cả các chế độ xem được lập chỉ mục mà tôi có trên bảng (7 trong số chúng, 2 chỉ mục cho mỗi chế độ xem bao gồm cả các chế độ xem theo cụm). Tôi đã chạy tập lệnh ban đầu (không có con trỏ) và nó thực sự chạy trong 5 phút.

Vì vậy, vấn đề của tôi bắt nguồn từ sự tồn tại của các chỉ số này.

Tôi đã tạo lại các chỉ mục của mình sau khi chạy bản cập nhật và mất 16 phút.

Bây giờ tôi hiểu các chỉ mục cần có thời gian để xây dựng lại, và tôi thực sự ổn với nhiệm vụ hoàn thành mất 20 phút.

Điều tôi vẫn không hiểu là tại sao khi tôi chạy bản cập nhật mà không xóa các chỉ mục trước, phải mất vài giờ, nhưng khi tôi xóa chúng trước rồi tạo lại chúng, phải mất 20 phút. Không nên mất cùng một lúc?


1
Bất cứ điều gì trong bản ghi lỗi SQL Server? Ngoài ra từ procmon các offset trong tập tin mà nó đang viết là gì? Bạn có thể chia cho 8.192 để lấy trang và sau đó sử dụng DBCC PAGEđể xem những gì đang được viết.
Martin Smith

3,5 GB có vẻ như dung lượng RAM tối đa mà hoạt động của cửa sổ 32 bit có thể xử lý .. nguy hiểm?
tschmit007

@MartinSmith Hoàn toàn không có gì kể từ khi tôi khôi phục Nhật ký máy chủ SQL SSMS và không có gì trong nhật ký sự kiện Windows
GFK

Các chỉ mục của bạn trên bảng A trông như thế nào (cột nào, v.v.)? Họ có bị phân mảnh không?
Stuart Ainsworth

@ tschmit007 Phiên bản Dev SQL 2008 R2 x64 trên Win Server 2008 R2 x64. Nó là một VM tự chạy trên Hyper-V (máy chủ lưu trữ là 2008 R2 x64); VM có bộ nhớ vật lý 4.2GB được sử dụng trong số 5GB và 4.6GB cam kết tối đa 10GB; máy chủ lưu trữ có bộ nhớ vật lý 7.2GB được sử dụng trong 8GB và 7.8 cam kết tối đa 16GB. Cả hai máy đều chậm hơn do sử dụng HD nhưng không bị tắc.
GFK

Câu trả lời:


0
  1. Dán với lệnh CẬP NHẬT. HIỆN TẠI sẽ chậm hơn cho những gì bạn đang cố gắng làm.
  2. Thả / vô hiệu hóa tất cả các chỉ mục, bao gồm cả các chỉ mục cho các khung nhìn được lập chỉ mục. Nếu bạn có khóa ngoại trên AX, hãy bỏ nó.
  3. Tạo chỉ mục sẽ chỉ chứa A.B_ID và một chỉ mục khác cho B.ID.
  4. Mặc dù bạn đang sử dụng mô hình Phục hồi đơn giản, giao dịch cuối cùng sẽ luôn ở trong nhật ký giao dịch trước khi được chuyển vào đĩa. Đó là lý do tại sao bạn cần phát triển trước nhật ký giao dịch của mình và đặt nó để tăng thêm một số tiền lớn hơn (ví dụ: 100 MB).
  5. Ngoài ra, đặt tăng trưởng tệp dữ liệu đến một số lượng lớn hơn.
  6. Hãy chắc chắn rằng bạn có đủ dung lượng đĩa để phát triển thêm các tệp nhật ký và dữ liệu.
  7. Khi cập nhật kết thúc, hãy tạo lại / bật các chỉ mục mà bạn đã bỏ / tắt trong bước 2.
  8. Nếu bạn không cần chúng nữa, hãy bỏ chỉ mục được tạo ở bước 3.

Chỉnh sửa: Vì tôi không thể nhận xét về bài đăng gốc của bạn, tôi sẽ trả lời câu hỏi của bạn từ Chỉnh sửa 4. Bạn có 7 chỉ mục trên AX Index là cây B và mỗi bản cập nhật cho trường đó làm cho cây B cân bằng lại. Nó nhanh hơn để xây dựng lại các chỉ mục đó từ đầu hơn là để cân bằng lại mỗi lần.


Đối với điểm 1, hãy xem câu trả lời của tôi cho ik_zelf. Con trỏ ở đó vì lý do điều tra và không có nhiều tác động. Tôi sẽ thực hiện phần còn lại của các đề xuất của bạn, tôi nghĩ đó là tất cả những gì tôi còn lại để làm. Nếu nó hoạt động, tôi vẫn sẽ bị bỏ lại mà không có lời giải thích về những gì xảy ra bây giờ mặc dù ...
GFK

Bạn có thể đăng DDL cho các bảng của mình (bao gồm tất cả các chỉ mục, các ràng buộc, v.v.). Có thể có một cái gì đó làm chậm hiệu suất của bạn và bạn đang thiếu điều đó.
bojan

1
Bỏ chỉ mục / Cập nhật / Xây dựng lại chỉ mục hoạt động và mặc dù tôi không muốn phải làm điều gì đó quyết liệt, tôi không thấy rằng mình có lựa chọn. Cảm ơn!
GFK

0

Một điều cần xem xét là tài nguyên hệ thống (Bộ nhớ, Đĩa, CPU) trong quá trình này. Tôi đã cố gắng chèn 7 triệu hàng riêng lẻ vào một bảng trong một công việc lớn và máy chủ của tôi được treo theo cách tương tự như của bạn.

Hóa ra tôi không có đủ bộ nhớ trên máy chủ để chạy công việc chèn hàng loạt này. Trong các tình huống như thế này, SQL thích giữ bộ nhớ và không để nó đi .... ngay cả sau khi đã nói lệnh chèn có thể hoặc chưa hoàn thành. Càng nhiều lệnh được xử lý trong các công việc lớn, bộ nhớ càng bị ăn hết. Một khởi động lại nhanh chóng giải phóng bộ nhớ cho biết.

Những gì tôi sẽ làm là bắt đầu quá trình này từ đầu với Trình quản lý tác vụ của bạn đang chạy. Nếu việc sử dụng bộ nhớ đạt hơn 75% thì khả năng hệ thống / quy trình của bạn sẽ đóng băng các bầu trời.

Nếu bộ nhớ / tài nguyên của bạn thực sự bị giới hạn như đã lưu ý ở trên thì các tùy chọn của bạn là cắt quá trình thành các phần nhỏ hơn (với khả năng khởi động lại thường xuyên nếu mức sử dụng bộ nhớ cao) thay vì một công việc lớn hoặc nâng cấp lên máy chủ 64 bit có nhiều bộ nhớ.


0

Kịch bản cập nhật luôn nhanh hơn so với sử dụng thủ tục.

Vì bạn đang cập nhật cột X của tất cả các hàng trong bảng A, trước tiên hãy đảm bảo bạn bỏ chỉ mục trên đó. Cũng đảm bảo rằng không có những thứ như kích hoạt và ràng buộc hoạt động trên cột đó.

Cập nhật chỉ mục là một công việc tốn kém, vì nó xác nhận các ràng buộc và thực thi các kích hoạt cấp hàng để thực hiện tra cứu dữ liệu khác.


Tôi không nghĩ đó là vấn đề. Tôi nhận ra việc cập nhật các bản ghi được lập chỉ mục cần có thời gian và tôi biết rằng, về tổng thể, một phần thời gian cần có là do điều này. Nhưng tôi mong đợi điều này, và tôi ổn với nó: như tôi đã nói, việc cập nhật 99% các dòng mất 5 phút (thậm chí sử dụng con trỏ), nhưng vì một số lý do, một dòng (và không phải lúc nào cũng giống nhau) mất 5h. Điều làm tôi lo lắng là hành vi đặc biệt này.
GFK

khóa không phải là vấn đề bạn đã nói .... làm thế nào về việc sử dụng hệ thống tập tin, đạt 90% hoặc cao hơn?
ik_zelf

không, nó là 31GB miễn phí trong số 120GB, vì vậy tôi nghĩ rằng nó ổn
GFK

điều gì xảy ra nếu bạn cố gắng sao chép bảng như tạo bảng a_copy khi chọn * từ a;
ik_zelf
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.