จะปรับปรุงการประมาณแถวได้อย่างไรเพื่อลดโอกาสของการหกถึง tempdb


11

ฉันสังเกตเห็นว่าเมื่อมีการรั่วไหลของเหตุการณ์ tempdb (ทำให้เกิดการสืบค้นที่ช้า) ซึ่งบ่อยครั้งที่การประมาณแถวนั้นเป็นวิธีการปิดการเข้าร่วมแบบเฉพาะ ฉันเคยเห็นเหตุการณ์การรั่วไหลเกิดขึ้นจากการรวมและแฮชรวมและพวกเขามักจะเพิ่มรันไทม์ 3x เป็น 10x คำถามนี้เกี่ยวข้องกับวิธีปรับปรุงการประมาณการแถวภายใต้สมมติฐานว่าจะลดโอกาสการเกิดเหตุการณ์หก

จำนวนแถวจริง 40k

สำหรับแบบสอบถามนี้แผนจะแสดงค่าประมาณแถวที่ไม่ดี (11.3 แถว):

select Value
  from Oav.ValueArray
 where ObjectId = (select convert(bigint, Value) NodeId
                     from Oav.ValueArray
                    where PropertyId = 3331  
                      and ObjectId = 3540233
                      and Sequence = 2)
   and PropertyId = 2840
option (recompile);

สำหรับแบบสอบถามนี้แผนแสดงการประมาณแถวที่ดี (56k แถว):

declare @a bigint = (select convert(bigint, Value) NodeId
                       from Oav.ValueArray
                      where PropertyId = 3331
                        and ObjectId = 3540233
                        and Sequence = 2);

select Value
  from Oav.ValueArray
 where ObjectId = @a               
   and PropertyId = 2840
option (recompile);

สามารถเพิ่มสถิติหรือคำแนะนำเพื่อปรับปรุงการประมาณแถวสำหรับกรณีแรกได้หรือไม่ ฉันพยายามเพิ่มสถิติด้วยค่าตัวกรองเฉพาะ (คุณสมบัติ = 2840) แต่อาจไม่ได้รับชุดค่าผสมที่ถูกต้องหรืออาจจะถูกละเว้นเพราะ ObjectId ไม่เป็นที่รู้จักในเวลารวบรวมและอาจเลือกค่าเฉลี่ยมากกว่า ObjectIds ทั้งหมด

มีโหมดใดบ้างที่จะทำแบบสอบถามโพรบก่อนจากนั้นใช้โหมดนั้นเพื่อกำหนดการประมาณแถวหรือต้องบินสุ่มสี่สุ่มห้า?

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

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

แก้ไข 2013/11/05 : การตอบสนองต่อความคิดเห็นและข้อมูลเพิ่มเติม:

นี่คือภาพแผนแบบสอบถาม คำเตือนเกี่ยวกับ cardinality / ค้นหาภาคที่มีการแปลง ():

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

ต่อความคิดเห็นของ @Aaron Bertrand ฉันลองแทนที่การแปลง () เป็นแบบทดสอบ:

create table Oav.SeekObject (
       LookupId bigint not null primary key,
       ObjectId bigint not null
);

insert into Oav.SeekObject (
   LookupId, ObjectId
) VALUES (
   1, 3540233
) 

select Value
  from Oav.ValueArray
 where ObjectId = (select ObjectId 
                     from Oav.SeekObject 
                    where LookupId = 1)
   and PropertyId = 2840
option (recompile);

ป้อนคำอธิบายรูปภาพที่นี่

ในฐานะที่เป็นจุดสนใจที่แปลก แต่ประสบความสำเร็จก็อนุญาตให้มันทำการลัดวงจรการค้นหา:

select Value
  from Oav.ValueArray
 where ObjectId = (select ObjectId 
                     from Oav.ValueArray
                    where PropertyId = 2840
                      and ObjectId = 3540233
                      and Sequence = 2)
   and PropertyId = 2840
option (recompile);

ป้อนคำอธิบายรูปภาพที่นี่

ทั้งสองรายการมีการค้นหาคีย์ที่เหมาะสม แต่มีเพียงรายการแรกเท่านั้นที่แสดงรายการ "เอาต์พุต" ของ ObjectId ฉันเดาว่าสิ่งที่สองคือการลัดวงจรจริง ๆ ?

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

หมายเหตุด้านข้างเนื่องจาก sql_variant จัดเก็บประเภทฐาน (SQL_VARIANT_PROPERTY = BaseType) ภายในเขตข้อมูลตัวเองฉันคาดว่าการแปลง () จะเกือบจะไร้ค่าตราบใดที่มันแปลงได้ "โดยตรง" (เช่นไม่ใช่สตริงถึงทศนิยม หรืออาจเป็น int ถึง bigint) เนื่องจากไม่เป็นที่รู้จักในเวลารวบรวม แต่อาจเป็นที่รู้จักของผู้ใช้บางทีฟังก์ชัน "AssumeType (type, ... )" สำหรับ sql_variants จะช่วยให้พวกเขาได้รับการปฏิบัติที่โปร่งใสมากขึ้น


