เป็นไปได้ไหมที่จะเพิ่มประสิทธิภาพการสืบค้นในตารางแคบ ๆ ที่มีแถวนับล้านแถว


14

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

คำค้นหา

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31'; 

โต๊ะ

CREATE TABLE [dbo].[Heartbeats](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [DeviceID] [int] NOT NULL,
    [IsPUp] [bit] NOT NULL,
    [IsWebUp] [bit] NOT NULL,
    [IsPingUp] [bit] NOT NULL,
    [DateEntered] [datetime] NOT NULL,
 CONSTRAINT [PK_Heartbeats] 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 [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)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]

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

UPDATE

เมื่อฉันเปลี่ยนแบบสอบถามเพื่อใช้คำใบ้ดัชนีบังคับแบบสอบถามดำเนินการใน 50ms:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats] WITH(INDEX(CommonQueryIndex))
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' 

การเพิ่ม DeviceID clause ที่เลือกอย่างถูกต้องยังส่งผลถึงช่วง 50ms:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' AND DeviceID = 4;

ถ้าฉันเพิ่มลงORDER BY [DateEntered], [DeviceID]ในคิวรีดั้งเดิมฉันอยู่ในช่วง 50ms:

SELECT TOP 1000 * FROM [CIA_WIZ].[dbo].[Heartbeats]
WHERE [DateEntered] BETWEEN '2011-08-30' and '2011-08-31' 
ORDER BY [DateEntered], [DeviceID];

สิ่งเหล่านี้ใช้ดัชนีที่ฉันคาดหวัง (CommonQueryIndex) ดังนั้นฉันคิดว่าคำถามของฉันคือตอนนี้มีวิธีบังคับดัชนีนี้ให้ใช้กับแบบสอบถามแบบนี้หรือไม่? หรือขนาดของตารางของฉันลดลงจากเครื่องมือเพิ่มประสิทธิภาพมากเกินไปและฉันต้องใช้ORDER BYหรือคำใบ้?


ฉันเดาว่าคุณสามารถเพิ่มดัชนีที่ไม่ใช่คลัสเตอร์ได้อีกหนึ่งรายการใน "DateEntered" ซึ่งจะเพิ่มประสิทธิภาพในระดับที่สูงขึ้น
Praveen

@Praveen โดยทั่วไปมันจะเหมือนกับดัชนีที่มีอยู่ของฉันหรือไม่ ฉันต้องทำอะไรเป็นพิเศษเพราะจะมีสองดัชนีในเขตข้อมูลเดียวกันหรือไม่?
เนท

@ เนทเนื่องจากตารางที่เรียกว่าการเต้นของหัวใจและมี 44million ระเบียนที่เกี่ยวข้องฉันคิดว่าคุณมีการแทรกอย่างหนักในตารางนี้? ด้วยการจัดทำดัชนีคุณสามารถเพิ่มดัชนีครอบคลุมเพื่อเพิ่มความเร็ว แต่ตามที่คุณกล่าวถึงคุณใช้แบบสอบถามนี้เป็นครั้งคราวเท่านั้นฉันขอแนะนำอย่างยิ่งว่าหากคุณใส่เม็ดมีดมาก มันเป็นสองเท่าโดยทั่วไปโหลดแทรกของคุณ คุณกำลังใช้งาน Enterprise Edition อยู่หรือไม่
Edward Dortland

ฉันสังเกตเห็นว่าคุณมี deviceID ในดัชนี NC ของคุณ เป็นไปได้หรือไม่ที่จะรวมไว้ในส่วนคำสั่งของคุณ? และนั่นจะทำให้ผลลัพธ์ที่ตั้งไว้ต่ำกว่าเกณฑ์หรือไม่ <ระเบียน 35k (ไม่มีข้อความสูงสุด 1,000 ข้อ)
Edward Dortland

1
คำถามที่ผ่านมาคุณมักจะใส่เรียงตามวันที่ลงไป? หรือสิ่งเหล่านี้อาจไม่เรียบร้อยเนื่องจากอุปกรณ์อาจแทรก async จากอุปกรณ์อื่น คุณอาจลองเปลี่ยนดัชนีคลัสเตอร์เป็นคอลัมน์ DateEntered หน้าลาของคุณของดัชนีคลัสเตอร์ตอนนี้ 445 หน้า นั่นจะเพิ่มเป็นสองเท่าหากคุณเปลี่ยนจาก int เป็น datetime แต่ในกรณีนี้อาจไม่เลว
Edward Dortland

คำตอบ:


13

เหตุใดเครื่องมือเพิ่มประสิทธิภาพไม่เหมาะกับดัชนีแรกของคุณ:

CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)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]

เป็นเรื่องของการเลือกของคอลัมน์ [DateEntered]

