วิธีเพิ่มความเร็วในการเรียงลำดับโดยการเรียงลำดับเมื่อใช้ดัชนี GIN ใน PostgreSQL


13

ฉันมีโต๊ะแบบนี้:

CREATE TABLE products (
  id serial PRIMARY KEY, 
  category_ids integer[],
  published boolean NOT NULL,
  score integer NOT NULL,
  title varchar NOT NULL);

ผลิตภัณฑ์สามารถเป็นของหลายหมวดหมู่ category_idsคอลัมน์เก็บรายการรหัสประจำตัวของหมวดหมู่ผลิตภัณฑ์ทั้งหมด

ข้อความค้นหาทั่วไปจะมีลักษณะดังนี้ (ค้นหาหมวดหมู่เดียวเสมอ):

SELECT * FROM products WHERE published
  AND category_ids @> ARRAY[23465]
ORDER BY score DESC, title
LIMIT 20 OFFSET 8000;

เพื่อเพิ่มความเร็วฉันใช้ดัชนีต่อไปนี้:

CREATE INDEX idx_test1 ON products
  USING GIN (category_ids gin__int_ops) WHERE published;

อันนี้ช่วยได้มากเว้นแต่จะมีผลิตภัณฑ์มากเกินไปในหมวดหมู่เดียว มันจะกรองผลิตภัณฑ์ที่อยู่ในหมวดหมู่นั้นอย่างรวดเร็ว แต่จากนั้นจะมีการดำเนินการเรียงลำดับที่ต้องดำเนินการอย่างหนัก (โดยไม่มีดัชนี)

btree_ginส่วนขยายที่ติดตั้งแล้วทำให้ฉันสามารถสร้างดัชนี GIN หลายคอลัมน์ได้ดังนี้:

CREATE INDEX idx_test2 ON products USING GIN (
  category_ids gin__int_ops, score, title) WHERE published;

แต่Postgres ไม่ต้องการที่จะใช้สำหรับการจัดเรียง แม้ว่าฉันจะลบตัวDESCระบุในแบบสอบถาม

วิธีการอื่น ๆ เพื่อเพิ่มประสิทธิภาพของงานยินดีต้อนรับมาก


ข้อมูลเพิ่มเติม:

  • PostgreSQL 9.4 พร้อมส่วนขยาย intarray
  • จำนวนผลิตภัณฑ์ทั้งหมดในปัจจุบันคือ 260k แต่คาดว่าจะเติบโตอย่างมีนัยสำคัญ (ไม่เกิน 10M นี่คือแพลตฟอร์มอีคอมเมิร์ซหลายผู้เช่า)
  • ผลิตภัณฑ์ต่อหมวดหมู่ 1..10000 (สามารถเติบโตได้สูงสุด 100k) โดยเฉลี่ยต่ำกว่า 100 แต่หมวดหมู่ที่มีผลิตภัณฑ์จำนวนมากมักจะดึงดูดคำขออื่น ๆ อีกมากมาย

ได้รับแผนแบบสอบถามต่อไปนี้จากระบบทดสอบที่มีขนาดเล็กกว่า (4680 ผลิตภัณฑ์ในหมวดหมู่ที่เลือกรวมผลิตภัณฑ์ 200k ในตาราง):

Limit  (cost=948.99..948.99 rows=1 width=72) (actual time=82.330..82.341 rows=20 loops=1)
  ->  Sort  (cost=948.37..948.99 rows=245 width=72) (actual time=80.231..81.337 rows=4020 loops=1)
        Sort Key: score, title
        Sort Method: quicksort  Memory: 928kB
        ->  Bitmap Heap Scan on products  (cost=13.90..938.65 rows=245 width=72) (actual time=1.919..16.044 rows=4680 loops=1)
              Recheck Cond: ((category_ids @> '{292844}'::integer[]) AND published)
              Heap Blocks: exact=3441
              ->  Bitmap Index Scan on idx_test2  (cost=0.00..13.84 rows=245 width=0) (actual time=1.185..1.185 rows=4680 loops=1)
                    Index Cond: (category_ids @> '{292844}'::integer[])
