สถิติหายไปหลังจากการอัพเดตที่เพิ่มขึ้น


21

เรามีฐานข้อมูล SQL Server ขนาดใหญ่ที่แบ่งพาร์ติชันโดยใช้สถิติที่เพิ่มขึ้น ดัชนีทั้งหมดได้รับการแบ่งพาร์ติชันแล้ว เมื่อเราพยายามที่จะสร้างพาร์ติชันออนไลน์ใหม่โดยการแบ่งพาร์ติชันสถิติทั้งหมดจะหายไปหลังจากสร้างดัชนีใหม่

ด้านล่างเป็นสคริปต์เพื่อทำซ้ำปัญหาใน SQL Server 2014 ด้วยฐานข้อมูล AdventureWorks2014

--Example against AdventureWorks2014 Database

CREATE PARTITION FUNCTION TransactionRangePF1 (DATETIME)
AS RANGE RIGHT FOR VALUES 
(
   '20130501', '20130601', '20130701', '20130801', 
   '20130901', '20131001', '20131101', '20131201', 
   '20140101', '20140201', '20140301'
);
GO

CREATE PARTITION SCHEME TransactionsPS1 AS PARTITION TransactionRangePF1 TO 
(
  [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], 
  [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY], 
  [PRIMARY], [PRIMARY], [PRIMARY]
);
GO

CREATE TABLE dbo.TransactionHistory 
(
  TransactionID        INT      NOT NULL, -- not bothering with IDENTITY here
  ProductID            INT      NOT NULL,
  ReferenceOrderID     INT      NOT NULL,
  ReferenceOrderLineID INT      NOT NULL DEFAULT (0),
  TransactionDate      DATETIME NOT NULL DEFAULT (GETDATE()),
  TransactionType      NCHAR(1) NOT NULL,
  Quantity             INT      NOT NULL,
  ActualCost           MONEY    NOT NULL,
  ModifiedDate         DATETIME NOT NULL DEFAULT (GETDATE()),
  CONSTRAINT CK_TransactionType 
    CHECK (UPPER(TransactionType) IN (N'W', N'S', N'P'))
) 
ON TransactionsPS1 (TransactionDate);


INSERT INTO dbo.TransactionHistory
SELECT * FROM Production.TransactionHistory
--  SELECT * FROM sys.partitions
--  WHERE object_id = OBJECT_ID('dbo.TransactionHistory');

CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId) 
  WITH (DATA_COMPRESSION = ROW, STATISTICS_INCREMENTAL=ON)  
  ON TransactionsPS1 (TransactionDate)

DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);
PRINT 'Stats are avialable'  

ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = ON , DATA_COMPRESSION = ROW)

PRINT 'After online index rebuild by partition stats are now gone'
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

PRINT 'Rebuild the stats with a rebuild for all paritions (this works)' 
ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = ALL WITH (ONLINE = ON , DATA_COMPRESSION = ROW, 
  STATISTICS_INCREMENTAL = ON)

PRINT 'Stats are back'
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

PRINT 'Works correctly for an offline rebuild by partition'
ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = OFF , DATA_COMPRESSION = ROW)

    --stats still there  
DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);

ALTER INDEX [IDX_ProductId] ON [dbo].[TransactionHistory] REBUILD 
  PARTITION = 9 WITH (ONLINE = ON , DATA_COMPRESSION = ROW)

DBCC SHOW_STATISTICS('dbo.TransactionHistory', IDX_ProductId);
PRINT' stats are gone!!!!!!'

ดังที่แสดงเราไม่สามารถสร้างดัชนีโดยพาร์ติชันออนไลน์ได้โดยไม่สูญเสียสถิติทั้งหมดสำหรับดัชนี นี่เป็นปัญหาการบำรุงรักษาที่สำคัญสำหรับเรา เกือบจะปรากฏว่าตัวเลือกการเพิ่มขึ้นของสถิติจำเป็นต้องเป็นส่วนหนึ่งของการสร้างดัชนีใหม่หรือตัวเลือกออนไลน์จำเป็นต้องจัดการอย่างถูกต้องเหมือนตัวเลือกออฟไลน์

โปรดแจ้งให้เราทราบหากฉันทำอะไรหาย

ปรับปรุง:

เท่าที่เราต้องการสถิติที่เพิ่มขึ้น: เรากำลังแบ่งพาร์ทิชันในรหัสลูกค้าภายในไม่ใช่วันที่ ดังนั้นเมื่อมีลูกค้าใหม่เข้ามา (โหลดข้อมูลจำนวนมาก) เราสามารถอัปเดตสถิติสำหรับพาร์ติชันและหลีกเลี่ยงแผนการสร้างที่น่าเกลียดสำหรับลูกค้าใหม่นี้ได้อย่างรวดเร็ว ฉันคิดว่าฉันจะยื่นเอกสารกับ Microsoft เป็นจุดบกพร่องและดูสิ่งที่พวกเขาต้องพูดและไปด้วยวิธีแก้ปัญหาเพียงแค่สุ่มตัวอย่างสถิติสำหรับพาร์ติชันนั้นอีกครั้ง

เชื่อมต่อรายงานข้อผิดพลาด:

สถิติหายไปหลังจากสร้างดัชนีออนไลน์ด้วยสถิติที่เพิ่มขึ้น

อัปเดต: Microsoft ยืนยันว่าเป็นข้อผิดพลาด


1
ปรับปรุง: ไมโครซอฟท์ส่งอีเมลฉันเช้าวันนี้ว่าปัญหานี้จะได้รับการแก้ไขในการปรับปรุงจุฬาฯ ต่อไปสำหรับ SQL 2014
JasonR

คุณรู้หรือไม่ว่า CU ใดที่แก้ไขหรือ KB ที่พวกเขารายงานในอีเมลนั้นคืออะไร พยายามที่จะดูเมื่อมันได้รับการแก้ไข
mbourgon

1
ค่อนข้างแน่ใจว่าเป็นหมายเลขข้อผิดพลาด VSTS 8046729 KB หมายเลขบทความ 3194959 ซึ่งเป็นส่วนหนึ่งของ CU 9 สำหรับ SQL Server 2014 SP1 เชื่อมโยงไปยัง KB คือที่นี่
JasonR

ใช่นั่นดูเหมือน - และเพิ่งได้รับการแก้ไขเมื่อ 2016SP1 เมื่อเดือนที่แล้ว ขอบคุณมาก!
mbourgon

การแก้ไข: เพิ่งแก้ไขใน 2016 SP1 CU2 มันเกิดขึ้นใน 2016 SP1 CU1
mbourgon

คำตอบ:


17

ถ้าไม่แน่ใจว่ามันเป็นข้อผิดพลาดต่อ seแต่ก็แน่นอนที่เกิดขึ้นที่น่าสนใจ การสร้างพาร์ติชันออนไลน์ใหม่เป็นสิ่งใหม่ใน SQL Server 2014 ดังนั้นอาจมี internals บางส่วนที่จะเรียงลำดับตามสิ่งนี้

นี่คือคำอธิบายที่ดีที่สุดของฉันสำหรับคุณ สถิติที่เพิ่มขึ้นจำเป็นต้องมีการสุ่มตัวอย่างพาร์ติชั่นทั้งหมดในอัตราเดียวกันดังนั้นเมื่อเอ็นจิ้นรวมหน้าสถิติมันสามารถมั่นใจได้ว่าการกระจายตัวอย่างนั้นเปรียบได้ REBUILDจำเป็นต้องใช้ข้อมูลตัวอย่างในอัตราตัวอย่าง 100% ไม่มีการรับประกันว่าอัตราตัวอย่าง 100% ในพาร์ติชัน 9 จะเป็นอัตราตัวอย่างที่แน่นอนของพาร์ติชั่นที่เหลืออยู่เสมอ ด้วยเหตุนี้มันจึงดูเหมือนว่าเอ็นจิ้นไม่สามารถรวมตัวอย่างได้และคุณจะจบลงด้วยสถิติหยดเปล่า อย่างไรก็ตามวัตถุสถิติยังคงอยู่ที่นั่น:

select 
    check_time = sysdatetime(),                         
    schema_name = sh.name,
    table_name = t.name,
    stat_name = s.name,
    index_name = i.name,
    stats_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
    s.stats_id,
    s.has_filter,                       
    s.is_incremental,
    s.auto_created,
    sp.last_updated,    
    sp.rows,
    sp.rows_sampled,                        
    sp.unfiltered_rows,
    modification_counter 
from sys.stats s 
join sys.tables t 
    on s.object_id = t.object_id
join sys.schemas sh
    on t.schema_id = sh.schema_id
