วิธีเพิ่มความเร็วในการเลือกที่แตกต่างกันอย่างไร


16

ฉันมีตัวเลือกแบบง่าย ๆ ในข้อมูลอนุกรมเวลา:

SELECT DISTINCT user_id
FROM events
WHERE project_id = 6
AND time > '2015-01-11 8:00:00'
AND time < '2015-02-10 8:00:00';

และใช้เวลา 112 วินาที นี่คือแผนแบบสอบถาม:

http://explain.depesz.com/s/NTyA

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

คำตอบ:


19

คุณอาจไม่อยากได้ยินสิ่งนี้ แต่ทางเลือกที่ดีที่สุดในการเร่งความเร็วSELECT DISTINCTคือการหลีกเลี่ยง DISTINCTการเริ่มต้น ในหลายกรณี (ไม่ใช่ทั้งหมด!) สามารถหลีกเลี่ยงได้ด้วยการออกแบบฐานข้อมูลหรือแบบสอบถามที่ดีขึ้น

บางครั้งGROUP BYเร็วกว่าเพราะใช้เส้นทางรหัสที่แตกต่าง

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

CREATE INDEX foo ON events (project_id, "time", user_id);

การเพิ่มuser_idจะมีประโยชน์ก็ต่อเมื่อคุณสแกนเฉพาะดัชนีนี้เท่านั้น ไปที่ลิงก์เพื่อดูรายละเอียด จะลบBitmap Heap Scanราคาแพงออกจากแผนคิวรีของคุณซึ่งใช้ 90% ของเวลาแบบสอบถาม

EXPLAINผลลัพธ์ของคุณบอกฉันว่าข้อความค้นหาต้องย่อผู้ใช้ 2,491 รายออกจากแถวที่ตรงกันครึ่งล้าน สิ่งนี้จะไม่เร็วอย่างมากไม่ว่าคุณจะทำอะไร แต่ก็สามารถเร็วขึ้นอย่างมาก

หากช่วงเวลาในแบบสอบถามของคุณเหมือนกันเสมอการMATERIALIIZED VIEWพับuser_idแต่ละครั้ง(project_id, <fixed time intervall>)จะไปไกล ไม่มีโอกาสที่จะมีช่วงเวลาที่แตกต่างกัน บางทีคุณอาจพับผู้ใช้อย่างน้อยต่อชั่วโมงหรือบางหน่วยเวลาขั้นต่ำอื่น ๆ และนั่นจะซื้อประสิทธิภาพเพียงพอที่จะรับประกันค่าใช้จ่ายจำนวนมาก

Nitpick:
ส่วนใหญ่เพรดิเคตที่"time"ควรจะเป็น:

AND "time" >= '2015-01-11 8:00:00'
AND "time" <  '2015-02-10 8:00:00';

นอกเหนือ:
อย่าใช้timeเป็นตัวระบุ มันเป็นคำสงวนใน SQL มาตรฐานและเป็นประเภทพื้นฐานใน Postgres


ฉันได้อ่านเพียงเล็กน้อยเกี่ยวกับการสแกนดัชนีเท่านั้นฉันจะให้มันยิง
แซม

โชคไม่ดีที่ช่วงเวลาไม่ได้ถูกแก้ไข
แซม

@ แซม: แล้วแบบสอบถามตัวอย่างของคุณถึงได้เร็วเท่าไหร่กับดัชนีที่แนะนำ?
Erwin Brandstetter

3
@edwin: ยังไม่ได้ลองผลิต อย่างไรก็ตามฉันเรียกใช้แบบสอบถามต้นฉบับในท้องถิ่นของฉัน (ด้วยข้อมูลเดียวกัน) และใช้เวลา 3678.780 มิลลิวินาที จากนั้นฉันก็เพิ่มดัชนีและมันเร่งความเร็วได้สูงสุด 170.156 ms ตอนนี้แผนมี 'สแกนดัชนีอย่างเดียวโดยใช้ foo กับเหตุการณ์'
Sam

1
@ Sam: ดี! นั่นคือสิ่งที่ฉันต้องการ
Erwin Brandstetter

2

นี่คือการทดสอบของฉันในกรณีของแซมและคำตอบของเออร์วิน

drop table t1
create table t1 (id int, user_id int, project_id int, date_time timestamp without time zone) ;

insert into t1 -- 10 million row - size="498 MB"
select row_number() over(), round(row_number() over()/1000), round(row_number() over()/100000) , date
from generate_series('2015-01-01'::date, '2016-12-01'::date,'6 seconds'::interval
) date 
limit 10000000

-- before indexing - 10000000 row - output=100 row - time=2900ms
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 8:00:00'
AND date_time < '2016-12-01 8:00:00' ;

CREATE INDEX foo ON t1 (project_id, date_time, user_id); -- time process=51.2 secs -- size="387 MB"         

-- after indexing - 10000000 row - output=100 row - time= 75ms (reduce ~ 38 times)
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 00:00:00'
AND date_time < '2016-12-01 00:00:00' ;

เออร์วินกล่าวว่า "คุณอาจไม่ต้องการฟังสิ่งนี้ แต่ทางเลือกที่ดีที่สุดในการเพิ่มความเร็วในการเลือก DISTINCT คือการหลีกเลี่ยง DISTINCT เพื่อเริ่มต้นในหลาย ๆ กรณี (ไม่ใช่ทั้งหมด!) สามารถหลีกเลี่ยงได้ด้วยการออกแบบฐานข้อมูลที่ดีขึ้น ". ฉันคิดว่าเขาพูดถูกเราควรหลีกเลี่ยงการใช้ "ชัดเจนจัดกลุ่มตามลำดับโดย" (ถ้ามี)

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


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