Planning time: 0.202 ms
Execution time: 82.404 ms

หมายเหตุ # 1 : 82 ms อาจไม่น่ากลัว แต่นั่นเป็นเพราะบัฟเฟอร์การเรียงเหมาะกับหน่วยความจำ เมื่อฉันเลือกคอลัมน์ทั้งหมดจากตารางผลิตภัณฑ์ ( SELECT * FROM ...และในชีวิตจริงมีประมาณ 60 คอลัมน์) มันจะSort Method: external merge Disk: 5696kBเพิ่มเวลาดำเนินการเป็นสองเท่า และนั่นเป็นเพียง 4680 ผลิตภัณฑ์

Action point # 1 (มาจาก Note # 1): เพื่อลดขนาดหน่วยความจำของการดำเนินการเรียงลำดับและดังนั้นจึงเร่งความเร็วขึ้นเล็กน้อยเพื่อการดึงเรียงลำดับและ จำกัด รหัสผลิตภัณฑ์ก่อนจากนั้นจึงดึงระเบียนทั้งหมด:

SELECT * FROM products WHERE id IN (
  SELECT id FROM products WHERE published AND category_ids @> ARRAY[23465]
  ORDER BY score DESC, title LIMIT 20 OFFSET 8000
) ORDER BY score DESC, title;

สิ่งนี้นำเราย้อนกลับไปSort Method: quicksort Memory: 903kBและประมาณ 80 มิลลิวินาทีสำหรับผลิตภัณฑ์ 4680 ยังสามารถช้าได้เมื่อจำนวนผลิตภัณฑ์เติบโตถึง 100k


ในหน้านี้: hlinnaka.iki.fi/2014/03/28/…มีความคิดเห็นที่ btree_gin ไม่สามารถใช้สำหรับการเรียงลำดับได้
Mladen Uzelac

ตกลงฉันเปลี่ยนชื่อใหม่เพื่ออนุญาตตัวเลือกเพิ่มเติม
Yaroslav Stavnichiy

คุณมักจะค้นหาหมวดหมู่เดียวหรือไม่? และโปรดให้ข้อมูลพื้นฐานเพิ่มเติม: รุ่น Postgres, ลำดับความสำคัญ, แถวต่อหมวดหมู่ (นาที / เฉลี่ย / สูงสุด) พิจารณาคำแนะนำในข้อมูลแท็กสำหรับPostgreSQL ประสิทธิภาพ และ: scoreสามารถเป็นโมฆะ แต่คุณยังคงเรียงลำดับตามไม่ได้score DESC score DESC NULLS LASTดูเหมือนว่าอย่างใดอย่างหนึ่งไม่ถูกต้อง ...
Erwin Brandstetter

ฉันได้เพิ่มข้อมูลเพิ่มเติมตามที่ร้องขอ ฉันมักจะค้นหาหมวดหมู่เดียว และscoreอันที่จริงแล้วไม่ใช่ NULL - ฉันแก้ไขนิยามของตารางแล้ว
Yaroslav Stavnichiy

คำตอบ:


10

ฉันทำการทดลองมากมายและนี่คือสิ่งที่ฉันค้นพบ

GIN และการเรียงลำดับ

ดัชนี GIN ในปัจจุบัน (ตั้งแต่เวอร์ชั่น 9.4) ไม่สามารถช่วยเหลือการสั่งซื้อได้

ในขณะนี้ประเภทดัชนีที่รองรับโดย PostgreSQL มีเพียง B-tree เท่านั้นที่สามารถสร้างเอาต์พุตที่เรียงลำดับได้ดัชนีประเภทอื่น ๆ จะส่งคืนแถวที่ตรงกันในลำดับที่ไม่ได้ระบุและขึ้นอยู่กับการใช้งาน

