Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Giảm thiểu tác động của việc mở rộng cột IDENTITY - phần 3

[Phần 1 | Phần 2 | Phần 3 | Phần 4]

Cho đến nay trong loạt bài này, tôi đã chứng minh tác động vật lý trực tiếp đến trang khi tăng kích thước từ int thành bigint , và sau đó lặp lại qua một số trình chặn phổ biến cho hoạt động này. Trong bài đăng này, tôi muốn kiểm tra hai cách giải quyết tiềm năng:một đơn giản và một cực kỳ phức tạp.

Con đường dễ dàng

Tôi đã bị cướp mất lời sấm của mình một chút trong một nhận xét về bài đăng trước của tôi - Keith Monroe gợi ý rằng bạn có thể đặt lại bảng xuống âm thấp hơn giới hạn của kiểu dữ liệu số nguyên, tăng gấp đôi dung lượng của bạn cho các giá trị mới. Bạn có thể thực hiện việc này với DBCC CHECKIDENT :

 DBCC CHECKIDENT (N'dbo.TableName ', RESEED, -2147483648); 

Điều này có thể hoạt động, giả sử các giá trị thay thế không có ý nghĩa đối với người dùng cuối (hoặc nếu có, người dùng sẽ không phải lo lắng khi đột nhiên nhận được số âm). Tôi cho rằng bạn có thể đánh lừa họ bằng cách xem:

 TẠO CHẾ ĐỘ XEM dbo.ViewNameAS CHỌN ID =CHUYỂN ĐỔI (bigint, TRƯỜNG HỢP KHI ID <0 THEN (2147483648 * 2) - 1 + CHUYỂN ĐỔI (bigint, ID) ELSE ID END) TỪ dbo.TableName; 

Điều này có nghĩa là người dùng đã thêm ID = -2147483648 thực sự sẽ thấy +2147483648 , người dùng đã thêm ID = -2147483647 sẽ thấy +2147483649 , và như thế. Tuy nhiên, bạn sẽ phải điều chỉnh mã khác để đảm bảo thực hiện phép tính ngược lại khi người dùng chuyển ID đó , ví dụ:

 THỦ TỤC THAY THẾ dbo.GetRowByID @ID bigintASBEGIN ĐẶT SỐ TÀI KHOẢN BẬT; DECLARE @RealID bigint; SET @RealID =CASE WHEN @ID> 2147483647 THEN @ID - (2147483648 * 2) + 1 ELSE @ID END; CHỌN ID, @ID / *, các cột khác * / FROM dbo.TableName WHERE ID =@RealID; ENDGO 

Tôi không phát điên vì sự khó hiểu này. Ở tất cả. Nó lộn xộn, gây hiểu lầm và dễ xảy ra lỗi. Và nó khuyến khích hiển thị các khóa thay thế - nói chung, IDENTITY giá trị không được hiển thị cho người dùng cuối, vì vậy họ thực sự không nên quan tâm xem họ có phải là khách hàng 24, 642, -376 hay các số lớn hơn nhiều ở một trong hai bên không.

"Giải pháp" này cũng giả định rằng bạn không có mã ở bất kỳ đâu đặt hàng theo IDENTITY để hiển thị các hàng được chèn gần đây nhất trước tiên hoặc suy ra rằng IDENTITY cao nhất giá trị phải là hàng mới nhất. Mã hiện dựa vào thứ tự sắp xếp của IDENTITY cột, rõ ràng hoặc ẩn ý (có thể nhiều hơn bạn nghĩ nếu đó là chỉ mục được phân nhóm), sẽ không hiển thị các hàng theo thứ tự mong đợi nữa - nó sẽ hiển thị tất cả các hàng được tạo sau RESEED , bắt đầu với dòng đầu tiên, và sau đó nó sẽ hiển thị tất cả các hàng được tạo trước RESEED , bắt đầu với cái đầu tiên.

Lợi ích chính của phương pháp này là nó không yêu cầu bạn thay đổi kiểu dữ liệu và kết quả là RESEED thay đổi không yêu cầu bất kỳ thay đổi nào đối với chỉ mục, ràng buộc hoặc khóa ngoại gửi đến.

Nhược điểm - tất nhiên, ngoài những thay đổi về mã đã đề cập ở trên - là điều này chỉ khiến bạn mất thời gian trong ngắn hạn. Cuối cùng, bạn cũng sẽ sử dụng hết tất cả các số nguyên âm có sẵn. Và đừng nghĩ rằng điều này sẽ làm tăng gấp đôi tuổi thọ hữu ích của phiên bản hiện tại của bảng về mặt thời gian - trong nhiều trường hợp, tốc độ tăng dữ liệu đang tăng nhanh, không duy trì ổn định, vì vậy bạn sẽ sử dụng hết 2 tỷ hàng tiếp theo nhanh hơn rất nhiều so với 2 tỷ hàng đầu tiên.

