SQL Server nvarchar (max) vs nvarchar (n) ảnh hưởng đến hiệu suất


16

Đây là SQL Server 2008 R2 SP2. Tôi có 2 bàn. Cả hai đều giống hệt nhau (dữ liệu và lập chỉ mục), ngoại trừ bảng đầu tiên có cột GIÁ TRỊ nvarchar(max)và bảng thứ hai có cùng cột với nvarchar(800). Cột này được bao gồm trong một chỉ mục không cụm. Tôi cũng đã tạo một chỉ mục cụm trên cả hai bảng. Tôi cũng đã xây dựng lại các chỉ mục. Độ dài chuỗi tối đa trong cột này là 650.

Nếu tôi chạy cùng một truy vấn đối với cả hai nvarchar(800)bảng thì nhanh hơn, nhanh gấp nhiều lần. Chắc chắn có vẻ như nó đang đánh bại mục đích của "varchar". Bảng chứa hơn 800.000 hàng. Truy vấn nên xem xét khoảng 110.000 hàng (đó là những gì kế hoạch ước tính).

Theo thống kê của io, không có số lần đọc lob, vì vậy mọi thứ dường như được xếp thành hàng. Các kế hoạch thực hiện là như nhau, ngoại trừ có một chút khác biệt về tỷ lệ phần trăm chi phí giữa hai bảng và kích thước hàng ước tính lớn hơn với nvarchar(max)(91 byte so với 63 byte). Số lần đọc cũng khá giống nhau.

Tại sao lại có sự khác biệt?

