Các khóa tự nhiên cung cấp hiệu suất cao hơn hoặc thấp hơn trong SQL Server so với các khóa nguyên thay thế?


25

Tôi là một fan hâm mộ của chìa khóa thay thế. Có một rủi ro phát hiện của tôi là sai lệch xác nhận.

Nhiều câu hỏi tôi đã thấy cả ở đây và tại http://stackoverflow.com sử dụng các khóa tự nhiên thay vì thay thế các khóa dựa trên IDENTITY()các giá trị.

Nền tảng của tôi trong các hệ thống máy tính cho tôi biết việc thực hiện bất kỳ thao tác so sánh nào trên một số nguyên sẽ nhanh hơn so với các chuỗi so sánh.

Nhận xét này khiến tôi đặt câu hỏi về niềm tin của mình, vì vậy tôi nghĩ rằng tôi sẽ tạo ra một hệ thống để điều tra luận điểm của mình rằng số nguyên nhanh hơn chuỗi để sử dụng làm khóa trong SQL Server.

Vì có thể có rất ít sự khác biệt rõ ràng trong các bộ dữ liệu nhỏ, tôi nghĩ ngay đến một thiết lập hai bảng trong đó bảng chính có 1.000.000 hàng và bảng phụ có 10 hàng cho mỗi hàng trong bảng chính cho tổng số 10.000.000 hàng bảng phụ. Tiền đề của thử nghiệm của tôi là tạo hai bộ bảng như thế này, một bộ sử dụng khóa tự nhiên và một bộ sử dụng khóa số nguyên và chạy thử nghiệm thời gian trên một truy vấn đơn giản như:

SELECT *
FROM Table1
    INNER JOIN Table2 ON Table1.Key = Table2.Key;

Sau đây là mã tôi đã tạo như một giường thử nghiệm:

USE Master;
IF (SELECT COUNT(database_id) FROM sys.databases d WHERE d.name = 'NaturalKeyTest') = 1
BEGIN
    ALTER DATABASE NaturalKeyTest SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE NaturalKeyTest;
END
GO
CREATE DATABASE NaturalKeyTest 
    ON (NAME = 'NaturalKeyTest', FILENAME = 
        'C:\SQLServer\Data\NaturalKeyTest.mdf', SIZE=8GB, FILEGROWTH=1GB) 
    LOG ON (NAME='NaturalKeyTestLog', FILENAME = 
        'C:\SQLServer\Logs\NaturalKeyTest.mdf', SIZE=256MB, FILEGROWTH=128MB);
GO
ALTER DATABASE NaturalKeyTest SET RECOVERY SIMPLE;
GO
USE NaturalKeyTest;
GO
CREATE VIEW GetRand
AS 
    SELECT RAND() AS RandomNumber;
GO
CREATE FUNCTION RandomString
(
    @StringLength INT
)
RETURNS NVARCHAR(max)
AS
BEGIN
    DECLARE @cnt INT = 0
    DECLARE @str NVARCHAR(MAX) = '';
    DECLARE @RandomNum FLOAT = 0;
    WHILE @cnt < @StringLength
    BEGIN
        SELECT @RandomNum = RandomNumber
        FROM GetRand;
        SET @str = @str + CAST(CHAR((@RandomNum * 64.) + 32) AS NVARCHAR(MAX)); 
        SET @cnt = @cnt + 1;
    END
    RETURN @str;
END;
GO
CREATE TABLE NaturalTable1
(
    NaturalTable1Key NVARCHAR(255) NOT NULL 
        CONSTRAINT PK_NaturalTable1 PRIMARY KEY CLUSTERED 
    , Table1TestData NVARCHAR(255) NOT NULL 
);
CREATE TABLE NaturalTable2
(
    NaturalTable2Key NVARCHAR(255) NOT NULL 
        CONSTRAINT PK_NaturalTable2 PRIMARY KEY CLUSTERED 
    , NaturalTable1Key NVARCHAR(255) NOT NULL 
        CONSTRAINT FK_NaturalTable2_NaturalTable1Key 
        FOREIGN KEY REFERENCES dbo.NaturalTable1 (NaturalTable1Key) 
        ON DELETE CASCADE ON UPDATE CASCADE
    , Table2TestData NVARCHAR(255) NOT NULL  
);
GO

