Làm thế nào NẾU KHÔNG EXISTS CHỌN THÌ CHỌN nhanh hơn chỉ số ĐỘC ĐÁO?


7

Trong SQL Server, ...

Sp:

CREATE PROCEDURE insertToTable
    @field1 VARCHAR(256), @field2 varchar(256), @field3 varchar(256)
AS
BEGIN
    SET NOCOUNT ON

    IF NOT EXISTS (SELECT * FROM my_table WHERE field1 = @field1)
      INSERT INTO my_table
        (field1, field2, field3)
      VALUES (@field1, @field2, @field3);
    ELSE
      THROW 50000, 'xxxxxx', 1;
    END
GO

Bàn:

CREATE TABLE my_table (
    field1 VARCHAR(256) NOT NULL,
    field2 VARCHAR(256) NOT NULL,
    field3 VARCHAR(256) NOT NULL
);
CREATE INDEX idx_field1 ON my_table(field1);

cái trên nhanh hơn cái dưới?

Sp:

CREATE PROCEDURE insertToTable
    @field1 VARCHAR(256), @field2 varchar(256), @field3 varchar(256)
AS
BEGIN
    SET NOCOUNT ON

    INSERT INTO my_table
        (field1, field2, field3)
    VALUES (@field1, @field2, @field3);
GO

Bàn:

CREATE TABLE my_table (
    field1 VARCHAR(256) NOT NULL,
    field2 VARCHAR(256) NOT NULL,
    field3 VARCHAR(256) NOT NULL
);
CREATE UNIQUE INDEX idx_field1 ON my_table(field1);

Đầu vào mẫu:

trường1: F56yCgZ9AEm9aFpTyjwhERtqNeglYEow

trường2:

trường3: A18E9049117A77E6A4D41C6CA3FFDEA65D842BF1F57705405B4E66969531D93D

Đầu vào được tạo ra một cách nhanh chóng bởi ứng dụng web và sử dụng các câu lệnh được chuẩn bị. Tôi sử dụng Jmeter để tạo yêu cầu cho ứng dụng web của mình.

Với UNIQUEchỉ mục, hiệu suất chèn giảm xuống sau khi chèn 100K và trở nên tồi tệ hơn.

Với NON UNIQUEchỉ mục và kiểm tra thủ công với IF NOT EXISTS SELECT, hiệu suất là không đổi ngay cả với hàng triệu bản ghi được chèn vào.

Các giá trị đủ độc đáo để không bao giờ có sự trùng lặp được tạo. Ngay cả sau một vài triệu giá trị được chèn vào.


Có thể phải làm thế nào để không nhận được khóa độc quyền và không quay trở lại giao dịch.
eckes

1
Tôi có khó khăn để có được toàn bộ hình ảnh ở đây. Bạn có thể đăng mã SQL đầy đủ không? Đối với cả hai trường hợp ...
Tibor Karaszi

1
Làm thế nào bạn đo tốc độ? Bạn có đang sử dụng THỐNG KÊ THỜI GIAN TỚI? Truy vấn nhanh hơn có thể diễn ra song song (sử dụng nhiều thời gian CPU hơn) và truy vấn chậm hơn có thể đang chạy một luồng (thời gian CPU ít hơn).
bình tĩnh

1
Vui lòng gửi một tập lệnh SQL đầy đủ thể hiện hiện tượng bạn đang hỏi về
Martin Smith

1
Vì vậy, tôi đã tạo một tập lệnh và chạy nó dựa trên cơ sở dữ liệu SQL Server 2017 mới được tạo với tệp nhật ký và dữ liệu được quy định là 4GB. Kiểm tra duy nhất không độc đáo và kiểm tra tại nhà (và không an toàn chủ đề) mất 231.101 giây. Một với chỉ số unque mất 194.940 giây. Tập lệnh đã sử dụng pastebin.com/nCB7QFt3 . Tập lệnh xuất pastebin.com/eHDfdHhc
Martin Smith

Câu trả lời:


5

CẬP NHẬT CUỐI CÙNG:

Đó là INSERT thực sự làm mọi thứ chậm lại.

Khi có một chỉ mục duy nhất, với mỗi bản ghi mới bạn thêm SQL phải kiểm tra xem giá trị đã tồn tại chưa. Khi bảng phát triển, số lượng tham chiếu chéo tăng lên. Một chỉ mục không duy nhất sẽ không yêu cầu tham chiếu chéo nên hiệu suất là không đổi.

Các chỉ mục duy nhất thường nhanh hơn cho các câu lệnh CHỌN nhưng điều đó phải trả giá khi cập nhật bảng.

Dưới đây là lý do tại sao đôi khi CHỌN có thể chậm hơn trên Chỉ mục duy nhất

Tôi đã tái tạo một phần tình huống của bạn đến mức tôi nghĩ rằng đó là sự kết hợp giữa việc đánh hơi Thông số và các ưu tiên SQL bằng cách sử dụng chỉ số NON-UNIQUE trên HEAP.

Thiết lập 2 bảng thử nghiệm, một trong số chúng là một đống (giống như bảng của bạn).

CREATE TABLE dbo.TEST1(ID VARCHAR(255) NOT NULL,TXT1 VARCHAR(255) NOT NULL,TXT2 VARCHAR(255) NOT NULL)
CREATE TABLE dbo.TEST2(ID VARCHAR(255) NOT NULL,TXT1 VARCHAR(255) NOT NULL,TXT2 VARCHAR(255) NOT NULL)
GO
INSERT INTO dbo.TEST1 VALUES(NEWID(),NEWID(),NEWID())
GO 30000

