ST_ ระยะทางไม่ได้ใช้ดัชนีสำหรับการสืบค้นเชิงพื้นที่


10

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

CREATE TABLE points (
    population DOUBLE PRECISION NOT NULL,
    location GEOGRAPHY(4326, POINT) NOT NULL
)
CREATE INDEX points_gix ON points USING GIST(location);

การสืบค้นนั้นง่ายมากตามที่ได้รับ

SELECT SUM(population)
FROM points
WHERE ST_Distance(
    location,
    ST_GeographyFromText('SRID=4326; POINT(0 0)')
) < 1000

PostgreSQL ใช้การสแกน Seq เสมอฉันลองชุดย่อยที่มี 10,000 คะแนน - ยังคงสแกน Seq ความคิดใด ๆ


3
คุณไม่ใช้ฟังก์ชันใด ๆ ที่สามารถใช้ดัชนี ใช้ st_dwithin แทน จากนั้น fuction จะทำการสแกนดัชนีก่อน
Nicklas Avén

ลองคิดถึงสิ่งที่คุณทำ - คำนวณระยะทางจากแต่ละจุดในตารางไปยังจุดที่กำหนด - และคุณจะเข้าใจว่าทำไมไม่สามารถใช้ดัชนี แทนที่จะใช้โอเปอเรเตอร์ที่สามารถใช้ดัชนีได้เช่น ST_DWithin
Vince

คำตอบ:


19

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

SELECT SUM(population) FROM points 
WHERE ST_DWithin(location, ST_GeographyFromText('SRID=4326; POINT(0 0)'), 1000);

ST_Distance นั้นมีประโยชน์มากกว่าสำหรับการสั่งซื้อผลลัพธ์ซึ่งมักจะใช้ร่วมกับ ORDER BY และ / หรือ LIMIT ที่ได้รับมาพร้อมกับแบบสอบถามที่ใช้ดัชนี


1
ขอบคุณ ฉันควรอ่านเอกสารก่อนถามคำถาม
synapse

1
ว้าว! ขอบคุณ! คุณเพียงแค่ "เร่ง" ข้อความค้นหาที่ช้าของฉันเช่น 100x fold หรือมากกว่าเนื่องจากเปลี่ยน st_distance เป็น st_dwithin (ผมบอกว่า "เร่ง" เพราะเรื่องนี้ไม่เคยควรจะเกิดขึ้นในครั้งแรกที่ฉันได้รับการระมัดระวังมากขึ้น)
Hendy Irawan

1
@HendyIrawan ยินดี. มันเป็นความผิดพลาดง่าย ๆ ที่จะทำ
John Powell

@ JohnPowellakaBarçaฉันเพิ่มการเพิ่มประสิทธิภาพอีกครั้ง (แม้ว่าจะสูญเสียมากฉันได้เพิ่มคำตอบสำหรับกรณีของฉัน) แต่คุณชี้ให้ฉันในทิศทางที่ถูกต้องขอบคุณ
Hendy Irawan

4

ในฐานะที่เป็น @ JohnPowellakaBarçaกล่าวว่าST_DWithin()เป็นวิธีที่จะไปเมื่อคุณต้องการความถูกต้อง

อย่างไรก็ตามในกรณีของฉันฉันต้องการการประเมินอย่างคร่าวๆดังนั้นแม้จะST_DWithin()มีราคาแพงเกินไป (ในค่าแบบสอบถาม) สำหรับความต้องการของฉัน ฉันใช้&&และST_Expand(box2d)(อย่าเข้าใจผิดกับgeometryเวอร์ชัน) แทน ตัวอย่าง:

SELECT * FROM profile
  WHERE
    address_point IS NOT NULL AND
    address_point && CAST(ST_Expand(CAST(ST_GeomFromText(:point) AS box2d), 0.5) AS geometry;

สิ่งที่ชัดเจนในทันทีคือเรากำลังจัดการกับองศาแทนที่จะเป็นเมตรและใช้กล่องขอบแทนวงกลมในทรงกลม สำหรับกรณีการใช้งานของฉันมันลดลงจาก24 msเหลือเพียง2 ms (ในเครื่องใน SSD) อย่างไรก็ตามสำหรับฐานข้อมูลการผลิตของฉันใน AWS RDS PostgreSQL พร้อมการเชื่อมต่อพร้อมกันและโควต้า IOPS แทบจะไม่ใจกว้าง (100 IOPS) ST_DWithin()แบบสอบถามดั้งเดิมใช้ IOPS มากเกินไปและสามารถทำงานได้มากกว่า2000 มิลลิวินาทีและแย่ลงมากเมื่อโควต้า IOPS หมดลง

นี่ไม่ใช่สำหรับทุกคน แต่ในกรณีที่คุณสามารถเสียสละความแม่นยำสำหรับความเร็ว (หรือเพื่อบันทึก IOPS) วิธีการนี้อาจเหมาะสำหรับคุณ ดังที่คุณเห็นในแผนแบบสอบถามด้านล่างST_DWithinยังคงต้องการตัวกรองเชิงพื้นที่ภายใน Bitmap Heap Scan นอกเหนือจาก Recheck Cond ในขณะ&&ที่รูปทรงเรขาคณิตกล่องไม่จำเป็นต้องใช้ตัวกรองและใช้ Recheck Cond เท่านั้น

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

ตาราง 20,000 แถวST_DWithin(geography, geography, 100000, FALSE)บน AWS RDS 512 MB RAM ที่มี 300 IOPS:

Aggregate  (cost=4.61..4.62 rows=1 width=8) (actual time=2011.358..2011.358 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..4.61 rows=1 width=0) (actual time=1735.025..2010.635 rows=1974 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)))
        Filter: (((status)::text = 'ACTIVE'::text) AND ((gender)::text = 'MALE'::text) AND (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(address_point, '100000'::double precision)) AND _st_dwithin(address_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, false)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(hometown_point, '100000'::double precision)) AND _st_dwithin(hometown_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, false))))
        Rows Removed by Filter: 3323
        Heap Blocks: exact=7014
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=1716.425..1716.425 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.42 rows=1 width=0) (actual time=1167.698..1167.698 rows=16086 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.42 rows=1 width=0) (actual time=548.723..548.723 rows=7846 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
Planning time: 47.366 ms
Execution time: 2011.429 ms

ตาราง 20,000 แถว&&และST_Expand(box2d)บน AWS RDS 512 MB RAM ที่มี 300 IOPS:

Aggregate  (cost=3.85..3.86 rows=1 width=8) (actual time=584.346..584.346 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..3.85 rows=1 width=0) (actual time=555.048..584.083 rows=1154 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)))
        Filter: (((status)::text = 'ACTIVE'::text) AND ((gender)::text = 'MALE'::text))
        Rows Removed by Filter: 555
        Heap Blocks: exact=3812
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=553.091..553.091 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.42 rows=1 width=0) (actual time=413.074..413.074 rows=4850 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.42 rows=1 width=0) (actual time=140.014..140.014 rows=3100 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
Planning time: 0.673 ms
Execution time: 584.386 ms

อีกครั้งด้วยข้อความค้นหาที่ง่ายกว่า:

ตาราง 20,000 แถวST_DWithin(geography, geography, 100000, FALSE)บน AWS RDS 512 MB RAM ที่มี 300 IOPS:

Aggregate  (cost=4.60..4.61 rows=1 width=8) (actual time=36.448..36.448 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..4.60 rows=1 width=0) (actual time=7.694..35.545 rows=2982 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)))
        Filter: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(address_point, '100000'::double precision)) AND _st_dwithin(address_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, true)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(hometown_point, '100000'::double precision)) AND _st_dwithin(hometown_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, true)))
        Rows Removed by Filter: 2322
        Heap Blocks: exact=2947
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=7.197..7.197 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.41 rows=1 width=0) (actual time=5.265..5.265 rows=5680 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.41 rows=1 width=0) (actual time=1.930..1.930 rows=2743 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
Planning time: 0.479 ms
Execution time: 36.512 ms

ตาราง 20,000 แถว&&และST_Expand(box2d)บน AWS RDS 512 MB RAM ที่มี 300 IOPS:

Aggregate  (cost=3.84..3.85 rows=1 width=8) (actual time=6.263..6.264 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..3.84 rows=1 width=0) (actual time=4.295..5.864 rows=1711 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)))
        Heap Blocks: exact=1419
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=4.122..4.122 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.41 rows=1 width=0) (actual time=3.018..3.018 rows=1693 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.41 rows=1 width=0) (actual time=1.102..1.102 rows=980 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
Planning time: 0.399 ms
Execution time: 6.306 ms

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