คุณบอกเราว่าตารางของคุณมี 44 ล้านแถว ขนาดแถวคือ:

4 ไบต์สำหรับ ID, 4 ไบต์สำหรับ ID อุปกรณ์, 8 ไบต์สำหรับวันที่และ 1 ไบต์สำหรับคอลัมน์ 4 บิต นั่นคือ 17 ไบต์ + 7 ไบต์ค่าใช้จ่ายสำหรับ (แท็ก, Null bitmap, var col offset, จำนวน col) รวม 24 ไบต์ต่อแถว

ที่จะแปลหน้า 140k เพื่อจัดเก็บ 44 ล้านแถวเหล่านั้น

ตอนนี้เครื่องมือเพิ่มประสิทธิภาพสามารถทำสองสิ่ง:

  1. มันสามารถสแกนตาราง (การสแกนดัชนีคลัสเตอร์)
  2. หรืออาจใช้ดัชนีของคุณ สำหรับทุกแถวในดัชนีของคุณจากนั้นจะต้องทำการค้นหาบุ๊กมาร์กในดัชนีคลัสเตอร์

ตอนนี้ถึงจุดหนึ่งมันจะมีราคาแพงกว่าที่จะทำการค้นหาเดี่ยวเหล่านี้ทั้งหมดในดัชนีคลัสเตอร์สำหรับทุกรายการดัชนีที่พบในดัชนีที่ไม่ใช่คลัสเตอร์ของคุณ เกณฑ์โดยทั่วไปคือจำนวนการค้นหาทั้งหมดควรเกิน 25% tot 33% ของจำนวนหน้าตารางทั้งหมด

ดังนั้นในกรณีนี้: 140k / 25% = 35000 แถว 140k / 33% = 46666 แถว

(@RBarryYoung, 35k คือ 0.08% ของแถวทั้งหมดและ 46666 คือ 0.10% ดังนั้นฉันคิดว่านั่นเป็นที่มาของความสับสน)

ดังนั้นหากส่วนคำสั่งของคุณจะส่งผลให้เกิดแถวระหว่าง 35000 ถึง 46666 (ซึ่งอยู่ใต้ส่วนคำสั่งด้านบน!) มีโอกาสมากที่จะไม่ใช้คลัสเตอร์ที่ไม่ใช่คลัสเตอร์ของคุณและจะใช้การสแกนดัชนีแบบกลุ่ม

สองวิธีในการเปลี่ยนแปลงเท่านั้น:

  1. ทำให้ข้อที่เลือกของคุณมากขึ้น (ถ้าเป็นไปได้)
  2. ดร็อป * และเลือกเพียงไม่กี่คอลัมน์เพื่อให้คุณสามารถใช้ดัชนีครอบคลุม

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

การเปลี่ยนจาก datetime เป็น smalldatetime เป็นการลดขนาดลง 16% ในดัชนีคลัสเตอร์และการลดขนาด 24% ในดัชนีที่ไม่ใช่คลัสเตอร์ของคุณ


โดยปกติแล้วเกณฑ์การสแกนจะต่ำกว่านั้นมาก (10% หรือต่ำกว่า) อย่างไรก็ตามเนื่องจากช่วงนั้นเป็นวันเดียวจากเมื่อปีที่แล้วจึงไม่ควรทำแม้กระทั่งเกณฑ์นั้น และการสแกนดัชนีแบบคลัสเตอร์ไม่ได้ถูกระบุเนื่องจากมีการเพิ่มดัชนีครอบคลุม เนื่องจากดัชนีดังกล่าวทำให้ส่วนคำสั่ง WHERE สามารถใช้กับ SARG ได้จึงควรเลือกใช้
RBarryYoung

@RarryYoung ฉันพยายามอธิบายว่าทำไมดัชนีที่ไม่ใช่แบบคลัสเตอร์ใน [ป้อนวันที่], [DeviceID] ไม่ได้ถูกใช้งานตั้งแต่แรก เกี่ยวกับเกณฑ์ที่ฉันคิดว่าเราทั้งสองเห็นด้วยฉันแค่พูดจากมุมมองของหน้า ฉันจะแก้ไขคำตอบเพื่อให้ชัดเจนยิ่งขึ้น
Edward Dortland

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

ขอบคุณสำหรับการตอบสนองที่ครอบคลุมมากทำให้รู้สึกมาก สำหรับภาระงานตารางมีเม็ดมีด 150-300 เม็ดต่อรอบระยะเวลา 5 นาทีและอ่านสองสามข้อความต่อวันเพื่อวัตถุประสงค์ในการรายงาน
เนท

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

8

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

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

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

แล้วจะทำอย่างไรดี?