/* insert 1,000,000 rows into NaturalTable1 */
INSERT INTO NaturalTable1 (NaturalTable1Key, Table1TestData) 
    VALUES (dbo.RandomString(25), dbo.RandomString(100));
GO 1000000 

/* insert 10,000,000 rows into NaturalTable2 */
INSERT INTO NaturalTable2 (NaturalTable2Key, NaturalTable1Key, Table2TestData)
SELECT dbo.RandomString(25), T1.NaturalTable1Key, dbo.RandomString(100)
FROM NaturalTable1 T1
GO 10 

CREATE TABLE IDTable1
(
    IDTable1Key INT NOT NULL CONSTRAINT PK_IDTable1 
    PRIMARY KEY CLUSTERED IDENTITY(1,1)
    , Table1TestData NVARCHAR(255) NOT NULL 
    CONSTRAINT DF_IDTable1_TestData DEFAULT dbo.RandomString(100)
);
CREATE TABLE IDTable2
(
    IDTable2Key INT NOT NULL CONSTRAINT PK_IDTable2 
        PRIMARY KEY CLUSTERED IDENTITY(1,1)
    , IDTable1Key INT NOT NULL 
        CONSTRAINT FK_IDTable2_IDTable1Key FOREIGN KEY 
        REFERENCES dbo.IDTable1 (IDTable1Key) 
        ON DELETE CASCADE ON UPDATE CASCADE
    , Table2TestData NVARCHAR(255) NOT NULL 
        CONSTRAINT DF_IDTable2_TestData DEFAULT dbo.RandomString(100)
);
GO
INSERT INTO IDTable1 DEFAULT VALUES;
GO 1000000
INSERT INTO IDTable2 (IDTable1Key)
SELECT T1.IDTable1Key
FROM IDTable1 T1
GO 10

Đoạn mã trên tạo ra một cơ sở dữ liệu và 4 bảng và điền vào các bảng với dữ liệu, sẵn sàng để kiểm tra. Mã kiểm tra tôi đã chạy là:

USE NaturalKeyTest;
GO
DECLARE @loops INT = 0;
DECLARE @MaxLoops INT = 10;
DECLARE @Results TABLE (
    FinishedAt DATETIME DEFAULT (GETDATE())
    , KeyType NVARCHAR(255)
    , ElapsedTime FLOAT
);
WHILE @loops < @MaxLoops
BEGIN
    DBCC FREEPROCCACHE;
    DBCC FREESESSIONCACHE;
    DBCC FREESYSTEMCACHE ('ALL');
    DBCC DROPCLEANBUFFERS;
    WAITFOR DELAY '00:00:05';
    DECLARE @start DATETIME = GETDATE();
    DECLARE @end DATETIME;
    DECLARE @count INT;
    SELECT @count = COUNT(*) 
    FROM dbo.NaturalTable1 T1
        INNER JOIN dbo.NaturalTable2 T2 ON T1.NaturalTable1Key = T2.NaturalTable1Key;
    SET @end = GETDATE();
    INSERT INTO @Results (KeyType, ElapsedTime)
    SELECT 'Natural PK' AS KeyType, CAST((@end - @start) AS FLOAT) AS ElapsedTime;

    DBCC FREEPROCCACHE;
    DBCC FREESESSIONCACHE;
    DBCC FREESYSTEMCACHE ('ALL');
    DBCC DROPCLEANBUFFERS;
    WAITFOR DELAY '00:00:05';
    SET @start = GETDATE();
    SELECT @count = COUNT(*) 
    FROM dbo.IDTable1 T1
        INNER JOIN dbo.IDTable2 T2 ON T1.IDTable1Key = T2.IDTable1Key;
    SET @end = GETDATE();
    INSERT INTO @Results (KeyType, ElapsedTime)
    SELECT 'IDENTITY() PK' AS KeyType, CAST((@end - @start) AS FLOAT) AS ElapsedTime;

    SET @loops = @loops + 1;