work_mem

ขอบคุณ Chris สำหรับการชี้ไปที่พารามิเตอร์การกำหนดค่านี้ มันเริ่มต้นที่ 4MB และในกรณีที่ชุดระเบียนของคุณมีขนาดใหญ่ขึ้นการเพิ่มwork_memค่าที่เหมาะสม (สามารถพบได้จากEXPLAIN ANALYSE) สามารถเร่งความเร็วการดำเนินการเรียงลำดับอย่างมีนัยสำคัญ

ALTER SYSTEM SET work_mem TO '32MB';

รีสตาร์ทเซิร์ฟเวอร์เพื่อให้การเปลี่ยนแปลงมีผลจากนั้นตรวจสอบอีกครั้ง:

SHOW work_mem;

ข้อความค้นหาดั้งเดิม

ฉันเติมฐานข้อมูลของฉันด้วยผลิตภัณฑ์ 650k โดยมีบางหมวดหมู่ที่ถือผลิตภัณฑ์ได้สูงสุด 40k ฉันทำให้การสืบค้นง่ายขึ้นเล็กน้อยโดยการลบpublishedคำสั่ง:

SELECT * FROM products WHERE category_ids @> ARRAY [248688]
ORDER BY score DESC, title LIMIT 10 OFFSET 30000;

Limit  (cost=2435.62..2435.62 rows=1 width=1390) (actual time=1141.254..1141.256 rows=10 loops=1)
  ->  Sort  (cost=2434.00..2435.62 rows=646 width=1390) (actual time=1115.706..1140.513 rows=30010 loops=1)
        Sort Key: score, title
        Sort Method: external merge  Disk: 29656kB
        ->  Bitmap Heap Scan on products  (cost=17.01..2403.85 rows=646 width=1390) (actual time=11.831..25.646 rows=41666 loops=1)
              Recheck Cond: (category_ids @> '{248688}'::integer[])
              Heap Blocks: exact=6471
              ->  Bitmap Index Scan on idx_products_category_ids_gin  (cost=0.00..16.85 rows=646 width=0) (actual time=10.140..10.140 rows=41666 loops=1)
                    Index Cond: (category_ids @> '{248688}'::integer[])
Planning time: 0.288 ms
Execution time: 1146.322 ms

อย่างที่เราเห็นwork_memไม่พอเราจึงมีSort Method: external merge Disk: 29656kB(จำนวนที่นี่เป็นค่าประมาณต้องการมากกว่า 32MB สำหรับหน่วยความจำด่วนในหน่วยความจำเล็กน้อย)

ลดขนาดหน่วยความจำ

อย่าเลือกระเบียนเต็มรูปแบบสำหรับการเรียงลำดับใช้รหัสใช้เรียงลำดับชดเชยและ จำกัด แล้วโหลดเพียง 10 รายการที่เราต้องการ:

SELECT * FROM products WHERE id in (
  SELECT id FROM products WHERE category_ids @> ARRAY[248688]
  ORDER BY score DESC, title LIMIT 10 OFFSET 30000
) ORDER BY score DESC, title;