Một cách khó hơn

Một cách tiếp cận khác mà bạn có thể thực hiện là ngừng sử dụng IDENTITY cột hoàn toàn; thay vào đó, bạn có thể chuyển đổi sang sử dụng SEQUENCE . Bạn có thể tạo một bigint mới , đặt giá trị mặc định thành giá trị tiếp theo từ SEQUENCE , cập nhật tất cả các giá trị đó với các giá trị từ cột ban đầu (theo lô nếu cần), bỏ cột ban đầu và đổi tên cột mới. Hãy tạo bảng hư cấu này và chèn một hàng duy nhất:

 TẠO BẢNG dbo.SequenceDemo (ID int IDENTITY (1,1), x char (1), CONSTRAINT PK_SD_Identity PRIMARY KEY CLUSTERED (ID)); ĐI CHÈN dbo.SequenceDemo (x) VALUES ('x'); 

Tiếp theo, chúng tôi sẽ tạo một SEQUENCE bắt đầu ngay bên ngoài giới hạn trên của một int:

 TẠO SEQUENCE dbo.BeyondIntAS bigintSTART VỚI 2147483648 TĂNG BẰNG 1; 

Tiếp theo, những thay đổi trong bảng cần thiết để chuyển sang sử dụng SEQUENCE cho cột mới:

 BẮT ĐẦU GIAO DỊCH; - thêm cột "danh tính" mới:ALTER TABLE dbo.SequenceDemo THÊM ID2 bigint; ĐI - đặt cột mới bằng các giá trị nhận dạng hiện có - đối với các bảng lớn, có thể cần thực hiện việc này theo lô:CẬP NHẬT dbo.SequenceDemo SET ID2 =ID; - bây giờ làm cho nó không thể null và thêm mặc định từ SEQUENCE của chúng tôi:ALTER TABLE dbo.SequenceDemo ALTER COLUMN ID2 bigint NOT NULL; ALTER TABLE dbo.SequenceDemo ADD CONSTRAINT DF_SD_Identity DEFAULT NEXT VALUE FOR dbo.BeyondInt FOR ID2; - cần bỏ PK hiện có (và bất kỳ chỉ mục nào):ALTER TABLE dbo.SequenceDemo DROP CONSTRAINT PK_SD_Identity; - thả cột cũ và đổi tên cột mới:ALTER TABLE dbo.SequenceDemo DROP COLUMN ID; EXEC sys.sp_rename N'dbo.SequenceDemo.ID2 ', N'ID', 'COLUMN'; - bây giờ đặt PK trở lại:ALTER TABLE dbo.SequenceDemo ADD CONSTRAINT PK_SD_Identity PRIMARY KEY CLUSTERED (ID); CAM KẾT GIAO DỊCH; 

Trong trường hợp này, lần chèn tiếp theo sẽ mang lại kết quả sau (lưu ý rằng SCOPE_IDENTITY() không còn trả về giá trị hợp lệ):

 CHÈN dbo.SequenceDemo (x) VALUES ('y'); CHỌN Si =SCOPE_IDENTITY (); CHỌN ID, x TỪ dbo.SequenceDemo; / * kết quả Si ---- NULL ID x ---------- -1 x2147483648 y * / 

Nếu bảng lớn và bạn cần cập nhật cột mới theo lô thay vì giao dịch một lần ở trên, như tôi đã mô tả ở đây - cho phép người dùng tương tác với bảng trong thời gian chờ đợi - bạn sẽ cần phải có trình kích hoạt tại chỗ để ghi đè SEQUENCE giá trị cho bất kỳ hàng mới nào được chèn, để chúng tiếp tục khớp với những gì được xuất cho bất kỳ mã gọi nào. (Điều này cũng giả định rằng bạn vẫn còn một số chỗ trong phạm vi số nguyên để tiếp tục chấp nhận một số bản cập nhật; nếu không, nếu bạn đã sử dụng hết phạm vi, bạn sẽ phải mất một thời gian ngừng hoạt động - hoặc sử dụng giải pháp dễ dàng ở trên trong ngắn hạn .)

Hãy bỏ mọi thứ và bắt đầu lại, sau đó chỉ cần thêm cột mới:

 DROP TABLE dbo.SequenceDemo; DROP SEQUENCE dbo.BeyondInt; GO CREATE TABLE dbo.SequenceDemo (ID int IDENTITY (1,1), x char (1), CONSTRAINT PK_SD_Identity PRIMARY KEY CLUSTERED (ID)); ĐI CHÈN dbo .SequenceDemo (x) VALUES ('x'); ĐI TẠO TƯƠNG TỰ dbo.BeyondIntAS bigintSTART VỚI 2147483648 TĂNG BẰNG 1; ĐI BẢNG ALTER dbo.SequenceDemo THÊM ID2 bigint; ĐI 

