ชะลอตัวลงอย่างมากในการสืบค้น SQL Server ในการเพิ่มไวด์การ์ด (หรือบน)


52

ฉันมีสวนสัตว์ 20 ล้านตัวที่ฉันติดตามบนฐานข้อมูล SQL Server 2005 ของฉัน ประมาณ 1% ของพวกเขาเป็นสีดำและประมาณ 1% ของพวกเขาเป็นหงส์ ฉันต้องการทราบรายละเอียดของหงส์ดำทั้งหมดและไม่ต้องการลบหน้าผลลัพธ์ที่ฉันทำ:

select top 10 * 
from animal 
where colour like 'black'  
and species like 'swan'

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

ปรากฎว่าหัวหน้าผู้ดูแลสวนคิดว่าเขาอาจเข้ามาในหงส์บางตัวว่า 'ดำ' ดังนั้นฉันจึงปรับเปลี่ยนการค้นหาตาม:

select top 10 * 
from animal  
where colour like 'black%' 
and species like 'swan'

กลับกลายเป็นว่าไม่มีสัตว์ตัวใด (และอันที่จริงไม่มีสัตว์ 'ดำ%' ยกเว้นสัตว์ 'ดำ') แต่แบบสอบถามตอนนี้ใช้เวลาประมาณ 30 วินาทีในการคืนค่าว่าง

ดูเหมือนว่าเป็นเพียงการรวมกันของ 'ด้านบน' และ 'ชอบ%' เท่านั้นที่ทำให้เกิดปัญหาได้

select count(*) 
from animal  
where colour like 'black%' 
and species like 'swan'

ผลตอบแทนที่ 0 อย่างรวดเร็วและแม้กระทั่ง

select * 
from animal 
where colour like 'black%' 
and species like 'swan'

ส่งคืนค่าว่างในเสี้ยววินาที

มีใครมีความคิดว่าทำไม 'ด้านบน' และ '%' ควรสมคบกันเพื่อทำให้เกิดการสูญเสียประสิทธิภาพอย่างมากโดยเฉพาะอย่างยิ่งในชุดผลลัพธ์ที่ว่างเปล่า?

แก้ไข: เพื่อชี้แจงฉันไม่ได้ใช้ดัชนี FreeText ใด ๆ ฉันแค่หมายถึงว่าเขตข้อมูลนั้นเป็น freetext ณ จุดที่เข้านั่นคือไม่ได้ทำให้เป็นมาตรฐานในฐานข้อมูล ขออภัยในความสับสนถ้อยคำที่ไม่ดีในส่วนของฉัน

คำตอบ:


76

นี่คือการตัดสินใจของเครื่องมือเพิ่มประสิทธิภาพตามต้นทุน

ค่าใช้จ่ายโดยประมาณที่ใช้ในตัวเลือกนี้ไม่ถูกต้องเนื่องจากจะถือว่ามีความเป็นอิสระทางสถิติระหว่างค่าในคอลัมน์ต่างๆ

มันคล้ายกับปัญหาที่อธิบายไว้ในRow Goals Gone Rogueซึ่งจำนวนคู่และคี่มีความสัมพันธ์เชิงลบ

มันง่ายที่จะทำซ้ำ

CREATE TABLE dbo.animal(
    id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
    colour varchar(50) NOT NULL,
    species varchar(50) NOT NULL,
    Filler char(10) NULL
);

/*Insert 20 million rows with 1% black and 1% swan but no black swans*/
WITH T
     AS (SELECT TOP 20000000 ROW_NUMBER() OVER (ORDER BY @@SPID) AS RN
         FROM   master..spt_values v1,
                master..spt_values v2,
                master..spt_values v3)
INSERT INTO dbo.animal
            (colour,
             species)
SELECT CASE
         WHEN RN % 100 = 1 THEN 'black'
         ELSE CAST(RN % 100 AS VARCHAR(3))
       END,
       CASE
         WHEN RN % 100 = 2 THEN 'swan'
         ELSE CAST(RN % 100 AS VARCHAR(3))
       END
FROM   T 

/*Create some indexes*/
CREATE NONCLUSTERED INDEX ix_species ON  dbo.animal(species);
CREATE NONCLUSTERED INDEX ix_colour ON  dbo.animal(colour);