Sort  (cost=2444.10..2444.11 rows=1 width=1390) (actual time=707.861..707.862 rows=10 loops=1)
  Sort Key: products.score, products.title
  Sort Method: quicksort  Memory: 35kB
  ->  Nested Loop  (cost=2436.05..2444.09 rows=1 width=1390) (actual time=707.764..707.803 rows=10 loops=1)
        ->  HashAggregate  (cost=2435.63..2435.64 rows=1 width=4) (actual time=707.744..707.746 rows=10 loops=1)
              Group Key: products_1.id
              ->  Limit  (cost=2435.62..2435.62 rows=1 width=72) (actual time=707.732..707.734 rows=10 loops=1)
                    ->  Sort  (cost=2434.00..2435.62 rows=646 width=72) (actual time=704.163..706.955 rows=30010 loops=1)
                          Sort Key: products_1.score, products_1.title
                          Sort Method: quicksort  Memory: 7396kB
                          ->  Bitmap Heap Scan on products products_1  (cost=17.01..2403.85 rows=646 width=72) (actual time=11.587..35.076 rows=41666 loops=1)
                                Recheck Cond: (category_ids @> '{248688}'::integer[])
                                Heap Blocks: exact=6471
                                ->  Bitmap Index Scan on idx_products_category_ids_gin  (cost=0.00..16.85 rows=646 width=0) (actual time=9.883..9.883 rows=41666 loops=1)
                                      Index Cond: (category_ids @> '{248688}'::integer[])
        ->  Index Scan using products_pkey on products  (cost=0.42..8.45 rows=1 width=1390) (actual time=0.004..0.004 rows=1 loops=10)
              Index Cond: (id = products_1.id)
Planning time: 0.682 ms
Execution time: 707.973 ms

Sort Method: quicksort Memory: 7396kBหมายเหตุ ผลลัพธ์ดีกว่ามาก

เข้าร่วมและดัชนี B-tree เพิ่มเติม

ตามที่ Chris แนะนำฉันได้สร้างดัชนีเพิ่มเติม:

CREATE INDEX idx_test7 ON products (score DESC, title);

ก่อนอื่นฉันลองเข้าร่วมเช่นนี้

SELECT * FROM products NATURAL JOIN
  (SELECT id FROM products WHERE category_ids @> ARRAY[248688]
  ORDER BY score DESC, title LIMIT 10 OFFSET 30000) c
ORDER BY score DESC, title;

แผนแบบสอบถามแตกต่างกันเล็กน้อย แต่ผลลัพธ์เหมือนกัน:

Sort  (cost=2444.10..2444.11 rows=1 width=1390) (actual time=700.747..700.747 rows=10 loops=1)
  Sort Key: products.score, products.title
  Sort Method: quicksort  Memory: 35kB
  ->  Nested Loop  (cost=2436.05..2444.09 rows=1 width=1390) (actual time=700.651..700.690 rows=10 loops=1)
        ->  HashAggregate  (cost=2435.63..2435.64 rows=1 width=4) (actual time=700.630..700.630 rows=10 loops=1)
              Group Key: products_1.id
              ->  Limit  (cost=2435.62..2435.62 rows=1 width=72) (actual time=700.619..700.619 rows=10 loops=1)
                    ->  Sort  (cost=2434.00..2435.62 rows=646 width=72) (actual time=697.304..699.868 rows=30010 loops=1)
                          Sort Key: products_1.score, products_1.title
                          Sort Method: quicksort  Memory: 7396kB
                          ->  Bitmap Heap Scan on products products_1  (cost=17.01..2403.85 rows=646 width=72) (actual time=10.796..32.258 rows=41666 loops=1)
                                Recheck Cond: (category_ids @> '{248688}'::integer[])
                                Heap Blocks: exact=6471
                                ->  Bitmap Index Scan on idx_products_category_ids_gin  (cost=0.00..16.85 rows=646 width=0) (actual time=9.234..9.234 rows=41666 loops=1)
                                      Index Cond: (category_ids @> '{248688}'::integer[])
        ->  Index Scan using products_pkey on products  (cost=0.42..8.45 rows=1 width=1390) (actual time=0.004..0.004 rows=1 loops=10)
              Index Cond: (id = products_1.id)
Planning time: 1.015 ms
Execution time: 700.918 ms

การเล่นด้วยจำนวนออฟเซ็ตและจำนวนผลิตภัณฑ์ที่หลากหลายฉันไม่สามารถทำให้ PostgreSQL ใช้ดัชนี B-tree เพิ่มเติมได้

ดังนั้นฉันจึงไปตามทางแบบดั้งเดิมและสร้างตารางแยก :