1
สิ่งแรกที่เดาได้คือการแปลงเป็นเรื่องใหญ่กำลังทำให้การประมาณการของคุณปิด (แผนแบบสอบถามจะมีคำเตือนเกี่ยวกับเรื่องนั้นใน SQL Server 2012) แต่ในทางกลับกันแบบสอบถามย่อยของคุณไม่สามารถส่งคืนสิ่งอื่นใดนอกเหนือจาก 0 หรือ 1 แถว สำหรับการค้นหาที่ประสบความสำเร็จ มันน่าสนใจที่จะเห็นแผนการสืบค้นที่คุณมี อาจเป็นลิงก์ไปยังเวอร์ชัน XML
Mikael Eriksson

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

2
SQL Server รุ่นใด คุณสามารถระบุ DDL ของตารางและดัชนีและสถิติ blobs (คอลัมน์เดียวและหลายคอลัมน์) สำหรับตารางเพื่อให้เราสามารถดูรายละเอียดปัญหาได้หรือไม่ การแบ่งคำถามโดยใช้declare @a bigint = อย่างที่คุณทำดูเหมือนเป็นวิธีแก้ปัญหาที่เป็นธรรมชาติสำหรับฉันทำไมจึงไม่ยอมรับ?
พอลไวท์ 9

2
ฉันเดาว่าการออกแบบของคุณคือการออกแบบ EAV (ง่ายมาก) ซึ่งบังคับให้คุณใช้CONVERT()ในคอลัมน์แล้วเข้าร่วม นี่คือกรณีส่วนใหญ่ไม่ได้มีประสิทธิภาพอย่างแน่นอน ในค่าเฉพาะนี้มีเพียงค่าเดียวที่จะถูกแปลงค่าซึ่งอาจไม่ใช่ปัญหา แต่ดัชนีใดที่คุณมีอยู่ในตาราง การออกแบบ EAV มักจะทำงานได้ดีโดยมีการจัดทำดัชนีที่เหมาะสมเท่านั้น (ซึ่งหมายถึงดัชนีจำนวนมากในตารางแคบ ๆ )
ypercubeᵀᴹ

@ พอลไวท์เท่าที่แยก ... มันเป็นเรื่องดีสำหรับกรณีนี้ แต่สำหรับทั่วไป / ซับซ้อนมากขึ้นฉันส่วนใหญ่ไม่ต้องการให้ขนานและการอ่าน สมมติว่าฉันมี 10 คำเหล่านี้เป็นคิวรีย่อย (บางอันมีความซับซ้อนมากขึ้น) ในคิวรี แต่มีเพียง 5 ข้อเท่านั้นที่จะต้อง "สุกงอม" ก่อนที่เคียวรีที่เหลือจะเริ่มต้นได้ - ต้องการหลีกเลี่ยงการคิดว่า 5 อันไหน
crokusek

คำตอบ:


7

ฉันจะไม่แสดงความคิดเห็นเกี่ยวกับการรั่วไหล, tempdb หรือคำแนะนำเพราะแบบสอบถามดูเหมือนง่ายที่จะต้องพิจารณามาก ฉันคิดว่าเครื่องมือเพิ่มประสิทธิภาพของ SQL-Server จะทำงานได้ค่อนข้างดีหากมีดัชนีที่เหมาะสมสำหรับการสืบค้น

และการแยกออกเป็นสองข้อความค้นหานั้นดีเพราะมันแสดงให้เห็นว่าดัชนีใดที่จะมีประโยชน์ ส่วนแรก:

(select convert(bigint, Value) NodeId
 from Oav.ValueArray
 where PropertyId = 3331  
   and ObjectId = 3540233
   and Sequence = 2)

ต้องการดัชนีบนรวมทั้ง(PropertyId, ObjectId, Sequence) Valueฉันจะทำให้มันUNIQUEปลอดภัย แบบสอบถามจะแสดงข้อผิดพลาดต่อไปในระหว่างรันไทม์หากมีการส่งคืนมากกว่าหนึ่งแถวดังนั้นจึงเป็นการดีที่จะมั่นใจได้ว่าจะไม่เกิดขึ้นล่วงหน้าด้วยดัชนีที่ไม่ซ้ำกัน:

CREATE UNIQUE INDEX
    PropertyId_ObjectId_Sequence_UQ
  ON Oav.ValueArray
    (PropertyId, ObjectId, Sequence) INCLUDE (Value) ;