Và đây là trình kích hoạt mà chúng tôi sẽ thêm vào:

 TẠO TRIGGER dbo.After_SequenceDemoON dbo.SequenceDemoAFTER INSERTASBEGIN UPDATE sd SET sd.ID2 =sd.ID FROM dbo.SequenceDemo AS sd INNER JOIN được chèn AS i ON sd.ID =i.ID; END 

Lần này, lần chèn tiếp theo sẽ tiếp tục tạo các hàng trong phạm vi số nguyên thấp hơn cho cả hai cột, cho đến khi tất cả các giá trị tồn tại từ trước đã được cập nhật và phần còn lại của các thay đổi đã được cam kết:

 CHÈN dbo.SequenceDemo (x) VALUES ('y'); CHỌN Si =SCOPE_IDENTITY (); CHỌN ID, ID2, x TỪ dbo.SequenceDemo; / * kết quả Si ---- 2 ID ID2 x ---- ---- --1 NULL x2 2 y * / 

Bây giờ, chúng tôi có thể tiếp tục cập nhật ID2 hiện có trong khi các hàng mới tiếp tục được chèn trong phạm vi thấp hơn:

 ĐẶT SỐ TÀI KHOẢN BẬT; KHAI BÁO @r INT =1; WHILE @r> 0 BẮT ĐẦU GIAO DỊCH; CẬP NHẬT HÀNG ĐẦU (10000) dbo.SequenceDemo SET ID2 =ID TRONG ĐÓ ID2 KHÔNG ĐỦ; ĐẶT @r =@@ ROWCOUNT; CAM KẾT GIAO DỊCH; - KIỂM TRA; - nếu đơn giản - NHẬT KÝ DỰ PHÒNG ... - nếu đầyEND 

Khi chúng tôi đã cập nhật tất cả các hàng hiện có, chúng tôi có thể tiếp tục với phần còn lại của các thay đổi, rồi thả trình kích hoạt:

 BEGIN TRANSACTION; ALTER TABLE dbo.SequenceDemo ALTER COLUMN ID2 BIGINT NOT NULL; ALTER TABLE dbo.SequenceDemo ADD CONSTRAINT DF_SD_Identity DEFAULT NEXT VALUE CHO dbo.BeyondInt FOR ID2; ALTER TABLE dbo.SequenceDemo DEMo DROP COLUMN ID; EXEC sys.sp_rename N'dbo.SequenceDemo.ID2 ', N'ID', 'COLUMN'; ALTER TABLE dbo.SequenceDemo ADD CONSTRAINT PK_SD_Identity PRIMARY KEY CLUSTERED (ID); DROP TRIGGER dbo.SequenceDemo ADD CONSTRAINT PK_SD_Identity PRIMARY KEY CLUSTERED (ID); DROP TRIGGER dbo.InsteadOf trước> 

Bây giờ, phần chèn tiếp theo sẽ tạo ra các giá trị sau:

 CHÈN dbo.SequenceDemo (x) VALUES ('z'); CHỌN Si =SCOPE_IDENTITY (); CHỌN ID, x TỪ dbo.SequenceDemo; / * kết quả Si ---- NULL ID x ---------- -1 x2 y2147483648 z * / 

Nếu bạn có mã dựa vào SCOPE_IDENTITY() , @@IDENTITY hoặc IDENT_CURRENT() , nó cũng sẽ phải thay đổi, vì những giá trị đó không còn được điền sau khi chèn - mặc dù OUTPUT mệnh đề sẽ tiếp tục hoạt động chính xác trong hầu hết các tình huống. Nếu bạn cần mã của mình để tiếp tục tin rằng bảng tạo IDENTITY giá trị, thì bạn có thể sử dụng trình kích hoạt để giả mạo điều này - tuy nhiên nó sẽ chỉ có thể điền @@IDENTITY trên chèn, không phải SCOPE_IDENTITY() . Điều này vẫn có thể yêu cầu thay đổi, vì trong hầu hết các trường hợp, bạn không muốn dựa vào @@IDENTITY cho bất kỳ điều gì (vì vậy, nếu bạn định thực hiện thay đổi, hãy xóa tất cả các giả định về IDENTITY ở tất cả).

 TẠO TRIGGER dbo.FakeIdentityON dbo.SequenceDemoINSTEAD OF INSERTASBEGIN SET NOCOUNT ON; DECLARE @lowestID bigint =(Đã chèn CHỌN MIN (id) TỪ); DECLARE @sql nvarchar (max) =N'DECLARE @foo TABLE (ID bigint IDENTITY ('+ CONVERT (varchar (32), @lowestID) + N', 1)); '; CHỌN @sql + =N'INSERT @foo GIÁ TRỊ ĐỊNH NGHĨA; ' TỪ được chèn vào; EXEC sys.sp_executesql @sql; INSERT dbo.SequenceDemo (ID, x) SELECT ID, x FROM được chèn; END 

