Thay đổi kiểu dữ liệu của các cột khóa chính của các bảng được phân đoạn


7

Tôi hiện đang may mắn với tình huống sau đây:

  • Chúng tôi có một cơ sở dữ liệu sản xuất được phân vùng và sử dụng các khóa chính loại int
  • Nhiều bảng (~ 20) mỗi bảng chứa> 100.000.000 hàng, 2 trong số này gần 2.000.000.000 hàng
  • Tất cả các bảng này đều có một cột loại intlà khóa chính tăng dần theo từng hàng
  • Các bảng này được phân vùng, sử dụng chức năng phân vùng int, sử dụng RIGHT(0), do đó, tất cả dữ liệu nằm trong phân vùng một và duy nhất

Vì một số bảng gần int32giá trị tối đa, chúng tôi phải thay đổi các loại cột này thành bigint. Đây là nơi các vấn đề bắt đầu:

  • Không thể sửa đổi loại cột khóa chính (điều này tôi có thể tránh bằng cách bỏ khóa chính, sửa đổi loại và tạo lại khóa chính), tuy nhiên, tôi vẫn không thể sửa đổi loại cột sau khi bỏ ràng buộc khóa chính, nhận được ngoại lệ sau:

    {"Đối tượng 'XXX_XXX' phụ thuộc vào cột 'ABCD'. ALTER TABLE ALTER COLUMN ABCD không thành công do một hoặc nhiều đối tượng truy cập vào cột này."}

Theo như tôi có thể thấy có một sự phụ thuộc ràng buộc lược đồ của lược đồ phân vùng (và hàm) vào bảng ngăn chặn sửa đổi kiểu dữ liệu. Không có khóa ngoại tham chiếu các khóa chính này.

Tôi không thấy một giải pháp để cập nhật loại cột trên cơ sở dữ liệu này, chứ chưa nói đến cơ sở dữ liệu sản xuất. Chúng tôi có thể đủ khả năng một số thời gian ngừng bảo trì, giả sử + - 60 phút, nhưng không còn nữa. Những lựa chọn của tôi là gì?

Thông tin thêm

  • Tôi bắt đầu tin rằng lựa chọn duy nhất của tôi là tạo các bảng mới với cấu trúc chính xác và bơm dữ liệu bảng hiện có vào các bảng mới ...

  • Các bảng đã được phân vùng dựa trên giá trị khóa chính của chúng (hiện chỉ có 1 phân vùng ( RIGHT(0), các phân vùng trong tương lai sẽ có thể cho các giá trị mới ( RIGHT(x)), ranh giới xvẫn chưa được xác định)

  • Đây là các bảng ghi dữ liệu, chỉ chèn, không cập nhật hoặc xóa

  • Luôn có DML được thực thi trên các bảng này ngoại trừ các cửa sổ bảo trì, cứ sau 6-8 tuần một lần trong khoảng 90 phút.

  • Các chỉ mục dựa trên khóa chính đều được nhóm

  • Không có chỉ mục không được nhóm trên các bảng này


Adimeus .... với bảng phân vùng và chức năng được liên kết, bạn sẽ cần 1) Phân tách chức năng khỏi bảng, 2) thả PK, 3) Nhập lại PK vào Kiểu dữ liệu bạn muốn, 4) Sửa đổi / Reattach Phần-Func vào bảng. Không có cách nào dễ dàng để thực hiện việc này, mà không bỏ Phần-Phần và Bảng và xây dựng lại cả hai từ đầu.
GoldBishop

Adimeus ... cũng vậy, việc sử dụng PK cho Part-Func là không khôn ngoan. Trong hầu hết các trường hợp kinh doanh, Part-Func được sử dụng trên Trường Ngày / Giờ để phân đoạn dữ liệu hàng năm. Trong các trường hợp hiếm hoi, tôi đã thấy chúng được sử dụng trên các trường smallint để phân vùng theo mã loại "trạng thái". Mặt khác, có vẻ như "tài sản" của bạn sẽ được phục vụ tốt hơn với Chỉ mục hoặc Chế độ xem lược đồ.
GoldBishop

Câu trả lời:


5

Đầu tiên trước khi tôi cung cấp giải pháp, tôi muốn xác nhận giả định của bạn:

Theo như tôi có thể thấy có một sự phụ thuộc ràng buộc lược đồ của lược đồ phân vùng (và hàm) vào bảng ngăn chặn sửa đổi kiểu dữ liệu.