===== Lược đồ ======

 CREATE TABLE [dbo].[table1](
        [ID] [bigint] IDENTITY(1,1) NOT NULL,
        [ProductID] [bigint] NOT NULL,
        [ProductSkeletonID] [bigint] NOT NULL,
        [Value] [nvarchar](max) NOT NULL,
        [IsKeywordSearchable] [bit] NULL,
        [ValueInteger] [bigint] NULL,
        [ValueDecimal] [decimal](18, 2) NULL,
        [ValueDate] [datetime] NULL,
        [TypeOfData] [nvarchar](20) NOT NULL,
     CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED 
    (
        [ID] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

    CREATE NONCLUSTERED INDEX [IX_table1_productskeletonid] ON [dbo].[table1] 
    (
        [ProductSkeletonID] ASC
    )
    INCLUDE ( [ProductID],
    [Value]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

    CREATE TABLE [dbo].[table2](
        [ID] [bigint] IDENTITY(1,1) NOT NULL,
        [ProductID] [bigint] NOT NULL,
        [ProductSkeletonID] [bigint] NOT NULL,
        [Value] [nvarchar](800) NOT NULL,
        [IsKeywordSearchable] [bit] NULL,
        [ValueInteger] [bigint] NULL,
        [ValueDecimal] [decimal](18, 2) NULL,
        [ValueDate] [datetime] NULL,
        [TypeOfData] [nvarchar](20) NOT NULL,
     CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED 
    (
        [ID] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]

    CREATE NONCLUSTERED INDEX [IX_table2_productskeletonid] ON [dbo].[table2] 
    (
        [ProductSkeletonID] ASC
    )
    INCLUDE ( [ProductID],
    [Value]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]


CREATE TABLE [dbo].[table_results](
    [SearchID] [bigint] NOT NULL,
    [RowNbr] [int] NOT NULL,
    [ProductID] [bigint] NOT NULL,
    [PermissionList] [varchar](250) NULL,
    [SearchWeight] [int] NULL,
 CONSTRAINT [PK_table_results] PRIMARY KEY NONCLUSTERED 
(
    [SearchID] ASC,
    [RowNbr] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_table_results_SearchID] ON [dbo].[cart_product_searches_results] 
(
    [SearchID] ASC
)
INCLUDE ( [ProductID]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

===== Truy vấn Bảng1 ======

    SELECT cppev.ProductSkeletonID, cppev.Value, COUNT(*) AS Value FROM table1 cppev
    JOIN search_results cpsr ON cppev.ProductID = cpsr.ProductID AND cpsr.SearchID = 227568 
    WHERE cppev.ProductSkeletonID in (3191, 3160, 3158, 3201)
    GROUP BY cppev.ProductSkeletonID, cppev.Value

    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'table1'. Scan count 4, logical reads 582, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'table_results'. Scan count 1, logical reads 82, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    SQL Server Execution Times:
       CPU time = 1373 ms,  elapsed time = 1576 ms.

 |--Compute Scalar(DEFINE:([Expr1005]=CONVERT_IMPLICIT(int,[Expr1008],0)))
       |--Stream Aggregate(GROUP BY:([cppev].[Value], [cppev].[ProductSkeletonID]) DEFINE:([Expr1008]=Count(*)))
            |--Sort(ORDER BY:([cppev].[Value] ASC, [cppev].[ProductSkeletonID] ASC))
                 |--Hash Match(Inner Join, HASH:([cpsr].[ProductID])=([cppev].[ProductID]), RESIDUAL:([dbo].[table1].[ProductID] as [cppev].[ProductID]=[dbo].[table_results].[ProductID] as [cpsr].[ProductID]))
                      |--Index Seek(OBJECT:([dbo].[table_results].[IX_table_results_SearchID] AS [cpsr]), SEEK:([cpsr].[SearchID]=(227568)) ORDERED FORWARD)
                      |--Index Seek(OBJECT:([dbo].[table1].[IX_table1_productskeletonid] AS [cppev]), SEEK:([cppev].[ProductSkeletonID]=(3158) OR [cppev].[ProductSkeletonID]=(3160) OR [cppev].[ProductSkeletonID]=(3191) OR [cppev].[ProductSkeletonID]=(3201)) ORDERED FORWARD)

===== Truy vấn Table2 ======

    SELECT cppev.ProductSkeletonID, cppev.Value, COUNT(*) AS Value FROM table2 cppev
    JOIN table_results cpsr ON cppev.ProductID = cpsr.ProductID AND cpsr.SearchID = 227568 
    WHERE cppev.ProductSkeletonID in (3191, 3160, 3158, 3201)
    GROUP BY cppev.ProductSkeletonID, cppev.Value

    Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'table2'. Scan count 4, logical reads 584, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'table_results'. Scan count 1, logical reads 82, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

    SQL Server Execution Times:
       CPU time = 484 ms,  elapsed time = 796 ms.

  |--Compute Scalar(DEFINE:([Expr1005]=CONVERT_IMPLICIT(int,[Expr1008],0)))
       |--Stream Aggregate(GROUP BY:([cppev].[Value], [cppev].[ProductSkeletonID]) DEFINE:([Expr1008]=Count(*)))
            |--Sort(ORDER BY:([cppev].[Value] ASC, [cppev].[ProductSkeletonID] ASC))
                 |--Hash Match(Inner Join, HASH:([cpsr].[ProductID])=([cppev].[ProductID]), RESIDUAL:([auctori_core_v40_D].[dbo].[table2].[ProductID] as [cppev].[ProductID]= [dbo].[table2].[ProductID] as [cpsr].[ProductID]))
                      |--Index Seek(OBJECT:([dbo].[table_results].[IX_table_results_SearchID] AS [cpsr]), SEEK:([cpsr].[SearchID]=(227568)) ORDERED FORWARD)
                      |--Index Seek(OBJECT:([dbo].[table2].[IX_table2_productskeletonid] AS [cppev]), SEEK:([cppev].[ProductSkeletonID]=(3158) OR [cppev].[ProductSkeletonID]=(3160) OR [cppev].[ProductSkeletonID]=(3191) OR [cppev].[ProductSkeletonID]=(3201)) ORDERED FORWARD)

4
Truy vấn, lược đồ bảng, mẫu hoặc dữ liệu chỉ định và kế hoạch thực hiện cho mỗi truy vấn xin vui lòng. "Tôi không nghĩ ..." không giống như "Chắc chắn là không ...".
Mark Storey-Smith

Bạn có phiên bản SQL Server nào?
Max Vernon

Xem technet.microsoft.com/en-us/l Library / ms189087 (v ​​= SQL.105) .aspx để biết chi tiết về lưu trữ liên tiếp cho các trường nvarchar (tối đa). Làm thế nào lớn là dữ liệu thực tế trong các lĩnh vực?
Max Vernon

Tôi đã cập nhật bài viết để giải quyết các phản hồi trên.
Brian Bohl

Câu trả lời:


14

Bạn đang thấy chi phí sử dụng MAXcác loại.

Mặc dù NVARCHAR(MAX)giống hệt NVARCHAR(n)trong TSQL và có thể được lưu trữ liên tiếp, nhưng nó được xử lý riêng bởi công cụ lưu trữ vì nó có thể được đẩy ra khỏi hàng. Khi off-row nó là một LOB_DATAđơn vị phân bổ, thay vì ROW_OVERFLOW_DATAđơn vị phân bổ và chúng tôi có thể giả định từ các quan sát của bạn rằng điều này mang một chi phí chung.

Bạn có thể thấy hai loại được lưu trữ nội bộ khác nhau với một trò chơi DBCC PAGE nhỏ . Mark Rasmussen đã đăng các trang ví dụ cho thấy sự khác biệt trong Kích thước của Con trỏ LOB cho các loại (MAX) như Varchar, Varbinary, Etc là gì?

Chúng tôi có thể giả định rằng đó là GROUP BYtrên MAXcột gây ra sự khác biệt hiệu suất trong trường hợp của bạn. Tôi đã không thử nghiệm các hoạt động khác trên một MAXloại nhưng nó có thể thú vị để làm như vậy và xem nếu kết quả tương tự được nhìn thấy.


Vì vậy, bạn đang nói rằng có quá trình xử lý bổ sung khi đọc [Dữ liệu nội tuyến BLOB] so với var var ol ol? Tôi đã mong đợi chi phí đáng kể nếu nó đi ra khỏi hàng, nhưng tất cả các dữ liệu này là nội tuyến (được sử dụng dbcc ind). Và tại sao bạn nghĩ rằng nhóm bằng cách đưa ra điều này?
Brian Bohl

Một chút chi phí để đọc nó, rất nhiều cho bất kỳ tính toán nào trên đó, vd GROUP BY. @RemusRusanu có thể cung cấp một số thông tin chi tiết (hy vọng anh ấy sẽ thấy ping).
Mark Storey-Smith

Tôi tìm thấy một bài viết khác ghi lại hành vi tương tự, thậm chí là bằng và thích. Tôi tự hỏi nếu nvarchar (max) sử dụng thuật toán kém hiệu quả hơn.
Brian Bohl
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.