ตอนนี้ลอง

SELECT TOP 10 *
FROM   animal
WHERE  colour LIKE 'black'
       AND species LIKE 'swan' 

นี้จะช่วยให้แผนด้านล่างซึ่งเป็น costed 0.0563167ที่

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

แผนสามารถทำการรวมการผสานระหว่างผลลัพธ์ของดัชนีสองรายการในidคอลัมน์ ( รายละเอียดเพิ่มเติมของการรวมการรวมอัลกอริทึมที่นี่ )

ผสานการเข้าร่วมต้องใช้ทั้งสองอินพุทโดยคีย์การเข้าร่วม

ดัชนีที่ไม่เป็นคลัสเตอร์ถูกจัดเรียงโดย(species, id)และ(colour, id)ตามลำดับ (ดัชนีที่ไม่ใช่คลัสเตอร์ที่ไม่ซ้ำกันจะมีตัวระบุแถวที่เพิ่มเข้าไปในส่วนท้ายของคีย์โดยนัยถ้าไม่ได้เพิ่มอย่างชัดเจน) แบบสอบถามโดยไม่สัญลักษณ์ใด ๆ ที่มีประสิทธิภาพเท่าเทียมกันขอเข้าและspecies = 'swan' colour ='black'เนื่องจากการค้นหาแต่ละครั้งจะดึงค่าที่แน่นอนจากคอลัมน์นำเท่านั้นแถวที่ตรงกันจะถูกเรียงลำดับตามidดังนั้นแผนนี้จึงเป็นไปได้

ผู้ประกอบการแผนแบบสอบถามรันจากซ้ายไปขวา ด้วยโอเปอเรเตอร์ด้านซ้ายที่ร้องขอแถวจากลูกของมันซึ่งจะขอแถวจากลูก ๆของพวกเขา (และต่อไปเรื่อย ๆ จนกว่าจะถึงโหนดใบไม้) ตัวTOPวนซ้ำจะหยุดร้องขอแถวเพิ่มเติมจากลูกของมันเมื่อได้รับ 10 แล้ว

SQL Server มีสถิติของดัชนีที่บอกว่า 1% ของแถวตรงกับแต่ละภาคแสดง มันถือว่าสถิติเหล่านี้เป็นอิสระ (เช่นไม่มีความสัมพันธ์เชิงบวกหรือเชิงลบ) ดังนั้นโดยเฉลี่ยเมื่อมีการประมวลผล 1,000 แถวที่ตรงกับภาคแรกจะพบ 10 การจับคู่ที่สองและสามารถออก (แผนข้างต้นแสดงให้เห็นถึง 987 มากกว่า 1,000 แต่ใกล้พอ)

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

เปรียบเทียบกับ

SELECT TOP 10 *
FROM   animal
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan' 

ซึ่งให้แผนด้านล่างซึ่งคิดต้นทุนอยู่ที่ 0.567943

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

การเพิ่ม wildcard ต่อท้ายทำให้เกิดการสแกนดัชนี ค่าใช้จ่ายของแผนยังค่อนข้างต่ำแม้ว่าจะสแกนบนตาราง 20 ล้านแถว

การเพิ่มquerytraceon 9130แสดงข้อมูลเพิ่มเติมบางอย่าง

SELECT TOP 10 *
FROM   animal
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan'       
OPTION (QUERYTRACEON 9130)  

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

จะเห็นได้ว่า SQL Server คิดว่ามันจะต้องสแกนประมาณ 100,000 แถวเท่านั้นก่อนที่มันจะพบ 10 เพรดิเคตที่ตรงกันและTOPสามารถหยุดการร้องขอแถวได้

อีกครั้งนี้เหมาะสมกับการสันนิษฐานอิสรภาพ 10 * 100 * 100 = 100,000

ในที่สุดให้ลองและบังคับแผนแยกดัชนี

SELECT TOP 10 *
FROM   animal WITH (INDEX(ix_species), INDEX(ix_colour))
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan' 

นี่เป็นแผนคู่ขนานสำหรับฉันด้วยค่าใช้จ่ายประมาณ 3.4625

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