คำแนะนำของฉันคือการลดดัชนี NC เปลี่ยน PK คลัสเตอร์เป็น nonclustered และสร้างดัชนีคลัสเตอร์บน [DateEntered] ง่ายกว่าดีกว่าจนกว่าจะได้รับการพิสูจน์เป็นอย่างอื่น


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

การเพิ่มข้อมูลไปยังโครงสร้าง b-tree จะทำให้เสียสมดุล แม้ว่าคุณจะเพิ่มแถวในลำดับคลัสเตอร์ดัชนีจะเสียสมดุล ตารางการจัดทำดัชนีใหม่จะลบการแตกแฟรกเมนต์และ DBA ใด ๆ จะบอกคุณว่าตารางจำเป็นต้องมีการทำดัชนีใหม่หลังจากมีการเพิ่มข้อมูล "เพียงพอ" ในตาราง (คำจำกัดความของ "พอ" อาจถูกถกเถียงกันหรือ "เมื่อ" อาจเป็นการสนทนา) ฉันไม่เห็นอะไรเลยในคำถามที่กล่าวว่าการจัดทำดัชนีใหม่ไม่สามารถทำได้ด้วยเหตุผลบางอย่าง
darin strait

4

ตราบใดที่คุณมี "*" อยู่ในนั้นสิ่งเดียวที่ฉันนึกได้ว่าจะสร้างความแตกต่างได้มากคือการเปลี่ยนนิยามดัชนีของคุณเป็น:

CREATE NONCLUSTERED INDEX [CommonQueryIndex] ON [dbo].[Heartbeats] 
(
    [DateEntered] ASC,
    [DeviceID] ASC
)INCLUDE (ID, IsWebUp, IsPingUp, IsPUp)
 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]

ดังที่ฉันได้กล่าวไว้ในความคิดเห็นมันควรใช้ดัชนีนั้น แต่ถ้าคุณไม่สามารถโน้มน้าวให้ใช้คำสั่ง ORDER BY หรือดัชนีใบ้ได้


ฉันเพิ่งลองทำสิ่งนี้และฉันก็ยังอยู่ในจุดเดิม 2500ms รอการตอบกลับจากเซิร์ฟเวอร์และเวลาในการประมวลผลไคลเอนต์ 10ms
เนท

โพสต์แผนแบบสอบถาม
RBarryYoung

ดูเหมือนว่าจะใช้ดัชนีแบบกลุ่ม (ค่าใช้จ่าย SELECT: 0% <- สูงสุดต้นทุน: 20% <- คลัสเตอร์ดัชนีสแกน PK_Heartbeats ค่าใช้จ่าย: 80%)
เนท

ใช่มันไม่ถูกต้องบางครั้งก็ทิ้งสถิติ / เครื่องมือเพิ่มประสิทธิภาพ เพิ่มคำใบ้เพื่อบังคับให้ใช้ดัชนีใหม่
RBarryYoung

@ Max Vernon: อาจเป็นไปได้ แต่ควรได้รับการตั้งค่าสถานะบนแผนแบบสอบถาม
RBarryYoung

3

ฉันดูที่นี่แตกต่างออกไปเล็กน้อย

  • ใช่ฉันรู้ว่ามันเป็นหัวข้อเก่า แต่ฉันสนใจ

ฉันจะทิ้งคอลัมน์วันที่และเวลา - เปลี่ยนเป็น int มีตารางการค้นหาหรือทำการแปลงสำหรับวันที่ของคุณ

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

สุดท้ายหากคุณสามารถใช้ SQL 2012 ได้ให้ลองใช้ SEQUENCE ซึ่งจะมีประสิทธิภาพเหนือกว่า identity () สำหรับส่วนแทรก


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

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

1
ฉันไม่แน่ใจว่าทำไมฉันถึงพลาดก่อนหน้านี้ แต่ใช้การบีบอัดแถวในดัชนีคลัสเตอร์และดัชนีที่ไม่ทำคลัสเตอร์เช่นกัน ฉันเพิ่งทดสอบตารางของคุณอย่างรวดเร็วและนี่คือสิ่งที่ฉันพบ: ฉันสร้างชุดข้อมูล (5.8 ล้านแถว) ในตารางที่กำหนดไว้ด้านบน ฉันบีบอัด (แถว) ดัชนีที่ทำคลัสเตอร์และไม่ได้ทำคลัสเตอร์ การอ่านแบบลอจิคัลขึ้นอยู่กับการค้นหาที่แน่นอนของคุณลดลงจาก 2,074 เป็น 1,433 นั่นคือการลดลงอย่างมีนัยสำคัญและฉันมั่นใจว่าเพียงอย่างเดียวจะช่วยให้คุณออก - และมีความเสี่ยงต่ำมาก
Jeremy Lowell
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.