ส่วนที่สองของแบบสอบถาม:

select Value
  from Oav.ValueArray
 where ObjectId = @a               
   and PropertyId = 2840

ต้องการดัชนีในการ(PropertyId, ObjectId)รวมถึงValue:

CREATE INDEX
    PropertyId_ObjectId_IX
  ON Oav.ValueArray
    (PropertyId, ObjectId) INCLUDE (Value) ;

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

ในกรณีนั้นการแปลง (ต้องการจากการออกแบบ EAV และการจัดเก็บประเภทข้อมูลที่แตกต่างกันในคอลัมน์เดียวกัน) เป็นสาเหตุที่เป็นไปได้และวิธีการแยก (ในขณะที่ @ AAron Bertrand และ @Paul White แสดงความคิดเห็น) และวิธีที่จะไป การออกแบบใหม่เพื่อให้มีประเภทข้อมูลที่แตกต่างกันในคอลัมน์ที่เกี่ยวข้องอาจเป็นอีกประเภทหนึ่ง


ตารางมีดัชนีครอบคลุม - ฉันควรระบุไว้ในคำถาม ตัวอย่างคือการเข้าร่วมย่อยโดยป้อนคิวรีที่มีขนาดใหญ่ขึ้นซึ่งเป็นสาเหตุที่ทำให้เกิดความวุ่นวายเกี่ยวกับ tempdb ทั้งหมด
crokusek

5

เป็นคำตอบบางส่วนสำหรับคำถามที่ชัดเจนเกี่ยวกับการปรับปรุงสถิติ ...

โปรดทราบว่าแถวประมาณการแม้กระทั่งในกรณีที่แยกจากกันจะยังคงปิด 10X (4k เทียบกับที่คาดไว้ 40k)

สถิติฮิสโตแกรมมีแนวโน้มแพร่กระจายบางเกินไปสำหรับคุณสมบัตินั้นเนื่องจากเป็นตารางยาว (แนวตั้ง), แถว 3.5M และคุณสมบัติเฉพาะนั้นกระจัดกระจายมาก

สร้างสถิติเพิ่มเติม (ค่อนข้างซ้ำซ้อนกับสถิติ IX) สำหรับคุณสมบัติแบบกระจาย:

create statistics [STAT_ValueArray_ObjPropValue_WhereProp2840] ON [Oav].[ValueArray](ObjectId, PropertyId, Value)
where PropertyId = 2840

ของเดิม:

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

ด้วยการแปลง () ลบออก (เหมาะสม):

ป้อนคำอธิบายรูปภาพที่นี่

ด้วยการแปลง () ถูกลบ (short-ciruit):

ป้อนคำอธิบายรูปภาพที่นี่

ยังคงปิดอยู่ที่ ~ 2X เนื่องจาก> 99.9% ของวัตถุไม่ได้มีการกำหนดคุณสมบัติ 2840 ไว้เลย ในความเป็นจริงสำหรับกรณีทดสอบนี้คุณสมบัติมีอยู่บน 1 ใน 200k Objects ที่แตกต่างกันของตารางแถว 3.5M เท่านั้น มันน่าทึ่งที่มันเข้าใกล้จริงๆ การปรับตัวกรองให้เป็น ObjectIds ที่น้อยลง

create statistics [STAT_ValueArray_ObjPropValue_WhereProp2840] ON [Oav].[ValueArray](ObjectId, PropertyId, Value)
where PropertyId = 2840 and ObjectId >= 3540000 and ObjectId < 3541000

create statistics [STAT_ValueArray_ObjPropValue_WhereProp2840] ON [Oav].[ValueArray](ObjectId, PropertyId, Value)
where PropertyId = 2840 and ObjectId = 3540233;

อืมไม่มีการเปลี่ยนแปลง ... สนับสนุนที่เพิ่ม "พร้อมการสแกนแบบเต็ม" ถึงจุดสิ้นสุดของสถิติ (อาจเป็นสาเหตุที่สองรายการก่อนหน้าไม่ทำงาน) และใช่:

create statistics [STAT_ValueArray_ObjPropValue_WhereProp2840] ON [Oav].[ValueArray](ObjectId, PropertyId, Value)
where PropertyId = 2840 with full scan;

ป้อนคำอธิบายรูปภาพที่นี่

เย้. ดังนั้นในตารางแนวตั้งสูงที่มี IX ครอบคลุมอย่างกว้าง ๆ การเพิ่มสถิติที่กรองเพิ่มเติมดูเหมือนจะเป็นการปรับปรุงที่ดีมาก


ลิงก์ไปยังปัญหาที่มีปัญหาเกี่ยวกับสถิติหลายคอลัมน์
crokusek
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.