ความแตกต่างหลักที่นี่คือเพรดิเคตcolour like 'black%'สามารถจับคู่หลายสีที่แตกต่างกัน idซึ่งหมายความว่าแถวดัชนีตรงกันสำหรับกริยาที่ไม่ได้รับประกันว่าจะถูกจัดเรียงในคำสั่งของ

ตัวอย่างเช่นการค้นหาดัชนีlike 'black%'อาจส่งคืนแถวต่อไปนี้

+------------+----+
|   Colour   | id |
+------------+----+
| black      | 12 |
| black      | 20 |
| black      | 23 |
| black      | 25 |
| blackberry |  1 |
| blackberry | 50 |
+------------+----+

ภายในแต่ละสีรหัสจะถูกเรียงลำดับ แต่รหัสในสีที่ต่างกันอาจไม่เป็นเช่นนั้น

เป็นผลให้ SQL Server ไม่สามารถทำการแยกการรวมดัชนีการผสาน (โดยไม่ต้องเพิ่มตัวดำเนินการเรียงลำดับการบล็อค) และจะทำการแฮชเข้าร่วมแทน Hash Join กำลังปิดกั้นอินพุตบิลด์ดังนั้นตอนนี้ต้นทุนจะสะท้อนความจริงที่ว่าแถวที่ตรงกันทั้งหมดจะต้องถูกประมวลผลจากบิลด์บิลด์แทนที่จะคิดว่ามันจะต้องสแกน 1,000 เท่าในแผนแรก

อินพุตโพรบไม่ใช่การปิดกั้นและยังคงประเมินอย่างไม่ถูกต้องว่าจะสามารถหยุดการโพรบหลังจากประมวลผลแถว 987 จากนั้น

(ข้อมูลเพิ่มเติมเกี่ยวกับการไม่ปิดกั้นและการปิดกั้นตัวทำเครื่องหมายที่นี่)

เนื่องจากค่าใช้จ่ายที่เพิ่มขึ้นของแถวที่ประมาณพิเศษและการแฮชเข้าร่วมการสแกนดัชนีแบบคลัสเตอร์บางส่วนนั้นดูถูกกว่า

ในทางปฏิบัติแล้วแน่นอนว่าการสแกนดัชนีแบบกลุ่มบางส่วนนั้นไม่ได้เป็นบางส่วนและจำเป็นต้องใช้เวลาในการตรวจสอบทั้ง 20 ล้านแถวแทนที่จะเป็น 100,000 เมื่อเปรียบเทียบกับแผน

การเพิ่มมูลค่าของTOP(หรือลบออกทั้งหมด) ในที่สุดก็พบจุดเปลี่ยนที่จำนวนแถวที่ประเมินการสแกน CI จะต้องครอบคลุมทำให้แผนนั้นดูมีราคาแพงกว่าและเปลี่ยนเป็นแผนตัดกันดัชนี สำหรับผมจุดตัดระหว่างสองแผนคือVSTOP (89)TOP (90)

สำหรับคุณอาจแตกต่างกันอย่างมากเนื่องจากขึ้นอยู่กับความกว้างของดัชนีคลัสเตอร์

การลบTOPและบังคับให้สแกน CI

SELECT *
FROM   animal WITH (INDEX = 1)
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan' 

มีค่าใช้จ่ายที่88.0586เครื่องของฉันสำหรับตารางตัวอย่างของฉัน

ถ้า SQL Server ทราบว่าสวนสัตว์ไม่มีหงส์ดำและต้องทำการสแกนแบบเต็มแทนที่จะอ่านเพียง 100,000 แถวแผนนี้จะไม่ถูกเลือก

ฉันได้ลองสถิติหลายคอลัมน์ในanimal(species,colour)และanimal(colour,species)และกรองสถิติในanimal (colour) where species = 'swan'แต่ไม่มีความช่วยเหลือเหล่านี้ทำให้มั่นใจได้ว่าหงส์ดำไม่มีอยู่และการTOP 10สแกนจะต้องดำเนินการมากกว่า 100,000 แถว

นี่คือสาเหตุที่ "สมมติฐานการรวม" ที่ SQL Server เป็นหลักถือว่าถ้าคุณกำลังค้นหาสิ่งที่มันอาจมีอยู่

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

SELECT TOP 10 *
FROM   animal
WHERE  colour LIKE 'black%'
       AND species LIKE 'swan'