Bạn nói đúng; thực tế là bạn có một hàm phân vùng được xác định cho kiểu dữ liệu INTđang ngăn bạn thay đổi kiểu dữ liệu của cột của bạn ... Đáng buồn thay, bạn bị mắc kẹt với bảng hiện tại vì theo cách hiểu của tôi, không thể điều chỉnh kiểu dữ liệu của hàm phân vùng mà không bỏ / tạo lại nó .... điều mà bạn không thể làm được vì bạn có các bảng phụ thuộc vào nó. Vì vậy, về cơ bản đây là kịch bản gà và trứng .

Tôi không thấy một giải pháp để cập nhật loại cột trên cơ sở dữ liệu này, chứ chưa nói đến cơ sở dữ liệu sản xuất. Chúng tôi có thể đủ khả năng một số thời gian ngừng bảo trì, giả sử + - 60 phút, nhưng không còn nữa. Những lựa chọn của tôi là gì?

Một giải pháp khả thi cho vấn đề của bạn là tận dụng Chế độ xem được phân vùng . Chức năng này đã tồn tại mãi mãi và thường bị bỏ qua, nhưng Chế độ xem được phân vùng sẽ cung cấp cho bạn một cách để vượt qua INTgiới hạn dữ liệu của bạn bằng cách thêm dữ liệu mới vào một bảng khác trong đó IDcột là BIGINTkiểu dữ liệu. Bảng mới này cũng cần có chức năng / lược đồ phân vùng tốt hơn bên dưới nó và hy vọng sẽ được duy trì tốt hơn một chút trong tương lai. Bất kỳ truy vấn nào đang trỏ đến bảng cũ sau đó sẽ được trỏ đến Chế độ xem được phân vùng và bạn sẽ không gặp phải vấn đề giới hạn dữ liệu.

Hãy để tôi giải thích với một ví dụ. Sau đây là một trò giải trí tầm thường của bảng được phân vùng hiện tại của bạn, bao gồm một phân vùng, như sau:

-- Create your very frustraiting parition scheme
CREATE PARTITION FUNCTION dear_god_why_this_logic (INT)
AS RANGE RIGHT FOR VALUES (0);

-- Scheme to store data on PartitionedData Filegroup
CREATE PARTITION SCHEME dear_god_why_this_scheme
AS PARTITION dear_god_why_this_logic ALL TO ([PRIMARY]);

-- Create Partitioned Table
CREATE TABLE dbo.TestPartitioned
(
    ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED,
    VAL CHAR(1)
) ON dear_god_why_this_scheme (ID);

--Populate Table with Data
INSERT INTO dbo.TestPartitioned WITH (TABLOCK) (VAL)
SELECT CHAR(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) % 128)
FROM master.sys.configurations t1
    CROSS JOIN master.sys.configurations t2
    CROSS JOIN master.sys.configurations t3;

Hãy nói vì lý do rằng tôi đang tiến gần đến INTgiới hạn (mặc dù rõ ràng là tôi không). Vì tôi không muốn hết các giá INTtrị hợp lệ trong IDcột, nên tôi sẽ tạo một bảng tương tự, nhưng sử dụng BIGINTcho IDcột thay thế. Bảng này sẽ được định nghĩa như sau:

CREATE PARTITION FUNCTION lets_maintain_this_going_forward (BIGINT)
AS RANGE RIGHT FOR VALUES (0, 500000, 1000000, 1500000, 2000000);

-- Scheme to store data on PartitionedData Filegroup
CREATE PARTITION SCHEME lets_maintain_this_going_forward_scheme
AS PARTITION lets_maintain_this_going_forward ALL TO ([PartitionedData]);

-- Table for New Data going forward with new ID datatype of BIGINT
CREATE TABLE dbo.TestPartitioned_BIGINT
(
    ID BIGINT IDENTITY(500000,1) PRIMARY KEY CLUSTERED,
    VAL CHAR(1)
) ON lets_maintain_this_going_forward_scheme (ID);

-- Add check constraint to be used by Partitioned View
ALTER TABLE dbo.TestPartitioned_BIGINT ADD CONSTRAINT CK_ID_BIGINT CHECK (ID > CAST(499999 AS BIGINT));

Một vài lưu ý ở đây:

Phân vùng bảng trên bảng mới

Bảng mới này cũng sẽ được phân vùng dựa trên yêu cầu của bạn. Nên nghĩ về nhu cầu bảo trì trong tương lai, vì vậy hãy tạo một số nhóm tệp mới, xác định chiến lược căn chỉnh phân vùng tốt hơn, v.v. Ví dụ của tôi là giữ cho nó đơn giản, vì vậy tôi sẽ ném tất cả các phân vùng của mình vào một Filegroup. Đừng làm điều đó trong sản xuất, thay vào đó hãy làm theo Thực tiễn tốt nhất về phân vùng bảng , với sự cho phép của Brent Ozar et. al.