CREATE TABLE prodcats AS SELECT id AS product_id, unnest(category_ids) AS category_id FROM products;
CREATE INDEX idx_prodcats_cat_prod_id ON prodcats (category_id, product_id);

SELECT p.* FROM products p JOIN prodcats c ON (p.id=c.product_id)
WHERE c.category_id=248688
ORDER BY p.score DESC, p.title LIMIT 10 OFFSET 30000;

Limit  (cost=122480.06..122480.09 rows=10 width=1390) (actual time=1290.360..1290.362 rows=10 loops=1)
  ->  Sort  (cost=122405.06..122509.00 rows=41574 width=1390) (actual time=1264.250..1289.575 rows=30010 loops=1)
        Sort Key: p.score, p.title
        Sort Method: external merge  Disk: 29656kB
        ->  Merge Join  (cost=50.46..94061.13 rows=41574 width=1390) (actual time=117.746..182.048 rows=41666 loops=1)
              Merge Cond: (p.id = c.product_id)
              ->  Index Scan using products_pkey on products p  (cost=0.42..90738.43 rows=646067 width=1390) (actual time=0.034..116.313 rows=210283 loops=1)
              ->  Index Only Scan using idx_prodcats_cat_prod_id on prodcats c  (cost=0.43..1187.98 rows=41574 width=4) (actual time=0.022..7.137 rows=41666 loops=1)
                    Index Cond: (category_id = 248688)
                    Heap Fetches: 0
Planning time: 0.873 ms
Execution time: 1294.826 ms

ยังไม่ได้ใช้ดัชนีต้นไม้ B ชุดwork_memผลลัพธ์ไม่พอดีดังนั้นผลลัพธ์ที่ไม่ดี

แต่ภายใต้สถานการณ์บางอย่างการมีผลิตภัณฑ์จำนวนมากและออฟเซ็ตออฟเซ็ตเล็ก ๆ ในตอนนี้ตัดสินใจใช้ดัชนีทรี B:

SELECT p.* FROM products p JOIN prodcats c ON (p.id=c.product_id)
WHERE c.category_id=248688
ORDER BY p.score DESC, p.title LIMIT 10 OFFSET 300;

Limit  (cost=3986.65..4119.51 rows=10 width=1390) (actual time=264.176..264.574 rows=10 loops=1)
  ->  Nested Loop  (cost=0.98..552334.77 rows=41574 width=1390) (actual time=250.378..264.558 rows=310 loops=1)
        ->  Index Scan using idx_test7 on products p  (cost=0.55..194665.62 rows=646067 width=1390) (actual time=0.030..83.026 rows=108037 loops=1)
        ->  Index Only Scan using idx_prodcats_cat_prod_id on prodcats c  (cost=0.43..0.54 rows=1 width=4) (actual time=0.001..0.001 rows=0 loops=108037)
              Index Cond: ((category_id = 248688) AND (product_id = p.id))
              Heap Fetches: 0
Planning time: 0.585 ms
Execution time: 264.664 ms

อันที่จริงแล้วค่อนข้างเป็นตรรกะเนื่องจากดัชนี B-tree ที่นี่ไม่ได้ให้ผลลัพธ์โดยตรง แต่จะใช้เป็นแนวทางในการสแกนตามลำดับเท่านั้น

ลองเปรียบเทียบกับแบบสอบถาม GIN:

SELECT * FROM products WHERE id in (
  SELECT id FROM products WHERE category_ids @> ARRAY[248688]
  ORDER BY score DESC, title LIMIT 10 OFFSET 300
) ORDER BY score DESC, title;

