Năm ngoái, Andy Mallon đã viết blog về việc tăng kích thước một cột từ int
thành bigint
không có thời gian chết. (Tại sao đây không phải là hoạt động chỉ siêu dữ liệu trong các phiên bản SQL Server hiện đại, tôi không thích, nhưng đó là một bài viết khác.)
Thông thường khi chúng tôi giải quyết vấn đề này, chúng là các bảng rộng và lớn (cả về số lượng hàng và kích thước tuyệt đối) và cột chúng tôi cần thay đổi là cột duy nhất / hàng đầu trong khóa phân cụm. Thường có các phức tạp khác liên quan - ràng buộc khóa ngoại gửi đến, nhiều chỉ mục không được phân cụm và cơ sở dữ liệu bận cực kỳ nhạy cảm với hoạt động nhật ký (vì nó liên quan đến Theo dõi thay đổi, sao chép, Nhóm khả dụng hoặc cả ba ).
Vì lý do này, chúng tôi cần thực hiện một cách tiếp cận như Andy đã nêu, trong đó chúng tôi xây dựng bảng bóng với lược đồ mới, tạo trình kích hoạt để đồng bộ hóa cả hai bản sao và sau đó chèn hàng loạt / chèn lấp theo tốc độ riêng của nhóm đó cho đến khi chúng sẵn sàng hoán đổi trong bản sao như một giao dịch thực sự.
Nhưng tôi lười!
Có một số trường hợp bạn có thể thay đổi cột trực tiếp, nếu bạn có đủ khả năng cho một khoảng thời gian ngừng hoạt động / chặn nhỏ và nó trở thành một hoạt động đơn giản hơn nhiều. Tuần trước, một trường hợp như vậy đã xuất hiện, với một bảng hơn 1TB, nhưng chỉ có 100 nghìn hàng. Hầu như tất cả dữ liệu đều nằm ngoài hàng (LOB), họ có thể dành một khoảng thời gian ngừng hoạt động nhỏ nếu cần, và họ đang có kế hoạch tắt Theo dõi thay đổi và định cấu hình lại nó. Tự tin rằng việc tạo lại PK nhóm sẽ không phải động đến dữ liệu LOB (nhiều), tôi đề xuất rằng đây có thể là trường hợp chúng ta có thể áp dụng thay đổi trực tiếp.
Trong một tình huống cô lập (không có khóa ngoại gửi vào, không có chỉ mục bổ sung, không có hoạt động nào phụ thuộc vào trình đọc nhật ký và không lo ngại về tính đồng thời), tôi đã tổng hợp một số thử nghiệm để xem, trong chân không, thay đổi này sẽ yêu cầu gì về thời lượng. và tác động đến nhật ký giao dịch. Câu hỏi chính mà tôi không biết trả lời trước là "Chi phí gia tăng của việc cập nhật bảng tại chỗ khi có một lượng lớn dữ liệu không phải khóa là bao nhiêu?"
Tôi sẽ cố gắng đóng gói nhiều thứ vào một bài đăng ở đây. Tôi đã thực hiện rất nhiều thử nghiệm và tất cả đều có liên quan, ngay cả khi không phải tất cả các tình huống thử nghiệm đều áp dụng cho bạn. Hãy chịu đựng tôi.
Các bảng
Tôi đã tạo 6 bảng, bao gồm một đường cơ sở chỉ có cột khóa, một bảng với 4K được lưu trữ trong hàng và sau đó bốn bảng, mỗi bảng có một cột varchar (tối đa) được điền với lượng dữ liệu chuỗi khác nhau (4K, 16K, 64K và 256K).
TẠO BẢNG dbo.withJustId (id int NOT NULL, CONSTRAINT pk_withJustId PRIMARY KEY CLUSTERED (id)); TẠO BẢNG dbo.withoutLob (id int NOT NULL, extradata varchar (4000) NOT NULL DEFAULT (REPLICATE ('x', 4000)), CONSTRAINT pk_withoutLob PRIMARY KEY CLUSTERED (id)); TẠO BẢNG dbo.withLob004 (id int NOT NULL, extradata varchar (max) NOT NULL DEFAULT (REPLICATE ('x', 4000)), CONSTRAINT pk_withLob004 PRIMARY KEY CLUSTERED (id)); TẠO BẢNG dbo.withLob016 (id int NOT NULL, extradata varchar (max) NOT NULL DEFAULT (REPLICATE (CONVERT (varchar (max), 'x'), 16000)), CONSTRAINT pk_withLob016 PRIMARY KEY CLUSTERED (id)); TẠO BẢNG dbo.withLob064 (id int NOT NULL, extradata varchar (max) NOT NULL DEFAULT (REPLICATE (CONVERT (varchar (max), 'x'), 64000)), CONSTRAINT pk_withLob064 PRIMARY KEY CLUSTERED (id)); TẠO BẢNG dbo.withLob256 (id int NOT NULL, extradata varchar (max) NOT NULL DEFAULT (REPLICATE (CONVERT (varchar (max), 'x'), 256000)), CONSTRAINT pk_withLob256 PRIMARY KEY CLUSTERED (id));Tôi đã điền vào mỗi hàng 100.000 hàng:
CHÈN dbo.withJustId (id) CHỌN ĐẦU (100000) id =ROW_NUMBER () HẾT (ĐẶT HÀNG THEO c1.name) TỪ sys.all_columns AS c1 CROSS JOIN sys.all_objects; INSERT dbo.withoutLob (id) CHỌN id TỪ dbo.withJustId; CHÈN dbo.withLob004 (id) CHỌN id TỪ dbo.withJustId; CHÈN dbo.withLob016 (id) CHỌN id TỪ dbo.withJustId; CHÈN dbo.withLob064 (id) CHỌN dbo.withLob064 id FROM dbo.withJustId; CHÈN dbo.withLob256 (id) CHỌN id TỪ dbo.withJustId;Tôi thừa nhận những điều trên là không thực tế; chúng ta có một bảng chỉ là số nhận dạng + dữ liệu LOB thường xuyên như thế nào? Tôi đã chạy lại các bài kiểm tra với bốn cột bổ sung này để cung cấp cho các trang dữ liệu không phải LOB giống với thế giới thực hơn một chút:
fill1 char (320) NOT NULL DEFAULT ('x'), count1 int NOT NULL DEFAULT (0), count2 int NOT NULL DEFAULT (0), dt datetime2 NOT NULL DEFAULT sysutcdatetime (),Các bảng này chỉ lớn hơn một chút về kích thước tổng thể, nhưng sự gia tăng tỷ lệ thuận về lượng dữ liệu không phải LOB (không được minh họa trong biểu đồ này) là sự khác biệt lớn nhưng ẩn:
Kích thước dành riêng của bảng, tính bằng GB
Các bài kiểm tra
Sau đó, tôi hẹn giờ và thu thập dữ liệu nhật ký cho từng thao tác này (có và không có
ONLINE = ON
) so với từng biến thể của bảng:ALTER TABLE dbo.DROP CONSTRAINT pk_ ; ALTER TABLE dbo. ALTER COLUMN id bigint NOT NULL; - VỚI (ONLINE =BẬT); ALTER TABLE dbo. ADD CONSTRAINT pk_ PRIMARY KEY CLUSTERED (id); Trong thực tế, tôi đã sử dụng SQL động để tạo tất cả các bài kiểm tra này, do đó tôi không phải loay hoay với các tập lệnh trước mỗi bài kiểm tra theo cách thủ công.
Trong một bài đăng khác, tôi sẽ chia sẻ SQL động mà tôi đã sử dụng để tạo các bài kiểm tra đó và thu thập thời gian ở mỗi bước.Để so sánh, tôi cũng đã thử nghiệm phương pháp của Andy (mặc dù không theo nhóm và chỉ trên phiên bản nhỏ của bảng):
CREATE TABLE dbo._copy (id bigint NOT NULL - <, cột extradata khi có liên quan> CONSTRAINT pk_copy_ PRIMARY KEY CLUSTERED (id)); INSERT dbo. _copy SELECT * FROM dbo. ; EXEC sys.sp_rename N'dbo. ', N'dbo. _old', N'OBJECT '; EXEC sys.sp_rename N'dbo. _copy', N'dbo. ' , N'OBJECT '; Tôi đã bỏ qua các bảng rộng hơn ở đây; Tôi không muốn giới thiệu sự phức tạp của mã hóa và đo lường các hoạt động hàng loạt. Điểm khó khăn rõ ràng ở đây là, không giống như thay đổi cột tại chỗ, với phương thức bóng, bạn phải sao chép từng byte đơn của dữ liệu LOB đó. Việc phân lô có thể giảm thiểu tác động lớn của việc cố gắng thực hiện điều đó trong một giao dịch duy nhất, nhưng tất cả việc xáo trộn cuối cùng sẽ phải được thực hiện lại. Việc phân phối tại nguồn không thể kiểm soát hoàn toàn mức độ ảnh hưởng của nó đến điểm đến.
Kết quả
Kết quả đầu tiên tôi sẽ hiển thị chỉ là thời lượng trung bình cho các thay đổi tại chỗ, cho tất cả 12 cấu hình bảng và có và không có
ONLINE = ON
:Thời lượng, tính bằng giây, của việc thay đổi cột tại chỗ
Thực hiện thao tác này dưới dạng thao tác trực tuyến mất nhiều thời gian hơn (trong trường hợp xấu nhất là 200 giây), nhưng không chặn người dùng. Nó dường như tăng cùng với kích thước, nhưng không hoàn toàn tuyến tính. Thực hiện thao tác ngoại tuyến này gây ra hiện tượng chặn, nhưng nhanh hơn nhiều và không thay đổi nhiều khi bảng lớn hơn (ngay cả ở kích thước lớn nhất, điều này vẫn xảy ra sau khoảng một phút).
Việc so sánh các hoạt động tại chỗ này với hoạt động hoán đổi và thả là khó khăn khi sử dụng biểu đồ đường vì sự khác biệt lớn về tỷ lệ. Thay vào đó, tôi sẽ hiển thị biểu đồ thanh ngang trong khoảng thời gian liên quan đến từng cấu hình bảng. Khi việc tạo lại nhanh hơn, tôi sẽ sơn nền của hàng đó màu xanh lá cây; khi nó chậm hơn (hoặc nằm giữa phương pháp ngoại tuyến và trực tuyến), tôi có thể không cần, nhưng tôi sẽ sơn nền của hàng đó màu đỏ.
Kích thước LOB | Phương pháp tiếp cận | Cấu hình bảng | Thời lượng (giây) | ||
---|---|---|---|
Chỉ cần Id | ALTER Ngoại tuyến | Bảng gầy (10 MB) | 8,8 |
Bảng rộng hơn (30 MB) | 6,3 | ||
ALTER Trực tuyến | Bảng gầy | 11.0 | |
Bảng rộng hơn | 13,6 | ||
Tạo lại | Bảng gầy | 3,4 | |
varchar 4K | Ngoại tuyến | Bảng gầy (390 MB) | 16,6 |
Bảng rộng hơn (780 MB) | 14.0 | ||
Trực tuyến | Bảng gầy | 30,4 | |
Bảng rộng hơn | 48,6 | ||
Tạo lại | Bảng gầy | 1.290.0 | |
tối đa 4k | Ngoại tuyến | Bảng gầy (390 MB) | 33,1 |
Bảng rộng hơn (780 MB) | 32,1 | ||
Trực tuyến | Bảng gầy | 81,9 | |
Bảng rộng hơn | 103,3 | ||
Tạo lại | Bảng gầy | 28,9 | |
tối đa 16k | Ngoại tuyến | Bảng gầy (1,6 GB) | 53,3 |
Bảng rộng hơn (1,7 GB) | 46,7 | ||
Trực tuyến | Bảng gầy | 130,9 | |
Bảng rộng hơn | 150,2 | ||
Tạo lại | Bảng gầy | 81,8 | |
tối đa 64k | Ngoại tuyến | Bảng gầy (7,0 GB) | 51,5 |
Bảng rộng hơn (7,1 GB) | 58,5 | ||
Trực tuyến | Bảng gầy | 136,5 | |
Bảng rộng hơn | 152,6 | ||
Tạo lại | Bảng gầy | 226,5 | |
tối đa 256k | Ngoại tuyến | Bảng gầy (25,8 GB) | 60,9 |
Bảng rộng hơn (25,9 GB) | 61,3 | ||
Trực tuyến | Bảng gầy | 149,1 | |
Bảng rộng hơn | 197,1 | ||
Tạo lại | Bảng gầy | 1.576,7 |
Đây là một sự lung lay không công bằng đối với phương pháp của Andy, bởi vì - trong thế giới thực - bạn sẽ không thực hiện toàn bộ thao tác đó trong một lần chụp. Tôi không hiển thị cách sử dụng nhật ký giao dịch ở đây cho ngắn gọn, nhưng sẽ dễ dàng hơn để kiểm soát điều đó thông qua việc phân lô trong một hoạt động song song. Mặc dù cách tiếp cận của anh ấy đòi hỏi nhiều công việc hơn trước, nhưng nó an toàn hơn rất nhiều về thời gian ngừng hoạt động và / hoặc chặn. Nhưng bạn có thể thấy trong trường hợp bạn có nhiều dữ liệu ngoài hàng và có thể gặp sự cố ngắn, việc thay đổi cột trực tiếp sẽ ít đau hơn rất nhiều. "Quá lớn để thay đổi tại chỗ" là chủ quan và có thể tạo ra các kết quả khác nhau tùy thuộc vào ý nghĩa của "lớn". Trước khi thực hiện một cách tiếp cận, có thể hữu ích khi kiểm tra sự thay đổi so với một bản sao hợp lý, bởi vì hoạt động tại chỗ có thể đại diện cho một sự đánh đổi có thể chấp nhận được.
Kết luận
Tôi không viết điều này để tranh luận với Andy. Cách tiếp cận trong bài đăng gốc là đúng đắn, đáng tin cậy 100% và chúng tôi sử dụng nó mọi lúc. Tuy nhiên, khi bạo lực được đánh giá cao hơn độ chính xác của phẫu thuật và đặc biệt nếu bạn có thể dành một chút thời gian chết, có thể có giá trị trong cách tiếp cận đơn giản hơn đối với một số hình dạng bảng nhất định.