③プランの作成 ④実行 SELECT id, name FROM users WHERE org_id = 123; id | name ----+------- 1 | Alice 3 | Beth 7 | Emily Plan tree Query tree Query Result Tweaked Query tree
④実行 SELECT id, name FROM users WHERE org_id = 123; id | name ----+------- 1 | Alice 3 | Beth 7 | Emily Plan tree Query tree Query Result Tweaked Query tree Plan tree 1 Plan tree 2 Plan tree 3
structure and the properties of the data is absolutely critical for good performance, so the system includes a complex planner that tries to choose good plans. クエリに合った最適なプランを選ぶことはクエリ最適化には必須 で、Plannerは頑張っ て最適なものを 選ぼうとしている 。 PostgreSQL Documentation: Using EXPLAIN 📖-1 7
AND ALL CHILDREN rows=THE REAL ROW COUNT loops=1) -> Seq Scan on something (actual time=THE TIME IT REALLY TOOK rows=THE REAL ROW COUNT loops=1) -> Index Scan using someidx on somethingelse (actual time=NOT REALLY HOW LONG IT TOOK rows=NOT REALLY HOW MANY ROWS WE GOT loops=HUGE NUMBER) 19 From Postgres mailing list: “explain analyze rows=%.0f” (Robert Haas)
FROM procurement_notices WHERE deadline_date < '2024-01-01'; QUERY PLAN -------------------------------------------------------------------------------------------------------------------- ---------- Seq Scan on procurement_notices (cost=0.00..30206.38 rows=257720 width=444) (actual time=0.270..76.563 rows=258386 loops=1) Filter: (deadline_date < '2024-01-01'::date) Rows Removed by Filter: 53164 Buffers: shared hit=110 read=26202 I/O Timings: shared read=31.352 Planning Time: 0.155 ms Execution Time: 85.389 ms (7 rows) 20 Data source: Procurement Notice from World Bank Open Data 🗄-2
SELECT * FROM tenk1 t1, tenk2 t2 WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2; QUERY PLAN ---------------------------------------------------------------------------------------- Nested Loop (cost=0.57..123.58 rows=10 width=488) -> Index Scan using tenk1_unique1 on tenk1 t1 (cost=0.29..44.46 rows=10 width=244) Index Cond: (unique1 < 10) -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.90 rows=1 width=244) Index Cond: (unique2 = t1.unique2) (5 rows) 35
SELECT * FROM tenk1 t1, tenk2 t2 WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2; QUERY PLAN ---------------------------------------------------------------------------------------- Nested Loop (cost=0.57..123.58 rows=10 width=488) -> Index Scan using tenk1_unique1 on tenk1 t1 (cost=0.29..44.46 rows=10 width=244) Index Cond: (unique1 < 10) -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.90 rows=1 width=244) Index Cond: (unique2 = t1.unique2) (5 rows) 36 ① tenk1から 10未満のものを Index Scanで探す
SELECT * FROM tenk1 t1, tenk2 t2 WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2; QUERY PLAN ---------------------------------------------------------------------------------------- Nested Loop (cost=0.57..123.58 rows=10 width=488) -> Index Scan using tenk1_unique1 on tenk1 t1 (cost=0.29..44.46 rows=10 width=244) Index Cond: (unique1 < 10) -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.29..7.90 rows=1 width=244) Index Cond: (unique2 = t1.unique2) (5 rows) 37 ② tenk1とtenk2を Nested Loopで結合する
WHERE user_id = 1; QUERY PLAN ------------------------------------------------------------------------------------------------------------------- Aggregate (cost=5.45..5.46 rows=1 width=8) (actual time=0.293..0.295 rows=1 loops=1) -> Index Only Scan using orders_user_id_idx on orders (cost=0.43..5.33 rows=51 width=0) (actual time=0.255..0.272 rows=47 loops=1) Index Cond: (user_id = 1) Heap Fetches: 0 Planning Time: 0.827 ms Execution Time: 0.418 ms (6 rows) CREATE INDEX ON orders(user_id); selectivityのよいWHERE句 💡インデックスの効果が最大限に利用される
12からCTEがデフォルトでマテリアライズ化されていないことについて例も含めて詳しく解説して ある。 ◦ [JA] https://www.sraoss.co.jp/tech-blog/pgsql/pg12-cte/ 8. Tracking Postgres Buffer Cache Statistics over time with pganalyze ◦ pg_buffercacheエクステンションを使ってバッファの使用状況の推移が見れる機能を紹介している。 ◦ [EN] https://pganalyze.com/blog/tracking-postgres-buffer-cache-statistics 9. Postgres Planner Quirks: The impact of ORDER BY + LIMIT on index usage ◦ ORDER BY + LIMITの罠について実例も交えて詳しく解説してある。 ◦ [EN] https://pganalyze.com/blog/5mins-postgres-planner-order-by-limit 76
Alice 123 2 Amber 125 3 Beth 123 id name 123 Apple 124 Banana 125 Orange userid username orgname 1 Alice Apple 2 Amber Orange 3 Beth Apple users orgs SELECT … FROM users u JOIN orgs o ON u.org_id = o.id;
FROM small_table JOIN large_table ON small_table.id = large_table.some_id; (index on large_table.id) Merge Join SELECT * FROM large_table1 JOIN large_table2 ON large_table1.created_at > large_table2.last_seen; (tables are sorted by join keys or have indexes on join keys) Hash Join SELECT * FROM smaller_table JOIN larger_table ON smaller_table.id = larger_table.some_id; (equi-join) 83 Join Nodes: 2つの子ノードを結合して行を取得する Internals of physical join operators 📹-4