SQL Server nvarchar (สูงสุด) vs nvarchar (n) มีผลต่อประสิทธิภาพ


16

นี่คือ SQL Server 2008 R2 SP2 ฉันมี 2 ตาราง ทั้งสองมีความเหมือนกัน (ข้อมูลและการจัดทำดัชนี) ยกเว้นตารางแรกมีคอลัมน์ VALUE เป็นและครั้งที่สองมีคอลัมน์เดียวกับnvarchar(max) nvarchar(800)คอลัมน์นี้รวมอยู่ในดัชนีที่ไม่ทำคลัสเตอร์ ฉันยังสร้างดัชนีคลัสเตอร์บนตารางทั้งสอง ฉันได้สร้างดัชนีอีกครั้ง ความยาวสตริงสูงสุดในคอลัมน์นี้คือ 650

ถ้าฉันเรียกใช้แบบสอบถามเดียวกันกับทั้งสองnvarchar(800)ตารางจะเร็วขึ้นอย่างต่อเนื่องหลาย ๆ ครั้งเร็วเป็นสองเท่า แน่นอนว่าดูเหมือนจะเอาชนะวัตถุประสงค์ของ "varchar" ตารางประกอบด้วย 800,000+ แถว แบบสอบถามควรดูประมาณ 110,000 แถว (ซึ่งเป็นสิ่งที่แผนประมาณการ)

ตามสถิติของ io จะไม่มีลูกเทนนิสอ่านดังนั้นทุกอย่างดูเหมือนจะอยู่ในแถว แผนการดำเนินการเหมือนกันยกเว้นมีความแตกต่างเล็กน้อยในเปอร์เซ็นต์ต้นทุนระหว่างสองตารางและขนาดแถวโดยประมาณนั้นใหญ่กว่าด้วยnvarchar(max)(91 ไบต์เทียบกับ 63 ไบต์) จำนวนการอ่านก็ค่อนข้างเหมือนกัน

ทำไมถึงแตกต่าง

===== Schema ======

 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]

===== แบบสอบถาม Table1 ======

    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)

===== แบบสอบถาม 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
แบบสอบถามสคีมาตารางตัวอย่างหรือข้อมูลที่บ่งบอกถึงและแผนการดำเนินการสำหรับแต่ละแบบสอบถามโปรด "ฉันไม่คิดว่า ... " ไม่เหมือนกับ "ไม่มีแน่นอน ... "
Mark Storey-Smith

คุณมี SQL Server รุ่นใด
Max Vernon

ดูtechnet.microsoft.com/en-us/library/ms189087(v=SQL.105).aspxสำหรับรายละเอียดเกี่ยวกับที่เก็บข้อมูลในแถวสำหรับฟิลด์ nvarchar (สูงสุด) ข้อมูลจริงในเขตข้อมูลเหล่านั้นมีขนาดเท่าใด
Max Vernon

ฉันอัปเดตโพสต์เพื่อตอบข้อเสนอแนะด้านบน
Brian Bohl

คำตอบ:


14

คุณเห็นต้นทุนค่าใช้จ่ายในการใช้MAXประเภท

ในขณะที่NVARCHAR(MAX)เหมือนกันNVARCHAR(n)ใน TSQL และสามารถจัดเก็บในแถวมันถูกจัดการแยกต่างหากโดยเครื่องมือเก็บข้อมูลเพราะมันสามารถผลักออกจากแถว เมื่อออฟโรดเป็นLOB_DATAหน่วยการจัดสรรแทนที่จะเป็นROW_OVERFLOW_DATAหน่วยการจัดสรรและเราสามารถสันนิษฐานได้จากการสังเกตของคุณว่าสิ่งนี้มีค่าใช้จ่าย

คุณสามารถมองเห็นทั้งสองประเภทจะถูกเก็บไว้ภายในที่แตกต่างกันมีน้อยspelunking DBCC หน้า Mark Rasmussen ที่ดัมพ์หน้าตัวอย่างที่แสดงความแตกต่างในขนาดของ LOB Pointer สำหรับ (MAX) ประเภทคืออะไรเช่น Varchar, Varbinary, Etc?

เราอาจจะถือว่ามันเป็นGROUP BYในMAXคอลัมน์ที่ทำให้เกิดความแตกต่างของประสิทธิภาพการทำงานในกรณีของคุณ ฉันไม่ได้ทดสอบการทำงานMAXประเภทอื่น ๆแต่อาจน่าสนใจที่จะทำเช่นนั้นและดูว่ามีผลลัพธ์ที่คล้ายกันหรือไม่


ดังนั้นคุณกำลังพูดว่ามีการประมวลผลพิเศษอ่าน [BLOB Inline Data] เทียบกับ 'ol varchar ธรรมดา? ฉันคาดหวังค่าใช้จ่ายที่สำคัญถ้ามันออกไปแถว แต่ข้อมูลทั้งหมดนี้เป็นแบบอินไลน์ (ใช้ dbcc ind) และทำไมคุณถึงคิดว่ากลุ่มโดยนำออกมา?
Brian Bohl

ค่าใช้จ่ายเล็ก ๆ น้อย ๆ สำหรับการอ่านมันจำนวนมากในการคำนวณใด ๆ GROUP BYเกี่ยวกับมันเช่น @RemusRusanu อาจเสนอข้อมูลเชิงลึกบางอย่าง (หวังว่าเขาจะเห็น ping)
Mark Storey-Smith

ฉันพบบทความอื่นที่บันทึกพฤติกรรมเดียวกันแม้จะเท่ากันหรือไม่ก็ตาม ฉันสงสัยว่า nvarchar (สูงสุด) ใช้อัลกอริทึมที่มีประสิทธิภาพน้อยกว่าหรือไม่
Brian Bohl
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.