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 WHILE
vò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?
DBCC PAGE
để xem những gì đang được viết.