END
SELECT KeyType, FORMAT(CAST(AVG(ElapsedTime) AS DATETIME), 'HH:mm:ss.fff') AS AvgTime 
FROM @Results
GROUP BY KeyType;

Đây là kết quả:

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

Tôi đang làm gì đó sai ở đây, hay các phím INT nhanh hơn 3 lần so với các phím tự nhiên 25 ký tự?

Lưu ý, tôi đã viết một câu hỏi tiếp theo ở đây .


1
INT là 4 byte và NVARCHAR hiệu quả (25) dài hơn khoảng 14 lần (bao gồm cả dữ liệu hệ thống như độ dài), vì vậy xét riêng về chỉ số tôi tin rằng bạn sẽ có chỉ số PK rộng hơn và sâu hơn đáng kể và do đó tôi càng hiểu / O là cần thiết sẽ ảnh hưởng đến thời gian xử lý. Howevev một số nguyên tự nhiên (thậm chí có thể được kiểm tra đã được khai thác) sẽ khá giống với INT mà chúng ta nghĩ về việc sử dụng cho một cột Nhận dạng thay thế. Vì vậy, "khóa tự nhiên" có thể là INT, BIGINT, CHAR, NVARCHAR và tất cả các vấn đề.
RLF

7
Tôi nghĩ rằng hiệu suất đạt được @ MikeSherrill'Catcall 'nhận được là bạn thực sự không cần tham gia vào bảng "tra cứu" khi bạn sử dụng khóa tự nhiên. So sánh một truy vấn để có được giá trị tra cứu với một phép nối, với một truy vấn trong đó giá trị đã được lưu trữ trong bảng chính. Bạn có thể nhận được một "người chiến thắng" khác nhau tùy thuộc vào độ dài khóa tự nhiên và số lượng hàng trong bảng tra cứu.
Mikael Eriksson

3
Những gì @MikaelEriksson nói cộng với các trường hợp khi bạn có một liên kết giữa hơn 2 bảng (giả sử 4) trong đó với những người thay thế bạn sẽ phải tham gia các bảng từ A đến D đến B và C trong khi với các phím tự nhiên bạn có thể tham gia trực tiếp từ A đến D
ypercubeᵀᴹ

Câu trả lời:


18

Nói chung, SQL Server sử dụng Cây B + cho các chỉ mục. Chi phí tìm kiếm chỉ mục có liên quan trực tiếp đến độ dài của khóa trong định dạng lưu trữ này. Do đó, khóa thay thế thường vượt trội so với khóa tự nhiên trên chỉ số tìm kiếm.

SQL Server cụm một bảng trên khóa chính theo mặc định. Khóa chỉ mục được nhóm được sử dụng để xác định các hàng, do đó, nó được thêm dưới dạng cột được bao gồm cho mọi chỉ mục khác. Khóa đó càng rộng, chỉ số phụ càng lớn.

Thậm chí tệ hơn, nếu các chỉ mục phụ không được xác định rõ ràng vì UNIQUEkhóa chỉ mục được nhóm tự động trở thành một phần của khóa của mỗi chỉ mục đó. Điều đó thường áp dụng cho hầu hết các chỉ mục, vì thông thường các chỉ mục được khai báo là duy nhất khi yêu cầu là thực thi tính duy nhất.

Vì vậy, nếu câu hỏi là, chỉ số cụm tự nhiên so với thay thế, người thay thế hầu như sẽ luôn giành chiến thắng.

Mặt khác, bạn đang thêm cột thay thế đó vào bảng làm cho bảng tự lớn hơn. Điều đó sẽ khiến quét chỉ mục cụm trở nên đắt hơn. Vì vậy, nếu bạn chỉ có rất ít chỉ mục phụ và khối lượng công việc của bạn yêu cầu phải xem xét tất cả (hoặc hầu hết các hàng) thường xuyên, bạn thực sự có thể tốt hơn với một khóa tự nhiên tiết kiệm thêm vài byte.

Cuối cùng, các khóa tự nhiên thường giúp dễ hiểu mô hình dữ liệu hơn. Trong khi sử dụng nhiều không gian lưu trữ hơn, các khóa chính tự nhiên dẫn đến các khóa ngoại tự nhiên, từ đó làm tăng mật độ thông tin cục bộ.