Sort  (cost=2519.53..2519.55 rows=10 width=1390) (actual time=143.809..143.809 rows=10 loops=1)
  Sort Key: products.score, products.title
  Sort Method: quicksort  Memory: 35kB
  ->  Nested Loop  (cost=2435.14..2519.36 rows=10 width=1390) (actual time=143.693..143.736 rows=10 loops=1)
        ->  HashAggregate  (cost=2434.71..2434.81 rows=10 width=4) (actual time=143.678..143.680 rows=10 loops=1)
              Group Key: products_1.id
              ->  Limit  (cost=2434.56..2434.59 rows=10 width=72) (actual time=143.668..143.670 rows=10 loops=1)
                    ->  Sort  (cost=2433.81..2435.43 rows=646 width=72) (actual time=143.642..143.653 rows=310 loops=1)
                          Sort Key: products_1.score, products_1.title
                          Sort Method: top-N heapsort  Memory: 68kB
                          ->  Bitmap Heap Scan on products products_1  (cost=17.01..2403.85 rows=646 width=72) (actual time=11.625..31.868 rows=41666 loops=1)
                                Recheck Cond: (category_ids @> '{248688}'::integer[])
                                Heap Blocks: exact=6471
                                ->  Bitmap Index Scan on idx_products_category_ids_gin  (cost=0.00..16.85 rows=646 width=0) (actual time=9.916..9.916 rows=41666 loops=1)
                                      Index Cond: (category_ids @> '{248688}'::integer[])
        ->  Index Scan using products_pkey on products  (cost=0.42..8.45 rows=1 width=1390) (actual time=0.004..0.004 rows=1 loops=10)
              Index Cond: (id = products_1.id)
Planning time: 0.630 ms
Execution time: 143.921 ms

ผลลัพธ์ของ GIN ดีกว่ามาก ฉันจะตรวจสอบกับชุดต่างๆของจำนวนของผลิตภัณฑ์และการชดเชยภายใต้วิธีการแยกตารางสถานการณ์ใด ๆ ที่ดีกว่า

พลังของดัชนีจริง

เพื่อให้ PostgreSQL ใช้ดัชนีอย่างเต็มที่สำหรับการเรียงลำดับWHEREพารามิเตอร์เคียวรีทั้งหมดรวมถึงORDER BYพารามิเตอร์จะต้องอยู่ในดัชนี B-tree เดียว เมื่อต้องการทำสิ่งนี้ฉันได้คัดลอกเขตข้อมูลเรียงลำดับจากผลิตภัณฑ์ไปยังตารางแยก:

CREATE TABLE prodcats AS SELECT id AS product_id, unnest(category_ids) AS category_id, score, title FROM products;
CREATE INDEX idx_prodcats_1 ON prodcats (category_id, score DESC, title, product_id);

SELECT * FROM products WHERE id in (SELECT product_id FROM prodcats WHERE category_id=248688 ORDER BY score DESC, title LIMIT 10 OFFSET 30000) ORDER BY score DESC, title;

Sort  (cost=2149.65..2149.67 rows=10 width=1390) (actual time=7.011..7.011 rows=10 loops=1)
  Sort Key: products.score, products.title
  Sort Method: quicksort  Memory: 35kB
  ->  Nested Loop  (cost=2065.26..2149.48 rows=10 width=1390) (actual time=6.916..6.950 rows=10 loops=1)
        ->  HashAggregate  (cost=2064.83..2064.93 rows=10 width=4) (actual time=6.902..6.904 rows=10 loops=1)
              Group Key: prodcats.product_id
              ->  Limit  (cost=2064.02..2064.71 rows=10 width=74) (actual time=6.893..6.895 rows=10 loops=1)
                    ->  Index Only Scan using idx_prodcats_1 on prodcats  (cost=0.56..2860.10 rows=41574 width=74) (actual time=0.010..6.173 rows=30010 loops=1)
                          Index Cond: (category_id = 248688)
                          Heap Fetches: 0
        ->  Index Scan using products_pkey on products  (cost=0.42..8.45 rows=1 width=1390) (actual time=0.003..0.003 rows=1 loops=10)
              Index Cond: (id = prodcats.product_id)
Planning time: 0.318 ms
Execution time: 7.066 ms