INSERT INTO dbo.TEST2
SELECT * FROM dbo.TEST1
GO

CREATE CLUSTERED INDEX cidx ON dbo.TEST1 (ID)
CREATE INDEX idx_nu ON dbo.TEST1 (ID)
CREATE UNIQUE INDEX idx_u ON dbo.TEST1 (ID)

CREATE INDEX idx_nu ON dbo.TEST2 (ID)
CREATE UNIQUE INDEX idx_u ON dbo.TEST2 (ID)

Xem lại dấu chân của các Chỉ mục, trên HEAP, chỉ số UNIQUE có dấu chân nhỏ hơn chỉ số KHÔNG ĐỘC ĐÁO. (có lẽ các trang của chỉ mục NON-UNIQUE chứa thông tin bổ sung - có thể hữu ích -) vấn đề.)

SELECT
    s.name AS SchemaName,
    t.name AS TableName,
    i.name AS IndexName,
    p.row_count,
    SUM (p.used_page_count) as used_pages_count,
    SUM (CASE
            WHEN (i.index_id < 2) THEN (in_row_data_page_count + lob_used_page_count + row_overflow_used_page_count)
            ELSE lob_used_page_count + row_overflow_used_page_count
        END) as pages
FROM 
    sys.dm_db_partition_stats  AS p 
        JOIN sys.tables AS t 
            ON 
            p.object_id = t.object_id
        JOIN sys.indexes AS i 
            ON 
            i.[object_id] = t.[object_id] 
            AND 
            p.index_id = i.index_id
        JOIN sys.schemas AS s 
            ON
            t.schema_id = s.schema_id
WHERE
    t.name IN ('TEST1','TEST2')
GROUP BY 
    s.name
    ,t.name
    ,i.name
    ,p.row_count

Bây giờ truy vấn các bảng với chữ và biến.

--SCAN of the UNIQUE index
DECLARE @account_id VARCHAR(255) = (SELECT TOP 1 ID FROM dbo.TEST2 WHERE ID like '%A%') 

--Parameter Sniffing kicks in --The optimiser doesn't know the value of @account_id

--SEEK of the CLUSTERED index 
DECLARE @ID1 VARCHAR(255)  = (SELECT TOP 1 ID FROM dbo.TEST1 WHERE ID = @account_id)

--SEEK of the NON UNIQUE index
DECLARE @ID2 VARCHAR(255)  = (SELECT TOP 1 ID FROM dbo.TEST2 WHERE ID = @account_id)

Vì một số lý do, SQL thích chỉ số NON UNIQUE trên HEAP khi thực hiện các hoạt động XEMK.

Đây là những gì tôi nghĩ đang diễn ra. Khi chỉ mục Không duy nhất có nhiều Trang hơn thì Biểu đồ tương ứng trong STATS có nhiều BƯỚC hơn, hãy chạy bên dưới mã.

DBCC SHOW_STATISTICS ( 'TEST2' , 'idx_nu' )
DBCC SHOW_STATISTICS ( 'TEST2' , 'idx_u' )

Các STEPS bổ sung tạo ra một cái nhìn chi tiết hơn về Chỉ số cơ bản, do đó Trình tối ưu hóa (biết rằng EQ_lawS luôn là 1) sẽ có được Ước tính Cardinality tốt hơn từ chỉ mục Không duy nhất.


Vậy tại sao phải bận tâm với chỉ số UNIITE khi NẾU KHÔNG EXISTS CHỌN thực hiện công việc tốt hơn?
dùng547

@ user547 một chỉ mục duy nhất sẽ nhanh hơn khi thực hiện Chọn câu lệnh trên bảng, nhưng điều này phải trả giá khi thực hiện cập nhật. Nó phụ thuộc vào loại hoạt động mà bảng của bạn chủ yếu sẽ phải tuân theo. Nếu có thể, tôi sẽ vô hiệu hóa Chỉ mục duy nhất trong khi BẮT ĐẦU, sau đó xây dựng lại Chỉ mục khi quá trình chèn hoàn tất. Ví dụ chọn của tôi từ trên là một trường hợp rất hiếm mà tôi chỉ thấy thú vị. ALTER INDEX idx TRÊN dbo. DISABLE DISABLE; ALTER INDEX idx TRÊN dbo. REBUILD;
bình tĩnh

Ah không thể vô hiệu hóa. Đây là một ứng dụng "thời gian thực". Có nghĩa là "người dùng" có thể tự đăng ký khi cần thiết. Bạn biết như một trang Đăng ký bình thường.
dùng547

1
Đối số của bạn không thực sự có ý nghĩa bởi vì họ đang thực hiện thủ công "tham chiếu chéo" giống như chỉ mục. Nhưng khi được thực hiện như là một phần của phần chèn thì điều này sẽ không quan trọng vì nó cần xác định đúng vị trí trong chỉ mục để chèn hàng.
Martin Smith

1
Họ đã không cung cấp bất kỳ lời trách móc nào mà chúng ta thực sự có thể chạy mà chứng minh hiện tượng này thậm chí còn tồn tại. Đó có thể là một lỗi với phương pháp đo điểm chuẩn của họ
Martin Smith
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.