Vì vậy, như thường thấy trong thế giới cơ sở dữ liệu, câu trả lời thực sự là "nó phụ thuộc". Và - luôn luôn kiểm tra trong môi trường của riêng bạn với dữ liệu thực tế.


10

Tôi tin rằng, tốt nhất nằm ở giữa .

Tổng quan về khóa tự nhiên:

  1. Họ đang làm cho mô hình dữ liệu rõ ràng hơn vì chúng đến từ khu vực chủ đề chứ không phải từ đầu của ai đó.
  2. Các khóa đơn giản (một cột, giữa CHAR(4)CHAR(20)) đang lưu một số byte bổ sung, nhưng bạn cần xem tính nhất quán của chúng ( ON UPDATE CASCADEtrở nên quan trọng đối với các khóa đó, có thể bị thay đổi).
  3. Rất nhiều trường hợp, khi các khóa tự nhiên phức tạp: bao gồm hai hoặc nhiều cột. Nếu khóa đó có thể di chuyển sang thực thể khác dưới dạng khóa bỏ qua, thì nó sẽ thêm chi phí dữ liệu (chỉ số và cột dữ liệu có thể trở nên lớn) và hiệu suất bị mất.
  4. Nếu khóa là một chuỗi lớn, thì có lẽ nó sẽ luôn bị lỏng với một khóa số nguyên, bởi vì điều kiện tìm kiếm đơn giản là so sánh mảng byte trong một công cụ cơ sở dữ liệu, trong hầu hết các trường hợp là chậm hơn so với so sánh số nguyên.
  5. Nếu khóa là một chuỗi đa ngôn ngữ thì cũng cần phải xem các đối chiếu.

Lợi ích: 1 và 2.

Cảnh giác: 3, 4 và 5.


Tổng quan về khóa nhận dạng nhân tạo:

  1. Bạn không cần bận tâm về việc tạo và xử lý chúng (trong hầu hết các trường hợp) vì tính năng này được xử lý bởi công cụ cơ sở dữ liệu. Chúng là duy nhất theo mặc định và không tốn nhiều không gian. Các hoạt động tùy chỉnh như ON UPDATE CASCADEcó thể bị dừng lại, bởi vì các giá trị chính không thay đổi.

  2. Họ (thường) là ứng cử viên tốt nhất để di chuyển dưới dạng khóa ngoại vì:

    2.1. bao gồm một cột;

    2.2. sử dụng một loại đơn giản có trọng lượng nhỏ và hoạt động nhanh cho các hoạt động so sánh.

  3. Đối với một thực thể kết hợp, các khóa không được di chuyển ở bất cứ đâu, nó có thể trở thành một chi phí dữ liệu thuần túy, vì tính hữu dụng của nó bị mất. Khóa chính tự nhiên phức tạp (nếu không có cột chuỗi ở đó) sẽ hữu ích hơn.

Lợi ích: 1 và 2.

Cảnh giác: 3.


PHẦN KẾT LUẬN:

Các khóa Arificial dễ bảo trì hơn, đáng tin cậy và nhanh hơn vì chúng đã được thiết kế cho các tính năng này. Nhưng trong một số trường hợp không cần thiết. Ví dụ, CHAR(4)ứng cử viên cột đơn trong hầu hết các trường hợp hành xử như thế nào INT IDENTITY. Vì vậy, có một câu hỏi khác ở đây: khả năng duy trì + ổn định hoặc rõ ràng ?

Câu hỏi "Tôi có nên tiêm chìa khóa nhân tạo hay không?" luôn phụ thuộc vào cấu trúc khóa tự nhiên:

  • Nếu nó chứa một chuỗi lớn, thì nó chậm hơn và sẽ thêm chi phí dữ liệu nếu di chuyển như nước ngoài sang thực thể khác.
  • Nếu nó bao gồm nhiều cột, thì nó chậm hơn và sẽ thêm chi phí dữ liệu nếu di chuyển như nước ngoài sang thực thể khác.