และนี่เป็นสถานการณ์ที่เลวร้ายที่สุดที่มีผลิตภัณฑ์จำนวนมากในหมวดหมู่ที่เลือกและออฟเซ็ตขนาดใหญ่ เมื่อ offset = 300 เวลาดำเนินการเพียง 0.5 ms

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

ดังนั้นฉันจึงยังคงอยู่กับดัชนี GIN จนถึงตอนนี้ด้วยการเพิ่มwork_memและลดการสืบค้นหน่วยความจำรอยเท้า


คุณไม่จำเป็นต้องรีสตาร์ทสำหรับการเปลี่ยนแปลงการwork_memตั้งค่าทั่วไปใน postgresql.conf โหลดซ้ำก็เพียงพอ และให้ฉันเตือนไม่ให้ตั้งค่าwork_memสูงเกินไปทั่วโลกในสภาพแวดล้อมที่มีผู้ใช้หลายคน (ไม่ต่ำเกินไป) ถ้าคุณมีบางคำสั่งที่ต้องการมากขึ้นwork_mem, ตั้งค่าที่สูงขึ้นสำหรับเซสชั่นเท่านั้นที่มีSET- SET LOCALหรือเพียงแค่ทำธุรกรรมกับ ดู: dba.stackexchange.com/a/48633/3684
Erwin Brandstetter

ช่างเป็นคำตอบที่ยอดเยี่ยม ช่วยฉันเป็นอย่างมากโดยเฉพาะกับดิสก์ -> การดำเนินการเรียงลำดับในหน่วยความจำการเปลี่ยนแปลงอย่างรวดเร็วเพื่อชัยชนะครั้งใหญ่ขอบคุณ!
Ricardo Villamil

4

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

1 work_mem

ดังนั้นฉันเห็นทันทีว่าการเรียงลำดับที่รายงานในแผนอธิบายของคุณSort Method: external merge Disk: 5696kBใช้เวลาน้อยกว่า 6 MB แต่กำลังรั่วไหลไปยังดิสก์ คุณต้องเพิ่มwork_memการตั้งค่าในpostgresql.confไฟล์ของคุณให้ดีพอที่การจัดเรียงจะพอดีกับหน่วยความจำ

แก้ไข:นอกจากนี้ในการตรวจสอบเพิ่มเติมฉันเห็นว่าหลังจากใช้ดัชนีเพื่อตรวจสอบcatgory_idsที่เหมาะกับเกณฑ์ของคุณสแกนบิตแมปดัชนีถูกบังคับให้กลายเป็น "สูญเสีย" และต้องตรวจสอบสภาพเมื่ออ่านแถวจากภายในหน้ากองที่เกี่ยวข้อง . อ้างถึงโพสต์นี้ใน postgresql.org สำหรับคำอธิบายที่ดีกว่าที่ฉันได้รับ : P ประเด็นหลักคือคุณwork_memต่ำเกินไป หากคุณไม่ได้ปรับการตั้งค่าเริ่มต้นบนเซิร์ฟเวอร์ของคุณจะไม่ทำงานได้ดี

การแก้ไขนี้จะทำให้คุณไม่มีเวลาทำ เปลี่ยนไปpostgresql.confแล้วคุณก็ออกไป! อ้างถึงหน้าการปรับแต่งประสิทธิภาพนี้สำหรับเคล็ดลับเพิ่มเติม

2. การเปลี่ยนแปลงโครงสร้าง

ดังนั้นคุณได้ตัดสินใจในการออกแบบสคีมาของคุณเพื่อทำให้category_idsอาเรย์เป็นจำนวนเต็มซึ่งทำให้คุณใช้ดัชนี GIN หรือ GIST เพื่อเข้าถึงอย่างรวดเร็ว จากประสบการณ์ของฉันการเลือกดัชนี GIN ของคุณจะเร็วกว่าการอ่านมากกว่า GIST ดังนั้นในกรณีนี้คุณเลือกถูกต้อง อย่างไรก็ตาม GIN เป็นดัชนีที่ไม่เรียงลำดับ คิดว่ามันมากขึ้นเช่นค่าคีย์ที่ภาคเท่าเทียมกันจะง่ายต่อการตรวจสอบ แต่การดำเนินการเช่นWHERE >, WHERE <หรือORDER BYไม่ได้รับการอำนวยความสะดวกโดยดัชนี

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