left join sys.indexes i 
    on s.object_id = i.object_id
    and s.name = i.name
outer apply sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
where t.name = 'TransactionHistory' and sh.name = 'dbo'

คุณสามารถเติมหยดผ่านวิธีการใด ๆ :

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE;

หรือ

UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE ON PARTITIONS (9);

หรือคุณสามารถรอให้ AutoStats อัปเดตในการคอมไพล์ครั้งแรกของแผนแบบสอบถามโดยใช้วัตถุนั้น:

-- look at my creative query
select * 
from dbo.TransactionHistory
where TransactionDate = '20140101';

ต้องบอกว่าทั้งหมดนี้โพสต์ enlightening โดย Erin Stellatoเน้นสิ่งที่ได้รับการมองว่าเป็นข้อบกพร่องที่สำคัญของสถิติที่เพิ่มขึ้น ข้อมูลระดับพาร์ติชันไม่ได้ถูกใช้โดยเครื่องมือเพิ่มประสิทธิภาพในการสร้างแผนแบบสอบถามซึ่งช่วยลดผลประโยชน์ที่สันนิษฐานไว้จากสถิติส่วนเพิ่ม อะไรคือประโยชน์ปัจจุบันของสถิติที่เพิ่มขึ้น? ฉันขอเสนอว่ายูทิลิตี้หลักของพวกเขาคือความสามารถในการสุ่มตัวอย่างตารางขนาดใหญ่อย่างสม่ำเสมอในอัตราที่สูงกว่าด้วยสถิติแบบดั้งเดิม

โดยใช้ตัวอย่างของคุณนี่คือลักษณะที่ปรากฏ:

set statistics time on;

update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;

--SQL Server Execution Times:
--  CPU time = 94 ms,  elapsed time = 131 ms.


update statistics dbo.TransactionHistory(IDX_ProductId)
with resample on partitions(2);

 --SQL Server Execution Times:
 --  CPU time = 0 ms,  elapsed time = 5 ms.

drop index IDX_ProductId On dbo.TransactionHistory;

CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId) 
  WITH (DATA_COMPRESSION = ROW)  
  ON [PRIMARY]

update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;

 --SQL Server Execution Times:
 --  CPU time = 76 ms,  elapsed time = 66 ms.

สถิติการสแกนแบบเต็มเกี่ยวกับค่าใช้จ่ายทางสถิติที่เพิ่มขึ้น 131 มิลลิวินาที การอัพเดตสถิติ fullscan เกี่ยวกับสถิติที่ไม่จัดแนวพาร์ติชัน 66 ms สถิติที่ไม่จัดแนวช้ากว่ามากที่สุดเนื่องจากค่าใช้จ่ายที่เกิดขึ้นจากการรวมหน้าสถิติแต่ละหน้ากลับเข้าไปในฮิสโตแกรมหลัก. อย่างไรก็ตามการใช้ออบเจกต์สถิติที่จัดเรียงพาร์ติชันเราสามารถอัปเดตพาร์ติชันหนึ่งและรวมกลับเข้าไปในฮิสโทแกรมหลักใน 5 ms ดังนั้น ณ จุดนี้ผู้ดูแลระบบที่มีสถิติที่เพิ่มขึ้นจะต้องเผชิญกับการตัดสินใจ พวกเขาสามารถลดเวลาการบำรุงรักษาสถิติโดยรวมได้โดยการอัพเดตพาร์ติชันที่จำเป็นต้องได้รับการอัพเดตแบบดั้งเดิมเท่านั้นหรือพวกเขาสามารถทดสอบด้วยอัตราตัวอย่างที่สูงขึ้นเพื่อที่พวกเขาจะได้รับแถวตัวอย่างเพิ่มเติมในช่วงเวลาเดียวกัน อดีตอนุญาตให้ห้องหายใจในหน้าต่างการบำรุงรักษาหลังอาจดันสถิติในตารางที่มีขนาดใหญ่มากไปยังสถานที่ที่แบบสอบถามได้รับแผนการที่ดีขึ้นตามสถิติที่แม่นยำยิ่งขึ้น นี่ไม่ใช่การรับประกันและไมล์สะสมของคุณอาจแตกต่างกันไป