OPTION (QUERYTRACEON 4138)       

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

แผนนี้มีต้นทุนอย่างถูกต้องสำหรับการอ่านเต็ม 200,000 แถวในดัชนีทั้งสองค้นหา แต่ค่าใช้จ่ายการค้นหาคีย์มากกว่า (ประมาณ 2 พันเทียบกับค่าจริง 0 TOP 10จะ จำกัด สิ่งนี้ให้สูงสุด 10 แต่ค่าสถานะการติดตามป้องกันไม่ให้ถูกนำมาพิจารณา) . แผนยังคงมีราคาถูกกว่าการสแกน CI เต็มรูปแบบดังนั้นจึงถูกเลือก

แน่นอนแผนนี้อาจไม่เหมาะสำหรับชุดค่าผสมที่เป็นเรื่องธรรมดา เช่นหงส์ขาว

ดัชนีคอมโพสิตในanimal (colour, species)หรือanimal (species, colour)จะช่วยให้แบบสอบถามมีประสิทธิภาพมากขึ้นสำหรับทั้งสองสถานการณ์

ที่จะทำให้การใช้งานมีประสิทธิภาพมากที่สุดของดัชนีคอมโพสิตยังจะต้องมีการเปลี่ยนแปลงไปLIKE 'swan'= 'swan'

ตารางด้านล่างแสดงเพรดิเคตค้นหาและเพรดิเคตที่เหลือที่แสดงในแผนการดำเนินการสำหรับการเปลี่ยนลำดับทั้งสี่

+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
|                 WHERE clause                 |       Index       |                         Seek Predicate                         |              Residual Predicate              |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+
| colour LIKE 'black%' AND species LIKE 'swan' | ix_colour_species | colour >= 'black' AND colour < 'blacL'                         | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species LIKE 'swan' | ix_species_colour | species >= 'swan' AND species <= 'swan'                        | colour like 'black%' AND species like 'swan' |
| colour LIKE 'black%' AND species = 'swan'    | ix_colour_species | (colour,species) >= ('black', 'swan')) AND colour < 'blacL'    | colour LIKE 'black%' AND species = 'swan'    |
| colour LIKE 'black%' AND species = 'swan'    | ix_species_colour | species = 'swan' AND (colour >= 'black' and colour <  'blacL') | colour like 'black%'                         |
+----------------------------------------------+-------------------+----------------------------------------------------------------+----------------------------------------------+

15

จากการค้นพบสิ่งที่น่าสนใจนี้ฉันได้ทำการค้นหาและสะดุดกับ Q / A นี้ (และทำไม) TOP ส่งผลกระทบต่อแผนการดำเนินการอย่างไร

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

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

ตัวอย่างเช่นฉันลองบนฐานข้อมูลและ: - เมื่อไม่มีการเรียกใช้บนจะใช้การขนานกัน - กับ TOP การขนานไม่ได้ใช้

ดังนั้นอีกครั้งการแสดงแผนการดำเนินการของคุณจะให้ข้อมูลเพิ่มเติม

ขอให้มีความสุขมาก ๆ ในวันนี้นะ


-1

ฉันเชื่อว่านี่อาจเป็นเพราะพื้นฐานของ MSSQL 2005 และวิธีที่เครื่องมือเพิ่มประสิทธิภาพการสืบค้นตัดสินใจว่าแผนการดำเนินการใดมีประสิทธิภาพมากที่สุด

หากคุณใช้ตัวแปร SQL คุณควร 'หลอก' ตัวเพิ่มประสิทธิภาพคิวรีเพื่อใช้การจับคู่แฮชแทนการวนซ้ำซ้อนกันซึ่งจะส่งผลให้เกิดการขนานที่สูงขึ้นมาก

ลอง:

DECLARE @topn INT = 10
SELECT TOP (@topn) *
FROM    animal
WHERE   colour LIKE 'black%' 
AND species LIKE 'swan'

5
obfuscating TOPค่าในตัวแปรหมายความว่ามันจะถือว่ามากกว่าTOP 100 TOP 10สิ่งนี้อาจช่วยได้หรือไม่ขึ้นอยู่กับว่าจุดเปลี่ยนระหว่างสองแผนคืออะไร
Martin Smith
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.