จำกัด แถวด้วยฟังก์ชั่น Spatial


9

ฉันพยายามปรับปรุงประสิทธิภาพสำหรับข้อความค้นหาด้านล่าง ไม่ว่าฉันจะเขียนแบบสอบถามอย่างไร (แบบสอบถามย่อยในส่วนคำสั่งย่อย, แบบสอบถามย่อยในส่วนคำสั่ง WHERE) จะยืนยันว่าจะเรียกใช้แถว ~ 570K ทั้งหมดผ่านฟังก์ชัน ST_DWITHIN ที่มีราคาแพงแม้ว่าจะมีเพียง 60 แถวที่เขต = 24 ฉันจะได้รับ postgres เพื่อกรองในเขต = 24 ก่อนที่จะวิ่งผ่าน fung postgis ซึ่งดูเหมือนว่าฉันจะเร็วขึ้นและมีประสิทธิภาพมากขึ้น? 700ms ไม่ได้ทำให้เกิดความกังวลมากเกินไป แต่เนื่องจากตารางนี้เพิ่มขึ้นเป็น 10M + ฉันกังวลเกี่ยวกับประสิทธิภาพ

นอกจากนี้เพื่อทราบว่า p.id เป็นคีย์หลัก p.zipcode เป็นดัชนี fk, z.county เป็นดัชนี fk และ p.geom มีดัชนี GiST

ค้นหา:

EXPLAIN ANALYZE
  SELECT count(p.id)
  FROM point AS p
  LEFT JOIN zipcode AS z
    ON p.zipcode = z.zipcode
  WHERE z.county = 24
    AND ST_DWithin(
      p.geom, 
      ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269), 
      16090.0,
      false
    )

อธิบายการวิเคราะห์:

Aggregate  (cost=250851.91..250851.92 rows=1 width=4) (actual time=724.007..724.007 rows=1 loops=1)
  ->  Hash Join  (cost=152.05..250851.34 rows=228 width=4) (actual time=0.359..723.996 rows=51 loops=1)
        Hash Cond: ((p.zipcode)::text = (z.zipcode)::text)
        ->  Seq Scan on point p  (cost=0.00..250669.12 rows=7437 width=10) (actual time=0.258..723.867 rows=63 loops=1)
              Filter: (((geom)::geography && '0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography) AND ('0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography && _st_expand((geom)::geography, 16090::double precision)) AND _st_dwithin((g (...)
              Rows Removed by Filter: 557731
        ->  Hash  (cost=151.38..151.38 rows=54 width=6) (actual time=0.095..0.095 rows=54 loops=1)
              Buckets: 1024  Batches: 1  Memory Usage: 3kB
              ->  Bitmap Heap Scan on zipcode z  (cost=4.70..151.38 rows=54 width=6) (actual time=0.023..0.079 rows=54 loops=1)
                    Recheck Cond: (county = 24)
                    Heap Blocks: exact=39
                    ->  Bitmap Index Scan on fki_zipcode_county_foreign_key  (cost=0.00..4.68 rows=54 width=0) (actual time=0.016..0.016 rows=54 loops=1)
                          Index Cond: (county = 24)
Planning time: 0.504 ms
Execution time: 724.064 ms

อาจลองเปลี่ยนบรรทัด "ชี้เป็น p ซ้ายเข้าร่วม zipcode เป็น z" เป็นอย่างเช่น "ชี้เป็น p ซ้ายเข้าร่วม (เลือก * จากรหัสไปรษณีย์ WHERE zipcode.county = 24) เป็น z" หรือไม่
weiji 14

เพิ่งลองผลลัพธ์เดียวกัน เมื่อฉันคัดลอกpointแถว~ 60 โดยที่เคาน์ตี = 24 ไปยังตารางใหม่ด้วยตัวเองแบบสอบถามใช้เวลาเพียง. 453ms เทียบกับ 724 ดังนั้นจึงมีความแตกต่างอย่างมาก
Josh

1
คุณควรใช้count(*)เป็นเรื่องของสไตล์ ถ้าidเป็น pkid อย่างที่คุณพูดNOT NULLนั่นหมายความว่ามันเหมือนกัน ยกเว้นcount(id)มีข้อเสียเปรียบที่คุณต้องถามคำถามว่าถ้าidเป็นโมฆะ
Evan Carroll

1
ฉันจะถามได้ไหมว่าเพราะเหตุใดคุณจึงใช้การรวมภายนอกด้านซ้าย ลองเปลี่ยนเป็นการเข้าร่วมภายใน ... ผลลัพธ์ควรเหมือนกัน
MickyT

หาก z.country เป็นปัจจัย จำกัด ฉันขอแนะนำให้คุณใส่สิ่งนี้ลงในแบบสอบถาม CTE ก่อนจากนั้นตรวจสอบผลลัพธ์เหล่านั้นเพื่อหาจุดตัดพร้อมจุดสนใจของคุณ เนื่องจากดัชนีอวกาศน่าจะเลือกได้น้อยกว่าเคาน์ตี = 24 ในกรณีนี้มันจึงเข้ามา
John Powell

คำตอบ:


3

คุณสามารถดูปัญหาที่เกิดขึ้นกับจำนวนแถวจริงที่คาดไว้ ผู้วางแผนคิดว่ามี 7,437 แถว แต่มีเพียง 63 แห่งเท่านั้นสถิติปิดอยู่ ที่น่าสนใจพอเกินไปก็ไม่ได้ใช้ดัชนีกรอบ (ดัชนี) ค้นหาด้วยคุณสามารถวางผลมาจากการDWithin \d pointPostGIS และ PostgreSQL รุ่นใด

ANALYZE pointลองใช้ คุณได้รับแผนเดียวกันเมื่อคุณขยับเงื่อนไขขึ้นหรือไม่?

JOIN zipcode AS z
  ON p.zipcode = z.zipcode
  AND z.county = 24

ฉันรันการวิเคราะห์และลองใช้เงื่อนไข AND ใหม่ในเปิด แต่ก็ยังได้รับ 700ms เวลาทำงาน นี่คือ PGSQL 9.4 และ PostGIS 2.2
Josh

2

ในฐานะที่เป็นบันทึกด้านข้างมีโอกาสที่สมเหตุสมผลที่พฤติกรรมนี้ได้รับการแก้ไขใน PostGIS 2.3.0 หากคุณต้องการเรียกว่าเป็นข้อบกพร่อง

จากเอกสารบน PostgreSQL

ตัวเลขบวกที่ให้ค่าใช้จ่ายการดำเนินการโดยประมาณสำหรับฟังก์ชันในหน่วยของ cpu_operator_cost หากฟังก์ชันส่งคืนชุดค่านี้จะเป็นราคาต่อแถวที่ส่งคืน หากไม่ได้ระบุต้นทุนจะมีการสันนิษฐาน 1 หน่วยสำหรับฟังก์ชันภาษา C และภายในและ 100 หน่วยสำหรับฟังก์ชั่นในภาษาอื่น ๆ ทั้งหมด ค่าที่มากขึ้นทำให้ผู้วางแผนพยายามหลีกเลี่ยงการประเมินฟังก์ชั่นบ่อยกว่าที่จำเป็น

ดังนั้นค่าเริ่มต้นคือ 1 (ถูกมาก) D_Withinการใช้ดัชนี GIST นั้นถูกมาก แต่นั่นเพิ่มขึ้นเป็น 100 (โดยพร็อกซีของภายใน_ST_DWithin)

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


1

ขอบคุณคำใบ้ของ John Powell ฉันได้แก้ไขแบบสอบถามเพื่อกำหนดเงื่อนไขการ จำกัด เขตในการค้นหาด้วย / CTE และการปรับปรุงนี้ค่อนข้างดีถึง 222ms เทียบกับ 700 ยังคงเป็นหนทางไกลจาก 0.74 ms ฉันได้รับเมื่อข้อมูลอยู่ในนั้น ตารางของตัวเอง ฉันยังไม่แน่ใจว่าทำไมนักวางแผนไม่ได้ จำกัด ชุดข้อมูลก่อนที่จะทำงานผ่านฟังก์ชั่น postgis ที่มีราคาแพงและฉันจะต้องลองชุดข้อมูลที่มีขนาดใหญ่กว่าเมื่อฉันมีพวกมัน แต่สิ่งนี้ดูเหมือนจะเป็นทางออกสำหรับสถานการณ์เฉพาะนี้ในตอนนี้

with points as (
   select p.id, p.geom from point p inner join zipcode z
   on p.zipcode = z.zipcode
   where county = 24
   ) 


SELECT count(points.id)
FROM points
WHERE ST_DWITHIN(points.geom, (ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269)), 16090.0, false)

1
เราจะต้องดูแผนแบบสอบถามทั้งสามแผนและสคีมาสำหรับตาราง (ร้องขอในคำตอบ \ d ของฉัน)
Evan Carroll

0

คุณควรสร้างดัชนีในzipcode(county, zipcode)ที่ควรให้ดัชนีสแกน z เท่านั้น

คุณอาจต้องการทดสอบด้วยbtree_gistส่วนขยายที่สร้างpoint(zipcode, geom)ดัชนีหรือpoint(geom, zipcode)และzipcode(zipcode, county)ดัชนี

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