Kiểm tra ràng buộc

Vì tôi muốn tận dụng Chế độ xem được phân vùng, tôi cần thêm một CHECK CONSTRAINTvào bảng mới này. Tôi biết rằng câu lệnh chèn của tôi đã tạo ra ~ 440k hồ sơ, vì vậy để an toàn, tôi sẽ bắt đầu IDENTITYhạt giống của mình ở mức 500k tạo ra một CONSTRAINTđịnh nghĩa này. Ràng buộc sẽ được sử dụng bởi trình tối ưu hóa khi đánh giá bảng nào có thể được loại bỏ khi Chế độ xem phân vùng cuối cùng được gọi.

Bây giờ để trộn hai bàn với nhau

Các khung nhìn được phân vùng không đặc biệt làm tốt khi bạn ném các kiểu dữ liệu hỗn hợp vào chúng khi nói đến cột phân vùng trong các bảng bên dưới. Để giải quyết vấn đề này, chúng tôi phải duy trì IDcột hiện tại trong bảng hiện tại của bạn làm BIGINTgiá trị. Chúng tôi sẽ làm điều đó bằng cách thêm Cột được tính toán liên tục , như sau:

-- Append Calculated Columns of New Datatype to Old Table
-- WARNING: This will take a while against a large data set and will likely lock the table
ALTER TABLE dbo.TestPartitioned ADD ID_BIG AS (CAST(ID AS BIGINT)) PERSISTED
GO

-- Add Constraints on Calculated Column
-- WARNING: This will likely lock the table
ALTER TABLE dbo.TestPartitioned ADD CONSTRAINT CK_ID_TestPartitioned_BIGINT CHECK(ID_BIG <= CAST(499999 AS BIGINT));
GO

-- Create a new Nonclustered index on ID_BIG to act as new "pkey"
CREATE NONCLUSTERED INDEX IX_TestPartitioned__BIG_ID__VAL ON dbo.TestPartitioned (ID_BIG) INCLUDE (VAL);

Tôi cũng đã thêm một CHECK CONSTRAINTbảng khác trên bảng cũ (để hỗ trợ Loại bỏ phân vùng khỏi Chế độ xem được phân vùng cuối cùng của chúng tôi) và Chỉ mục không phân cụm mới sẽ hoạt động như một chỉ mục khóa chính (bởi vì các tra cứu bắt nguồn từ Chế độ xem được phân vùng sẽ diễn ra được xảy ra chống lại ID_BIGthay vì ID).

Với Cột được tính toán mới và Kiểm tra ràng buộc tại chỗ, cuối cùng tôi cũng có thể xác định Chế độ xem được phân vùng, như sau:

-- Build a Partitioned View on Top of the old and new tables
CREATE VIEW dbo.vw_TableAll
WITH SCHEMABINDING
AS
SELECT ID_BIG AS ID, VAL FROM dbo.TestPartitioned
UNION ALL
SELECT ID, VAL FROM dbo.TestPartitioned_BIGINT
GO

Chạy một truy vấn nhanh đối với chế độ xem này sẽ xác nhận chúng tôi có hoạt động loại bỏ phân vùng (vì bạn không thấy bất kỳ tra cứu nào xảy ra đối với bảng mới mà chúng tôi đã tạo):

SELECT *
FROM dbo.vw_TableAll
WHERE ID < CAST(500 AS BIGINT);

Kế hoạch thực hiện:

nhập mô tả hình ảnh ở đây

Ở giai đoạn này, bạn sẽ cần thực hiện một vài thay đổi cho ứng dụng của mình:

  1. Dừng chèn các bản ghi mới vào bảng cũ, và thay vào đó chèn chúng vào bảng mới. Chúng tôi không được phép chèn các bản ghi vào Chế độ xem được phân vùng vì chúng tôi đang sử dụng IDENTITYcác giá trị trong các bảng bên dưới. Chế độ xem được phân vùng không cho phép điều này , vì vậy bạn phải chèn các bản ghi trực tiếp vào bảng mới trong kịch bản này.
  2. Điều chỉnh bất kỳ SELECTtruy vấn nào (trỏ đến bảng cũ) để trỏ đến Chế độ xem được phân vùng. Ngoài ra, bạn có thể đổi tên bảng cũ (ví dụ TableName_Old) và tạo dạng xem dưới dạng tên của các bảng cũ (ví dụ: Tên bảng); bạn muốn đến đây như thế nào là tùy thuộc vào bạn.

Bản ghi mới vào bảng mới

Tại thời điểm này, các bản ghi mới sẽ được chèn vào bảng mới. Tôi sẽ mô phỏng điều này bằng cách chạy như sau:

--Populate Table with Data
INSERT INTO dbo.TestPartitioned_BIGINT WITH (TABLOCK) (VAL)
SELECT CHAR(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) % 128)
FROM master.sys.configurations t1
    CROSS JOIN master.sys.configurations t2
    CROSS JOIN master.sys.configurations t3;
GO

Một lần nữa, Hạt giống danh tính của tôi được định cấu hình để tôi sẽ không có bất kỳ xung đột ID nào giữa hai bảng. Trên CHECK CONSTRAINTScả hai bảng cũng nên thực thi điều này. Hãy xác nhận rằng Loại bỏ phân vùng vẫn đang xảy ra:

SELECT *
FROM dbo.vw_TableAll
WHERE ID > CAST(300000 AS BIGINT)
    AND ID < CAST(300500 AS BIGINT);

SELECT *
FROM dbo.vw_TableAll
WHERE ID > CAST(500000 AS BIGINT)
    AND ID < CAST(500500 AS BIGINT);

Kế hoạch thực hiện:

nhập mô tả hình ảnh ở đây

Hãy lưu ý rằng hầu hết các truy vấn sẽ có khả năng kéo dài cả hai bảng. Mặc dù chúng tôi sẽ không thể tận dụng việc loại bỏ phân vùng trong các tình huống này, các kế hoạch truy vấn sẽ vẫn tối ưu nhất có thể với lợi ích mà bạn không phải viết lại các truy vấn cơ bản của mình (nếu bạn quyết định đặt tên cho chế độ xem giống nhau như bảng cũ của bạn).

Làm gì bây giờ?

Vâng, điều này hoàn toàn phụ thuộc vào bạn. Nếu các bản ghi hiện tại sẽ không bao giờ biến mất và bạn hài lòng với hiệu suất từ ​​Chế độ xem được phân vùng, hãy rót bia kỷ niệm vì bạn đã hoàn thành. Huzzah!

Nếu bạn muốn hợp nhất bảng cũ vào bảng mới, bạn sẽ phải chọn các cửa sổ bảo trì để thực hiện chuỗi thao tác tiếp theo. Về cơ bản, bạn sẽ bỏ các ràng buộc trên các bảng (sẽ phá vỡ thành phần loại bỏ phân vùng của Chế độ xem được phân vùng), sao chép các bản ghi gần đây nhất của bạn từ bảng cũ sang bảng mới, xóa các bản ghi này khỏi bảng cũ, và sau đó cập nhật các ràng buộc (để Chế độ xem được phân vùng có thể tận dụng loại bỏ phân vùng một lần nữa). Do khối lượng dữ liệu bạn có trong các bảng hiện có, bạn có thể phải trải qua một vài vòng của quy trình này để hợp nhất mọi thứ. Các bước được tóm tắt như sau:

-- During a maintenance window, transfer old records to new table if you so choose
-- Drop Check Constraint while transferring over records
ALTER TABLE dbo.TestPartitioned_BIGINT DROP CONSTRAINT CK_ID_BIGINT;

-- Retain Identity Values
SET IDENTITY_INSERT dbo.TestPartitioned_BIGINT ON

-- Copy records into the new table
INSERT INTO dbo.TestPartitioned_BIGINT (ID, VAL)
SELECT ID_BIG, VAL
FROM dbo.TestPartitioned
WHERE ID > 300000

SET IDENTITY_INSERT dbo.TestPartitioned_BIGINT OFF

-- Enable Check Constraint after transferred records are complete, ensuring the Check Constraint is defined on the proper ID value
ALTER TABLE dbo.TestPartitioned_BIGINT ADD CONSTRAINT CK_ID_BIGINT CHECK (ID > CAST(300000 AS BIGINT));
GO

-- Purge records from original table
DELETE
FROM dbo.TestPartitioned
WHERE ID > 300000

-- Recreate Check Constraint on original table, ensuring the Check Constraint is defined on the proper ID value
ALTER TABLE dbo.TestPartitioned DROP CONSTRAINT CK_ID_TestPartitioned_BIGINT;
GO
ALTER TABLE dbo.TestPartitioned ADD CONSTRAINT CK_ID_TestPartitioned_BIGINT CHECK(ID_BIG <= CAST(300000 AS BIGINT));
GO

Nếu có thể, hãy kiểm tra điều này trong một môi trường không sản xuất. Tôi không bỏ qua thử nghiệm trong sản xuất. Hy vọng câu trả lời này có ích, và nếu bạn có bất kỳ câu hỏi nào, vui lòng gửi bình luận và tôi sẽ cố gắng hết sức để nhanh chóng quay lại với bạn.

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.