Bây giờ, phần chèn tiếp theo sẽ tạo ra các giá trị sau:

 CHÈN dbo.SequenceDemo (x) VALUES ('a'); SELECT Si =SCOPE_IDENTITY (), Ident =@@ IDENTITY; CHỌN ID, x FROM dbo.SequenceDemo; / * kết quả Si Ident ---- ----- NULL 2147483649 ID x ---------- -1 x2 y2147483648 z2147483649 a * / 

Với cách giải quyết này, bạn vẫn cần phải xử lý các ràng buộc, chỉ mục và bảng khác có khóa ngoại gửi đến. Các ràng buộc và chỉ mục cục bộ khá đơn giản, nhưng tôi sẽ giải quyết tình huống phức tạp hơn với khóa ngoại trong phần tiếp theo của loạt bài này.

Một cái sẽ không hiệu quả, nhưng tôi ước nó sẽ làm được

ALTER TABLE SWITCH có thể là một cách rất hiệu quả để thực hiện một số thay đổi siêu dữ liệu khó thực hiện được. Và trái với niềm tin phổ biến, điều này không chỉ liên quan đến phân vùng và không bị hạn chế đối với Phiên bản Doanh nghiệp. Mã sau sẽ hoạt động trên Express và là phương pháp mọi người đã sử dụng để thêm hoặc xóa IDENTITY thuộc tính trên một bảng (một lần nữa, không tính đến khóa ngoại và tất cả những phần mềm chặn khó chịu khác).

 TẠO BẢNG dbo.WithIdentity (ID int IDENTITY (1,1) NOT NULL); TẠO BẢNG dbo.WithoutIdentity (ID int NOT NULL); BẢNG THAY THẾ dbo.WithIdentity CHUYỂN ĐỔI ĐẾN dbo.WithoutIdentity; ĐI BẢNG DROP dbo.WithIdentity; EXEC sys.sp_rename N'dbo.WithoutIdentity ', N'dbo.WithIdentity', 'OBJECT'; 

Điều này hoạt động vì các loại dữ liệu và khả năng vô hiệu khớp chính xác và không có sự chú ý nào được chú ý đến IDENTITY thuộc tính. Tuy nhiên, hãy thử kết hợp các loại dữ liệu và mọi thứ không hoạt động tốt:

 TẠO BẢNG dbo.SourceTable (ID int IDENTITY (1,1) NOT NULL); TẠO BẢNG dbo.TrySwitch (ID bigint IDENTITY (1,1) NOT NULL); ALTER TABLE dbo.SourceTable CHUYỂN ĐỔI ĐẾN dbo.TrySwitch; 

Điều này dẫn đến:

Msg 4944, Mức 16, Trạng thái 1
Câu lệnh ALTER TABLE SWITCH không thành công vì cột 'ID' có kiểu dữ liệu int trong bảng nguồn 'dbo.SourceTable' khác với kiểu bigint trong bảng đích 'dbo.TrySwitch'.

Sẽ thật tuyệt vời nếu một SWITCH hoạt động có thể được sử dụng trong một tình huống như thế này, trong đó sự khác biệt duy nhất trong lược đồ không thực sự * yêu cầu * bất kỳ thay đổi vật lý nào để phù hợp (một lần nữa, như tôi đã trình bày trong phần 1, dữ liệu được ghi lại trên các trang mới, mặc dù không cần thiết phải làm như vậy).

Kết luận

Bài đăng này đã nghiên cứu hai cách giải quyết tiềm năng để bạn có thêm thời gian trước khi thay đổi IDENTITY hiện có của bạn hoặc bỏ qua IDENTITY hoàn toàn có lợi cho SEQUENCE . Nếu cả hai cách giải quyết này đều không được bạn chấp nhận, vui lòng xem phần 4, nơi chúng tôi sẽ giải quyết vấn đề này trực tiếp.

-

[Phần 1 | Phần 2 | Phần 3 | Phần 4]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tại sao Học Cassandra với Hadoop?

  2. Làm thế nào để phân tích cú pháp chuỗi giống như một chuyên gia bằng cách sử dụng hàm SUBSTRING () của SQL?

  3. Chia tỷ lệ cơ sở dữ liệu chuỗi thời gian của bạn - Cách đơn giản mở rộng quy mô thời gianDB

  4. Hiểu các loại và định dạng của MapReduce

  5. Phương pháp tiếp cận thời tiền sử và hiện đại để thiết kế cơ sở dữ liệu