ในกรณีนี้คุณมีหลายประเภทและชุดของจำนวนเต็มที่สอดคล้องกันcategory_idและคุณมีผลิตภัณฑ์มากมายและproduct_ids ที่สอดคล้องกัน แทนที่จะเป็นคอลัมน์ในตารางผลิตภัณฑ์ของคุณซึ่งเป็นอาร์เรย์จำนวนเต็มcategory_ids ให้ลบคอลัมน์อาร์เรย์นี้ออกจากสคีมาของคุณและสร้างตารางเป็น

CREATE TABLE join_products_categories (product_id int, category_id int);

จากนั้นคุณสามารถสร้างดัชนี B-tree บนสองคอลัมน์ของตารางบริดจ์

CREATE INDEX idx_products_in_join_table ON join_products_categories (product_id);
CREATE INDEX idx_products_in_join_table ON join_products_categories (category_id);

เพียงแค่ความเห็นต่ำต้อยของฉัน แต่การเปลี่ยนแปลงเหล่านี้อาจสร้างความแตกต่างอย่างมากสำหรับคุณ ลองwork_memเปลี่ยนสิ่งแรกเลยอย่างน้อยที่สุด

ขอให้โชคดี!

แก้ไข:

สร้างดัชนีเพิ่มเติมเพื่อช่วยในการจัดเรียง

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

ดูอย่างเป็นทางการ PostgreSQL เอกสารอธิบายดัชนีและ ORDER BY

ถ้าคุณสร้างดัชนีการจับคู่ของคุณORDER BYreqirements

CREATE INDEX idx_product_sort ON products (score DESC, title);

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

ถึงตอนนี้จุดสำคัญของการ 'นำเข้าที่ใหญ่ที่สุดสำหรับเจ้าชู้' ของคุณอาจจะใช้งานได้มากกว่าwork_memแต่ก็มีหลายกรณีที่ดัชนีสามารถรองรับการเรียงลำดับ


ฉันยังคิดที่จะใช้ตารางแยกเพื่อหลีกเลี่ยง GIN แต่คุณไม่ได้ระบุวิธีที่จะช่วยในการจัดเรียง ฉันคิดว่ามันจะไม่ช่วย ฉันลองเข้าร่วมตารางผลิตภัณฑ์กับชุดรหัสผลิตภัณฑ์ที่รวบรวมผ่านแบบสอบถาม GIN ซึ่งฉันเชื่อว่าค่อนข้างคล้ายกับการเข้าร่วมที่คุณเสนอและการดำเนินการนั้นไม่สามารถใช้ดัชนีต้นไม้แบบ b กับคะแนนและชื่อเรื่องได้ บางทีฉันอาจสร้างดัชนีผิด คุณช่วยอธิบายเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ไหม
Yaroslav Stavnichiy

ขออภัยบางทีฉันไม่ได้อธิบายอย่างชัดเจนเพียงพอ การเปลี่ยนแปลงการwork_memกำหนดค่าของคุณมีวัตถุประสงค์เพื่อแก้ไขปัญหา 'การเรียงลำดับบนดิสก์' ของคุณเช่นเดียวกับปัญหาเงื่อนไขการตรวจสอบซ้ำของคุณ เมื่อจำนวนผลิตภัณฑ์เพิ่มขึ้นคุณอาจต้องมีดัชนีเพิ่มเติมเพื่อจัดเรียง โปรดดูการแก้ไขของฉันด้านบนเพื่อความกระจ่าง
Chris
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.