ผู้อ่านจะเห็นว่า 66 ms ไม่ใช่เวลาอัปเดตสถิติอันเจ็บปวดในตารางนี้ดังนั้นฉันจึงลองตั้งค่าการทดสอบกับชุดข้อมูล stackexchange มีโพสต์ 6,418,608 โพสต์ (ยกเว้นโพสต์ StackOverflow และโพสต์ทั้งหมดจาก 2012 - ข้อผิดพลาดข้อมูลในส่วนของฉัน) ในการถ่ายโอนข้อมูลล่าสุดที่ฉันดาวน์โหลด

ฉันแบ่งพาร์ติชันข้อมูลโดย[CreationDate]เพราะ ... สาธิต

ต่อไปนี้เป็นเวลาสำหรับสถานการณ์มาตรฐานที่สวยงาม (100% - การสร้างดัชนีใหม่ค่าเริ่มต้น - สถิติอัปเดตอัตโนมัติหรือUPDATE STATISTICSไม่มีอัตราตัวอย่างที่ระบุ:

  • สร้างสถิติที่ไม่เพิ่มขึ้นด้วย Fullscan: เวลา CPU = 23500 ms, เวลาที่ผ่านไป = 22521 ms
  • สร้างสถิติที่เพิ่มขึ้นด้วย Fullscan: เวลา CPU = 20406 ms, เวลาที่ผ่านไป = 15413 ms
  • อัปเดตสถิติที่ไม่เพิ่มขึ้นด้วยอัตราตัวอย่างเริ่มต้น: เวลา CPU = 406 ms, เวลาที่ผ่านไป = 408 ms
  • อัปเดตสถิติส่วนเพิ่มด้วยอัตราตัวอย่างเริ่มต้น: เวลา CPU = 453 ms, เวลาที่ผ่านไป = 507 ms

สมมติว่าเรามีความซับซ้อนมากกว่าสถานการณ์เริ่มต้นเหล่านี้และได้ตัดสินใจว่าอัตราตัวอย่าง 10% นั้นเป็นอัตราขั้นต่ำที่จะทำให้เราได้รับแผนการที่เราต้องการในขณะที่รักษาเวลาให้อยู่ในกรอบเวลาที่เหมาะสม

  • อัพเดตสถิติที่ไม่เพิ่มขึ้นด้วยตัวอย่าง 10 เปอร์เซ็นต์: เวลา CPU = 2344 ms, เวลาที่ผ่านไป = 2441 มิลลิวินาที
  • อัปเดตสถิติส่วนเพิ่มพร้อมตัวอย่าง 10 เปอร์เซ็นต์: เวลา CPU = 2344 ms, เวลาที่ผ่านไป = 2388 มิลลิวินาที

จนถึงตอนนี้ยังไม่มีประโยชน์ชัดเจนในการมีสถิติที่เพิ่มขึ้น อย่างไรก็ตามหากเราใช้ประโยชน์จากDMV ที่ไม่มีเอกสาร sys.dm_db_stats_properties_internal() (ด้านล่าง) คุณสามารถรับข้อมูลเชิงลึกเกี่ยวกับพาร์ติชันที่คุณอาจต้องการอัปเดต สมมติว่าเราทำการเปลี่ยนแปลงข้อมูลในพาร์ติชัน 3 และเราต้องการให้สถิติมีความสดใหม่สำหรับการค้นหาที่เข้ามา ตัวเลือกของเรามีดังนี้:

  • อัปเดตแบบไม่เพิ่มที่ค่าเริ่มต้น (รวมถึงพฤติกรรมเริ่มต้นของการอัปเดตสถิติอัตโนมัติ): 408 ms
  • อัปเดตไม่เพิ่มที่ 10%: 2441 ms
  • อัปเดตสถิติเพิ่มเติมส่วนที่ 3 ด้วย Resample (10% - อัตราตัวอย่างของเราที่กำหนด): เวลา CPU = 63 ms, เวลาที่ผ่านไป = 63 ms

ที่นี่เราจำเป็นต้องตัดสินใจ เราจะชนะด้วย 63 มิลลิวินาที การอัพเดทสถิติตามพาร์ติชั่นหรือเราชนอัตราตัวอย่างสูงกว่านี้ไหม? สมมติว่าเรายินดีที่จะเริ่มต้นการสุ่มตัวอย่างที่ 50% ตามสถิติที่เพิ่มขึ้น:

  • อัปเดตสถิติเพิ่มเติมแบบ 50%: เวลาที่ผ่านไป = 16840 มิลลิวินาที
  • อัปเดตสถิติเพิ่มเติมส่วนที่ 3 พร้อม Resample (50% - เวลาอัปเดตใหม่ของเรา): เวลาที่ผ่านไป = 295 ms

เราสามารถสุ่มตัวอย่างข้อมูลได้มากขึ้นอาจตั้งค่าเครื่องมือเพิ่มประสิทธิภาพเพื่อคาดเดาข้อมูลของเราได้ดีขึ้น (แม้ว่าจะยังไม่ได้ใช้สถิติระดับพาร์ติชัน แต่) และเราสามารถทำสิ่งนี้ได้เร็วกว่าที่เรามี สถิติที่เพิ่มขึ้น

สิ่งหนึ่งที่สนุกที่จะคิดออก การอัพเดตสถิติแบบซิงโครนัสคืออะไร? อัตราตัวอย่าง 50% ถูกเก็บรักษาไว้หรือไม่แม้ว่าจะมีการป้อนอัตโนมัติหรือไม่

ฉันลบข้อมูลจากพาร์ติชัน 3 และเรียกใช้คิวรีบน CreationDate และตรวจสอบแล้วตรวจสอบอัตราด้วยคิวรีเดียวกันด้านล่าง อัตราตัวอย่าง 50% ถูกรักษาไว้

เรื่องสั้นที่มีความยาว: สถิติที่เพิ่มขึ้นอาจเป็นเครื่องมือที่มีประโยชน์โดยมีปริมาณความคิดและการตั้งค่าเริ่มต้นที่เหมาะสม อย่างไรก็ตามคุณต้องทราบปัญหาที่คุณพยายามแก้ไขแล้วคุณต้องแก้ไขให้เหมาะสม หากคุณได้รับการประเมินความสำคัญเชิงหัวใจที่ไม่ดีคุณอาจจะได้รับแผนการที่ดีขึ้นด้วยอัตราตัวอย่างเชิงกลยุทธ์และการแทรกแซงการลงทุน อย่างไรก็ตามคุณจะได้รับผลประโยชน์เพียงเล็กน้อยเนื่องจากการใช้ฮิสโตแกรมนั้นเป็นหน้าสถิติที่รวมเข้าด้วยกันและไม่ใช่ข้อมูลระดับพาร์ติชัน หากคุณรู้สึกเจ็บปวดในหน้าต่างการบำรุงรักษาสถิติที่เพิ่มขึ้นอาจช่วยคุณได้ แต่มันอาจจะทำให้คุณต้องตั้งค่ากระบวนการแทรกแซงการบำรุงรักษาแบบสัมผัสสูง โดยไม่คำนึงถึง:

  • สถิติที่สร้างด้วยดัชนีที่ไม่ได้จัดพาร์ติชันไว้กับตารางฐาน
  • สถิติที่สร้างขึ้นบนฐานข้อมูลรองที่สามารถอ่านได้ AlwaysOn
  • สถิติที่สร้างบนฐานข้อมูลแบบอ่านอย่างเดียว
  • สถิติที่สร้างขึ้นบนดัชนีที่กรองแล้ว
  • สถิติที่สร้างขึ้นในมุมมอง
  • สถิติที่สร้างขึ้นในตารางภายใน
  • สถิติที่สร้างด้วยดัชนีเชิงพื้นที่หรือดัชนี XML

หวังว่านี่จะช่วยได้

select 
    sysdatetime(),                          
    schema_name = sh.name,
    table_name = t.name,
    stat_name = s.name,
    index_name = i.name,
    leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
    s.stats_id,
    parition_number = isnull(sp.partition_number,1),
    s.has_filter,                       
    s.is_incremental,
    s.auto_created,
    sp.last_updated,    
    sp.rows,
    sp.rows_sampled,                        
    sp.unfiltered_rows,
    modification_counter = coalesce(sp.modification_counter, n1.modification_counter) 
from sys.stats s 
join sys.tables t 
    on s.object_id = t.object_id
join sys.schemas sh
    on t.schema_id = sh.schema_id
left join sys.indexes i 
    on s.object_id = i.object_id
        and s.name = i.name
cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp
outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1
where n1.node_id = 1
    and (
            (is_incremental = 0)
               or
            (is_incremental = 1 and sp.partition_number is not null)
         )
    and t.name = 'Posts'
    and s.name like 'st_posts%'
order by s.stats_id,isnull(sp.partition_number,1)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.