5
"Các hoạt động tùy chỉnh như TRÊN CẬP NHẬT CASCADE có thể bị dừng lại, vì các giá trị chính không thay đổi." Tác dụng của các khóa thay thế là làm cho mọi tham chiếu khóa ngoại tương đương với "TRÊN CẬP NHẬT CASCADE". Chìa khóa không thay đổi, nhưng giá trị nó đại diện thực hiện .
Mike Sherrill 'Nhớ lại mèo'

@ MikeSherrill'Catcall 'Vâng, tất nhiên. Tuy nhiên, ON UPDATE CASCADEkhông được sử dụng, trong khi các phím không bao giờ được cập nhật. Nhưng, nếu có, thì đó có thể là một vấn đề nếu ON UPDATE NO ACTIONđược cấu hình. Ý tôi là, DBMS không bao giờ sử dụng nó, trong khi các giá trị cột chính không thay đổi.
BlitZ

4

Khóa là một tính năng logic của cơ sở dữ liệu trong khi hiệu suất luôn được xác định bởi việc thực hiện vật lý trong lưu trữ và bởi các hoạt động vật lý chạy theo thực hiện đó. Do đó, đó là một sai lầm khi quy các đặc tính hiệu suất cho các khóa.

Tuy nhiên, trong ví dụ cụ thể này, hai triển khai bảng và truy vấn có thể được so sánh với nhau. Ví dụ không trả lời câu hỏi được đặt ra trong tiêu đề ở đây. Việc so sánh được thực hiện là các phép nối sử dụng hai kiểu dữ liệu khác nhau (số nguyên và ký tự) chỉ bằng một loại chỉ mục (cây B). Một điểm "rõ ràng" là nếu một chỉ mục băm hoặc loại chỉ mục khác được sử dụng thì hoàn toàn có thể không có sự khác biệt hiệu suất có thể đo lường được giữa hai lần thực hiện. Tuy nhiên, có nhiều vấn đề cơ bản hơn với ví dụ.

Hai truy vấn đang được so sánh về hiệu suất nhưng hai truy vấn không tương đương về mặt logic vì chúng trả về các kết quả khác nhau! Một thử nghiệm thực tế hơn sẽ so sánh hai truy vấn trả về cùng một kết quả nhưng sử dụng các triển khai khác nhau.

Điểm cốt yếu về khóa thay thế là nó là một thuộc tính bổ sung trong bảng trong đó bảng cũng có các thuộc tính khóa "có ý nghĩa" được sử dụng trong miền doanh nghiệp. Đây là thuộc tính không thay thế được quan tâm để kết quả truy vấn có ích. Do đó, một thử nghiệm thực tế sẽ so sánh các bảng chỉ sử dụng các khóa tự nhiên với việc triển khai thay thế có cả khóa tự nhiên khóa thay thế trong cùng một bảng. Các khóa thay thế thường yêu cầu lưu trữ và lập chỉ mục bổ sung và theo định nghĩa yêu cầu các ràng buộc duy nhất bổ sung. Người thay thế yêu cầu xử lý bổ sung để ánh xạ các giá trị khóa tự nhiên bên ngoài lên người thay thế của họ và ngược lại.

Bây giờ so sánh truy vấn tiềm năng này:

A.

SELECT t2.NaturalTable2Key, t2.NaturalTable1Key
FROM Table2 t2;

Tương đương logic của nó nếu thuộc tính NaturalTable1Key trong Bảng2 được thay thế bằng IDTable1Key thay thế:

B.

SELECT t2.NaturalTable2Key, t1.NaturalTable1Key
FROM Table2 t2
INNER JOIN Table1 t1
ON t1.IDTable1Key = t2.IDTable1Key;

Truy vấn B yêu cầu tham gia; Truy vấn A thì không. Đây là một tình huống quen thuộc trong cơ sở dữ liệu (hơn) sử dụng thay thế. Các truy vấn trở nên phức tạp không cần thiết và khó khăn hơn nhiều để tối ưu hóa. Logic nghiệp vụ (đặc biệt là các ràng buộc toàn vẹn dữ liệu) trở nên khó thực hiện, kiểm tra và xác minh hơ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.