Contenu connexe Similaire à PostgreSQL:行数推定を読み解く (20) PostgreSQL:行数推定を読み解く10. 実行計画をみてみる
Sort (cost=582.43..582.44 rows=1 width=312) (actual time=3112.054..3113.061 rows=5579 loops=1)
Sort Key: [略]
Sort Method: quicksort Memory: 1674kB
-> Nested Loop (cost=2.59..582.42 rows=1 width=312) (actual time=548.120..3088.081 rows=5579 loops=1)
-> Nested Loop (cost=2.16..581.58 rows=1 width=297) (actual time=548.032..3008.849 rows=5579 loops=1)
-> Nested Loop (cost=1.73..573.12 rows=1 width=308) (actual time=548.020..2980.460 rows=5579 loops=1)
Join Filter: [略]
-> Nested Loop (cost=1.29..570.17 rows=1 width=256) (actual time=544.342..2929.895 rows=6286 loops=1)
-> Nested Loop (cost=0.86..456.48 rows=85 width=245) (actual time=0.054..1044.752 rows=473464 loops=1)
-> Index Scan using idx_[略] on user_table (cost=0.43..8.46 rows=1 width=70) (actual time=0.031..0.139 rows=115 loops=1)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
-> Index Scan using idx_[略] (cost=0.43..446.91 rows=111 width=191) (actual time=0.015..4.347 rows=4117 loops=115)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..1.33 rows=1 width=22) (actual time=0.003..0.003 rows=0 loops=473464)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..2.92 rows=1 width=84) (actual time=0.006..0.006 rows=1 loops=6286)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 0
-> Index Scan using [略] (cost=0.43..8.45 rows=1 width=11) (actual time=0.004..0.004 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
-> Index Scan using [略] (cost=0.43..0.61 rows=1 width=42) (actual time=0.004..0.005 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
Total runtime: 3114.997 ms
11. 実行計画をみてみる
Sort (cost=582.43..582.44 rows=1 width=312) (actual time=3112.054..3113.061 rows=5579 loops=1)
Sort Key: [略]
Sort Method: quicksort Memory: 1674kB
-> Nested Loop (cost=2.59..582.42 rows=1 width=312) (actual time=548.120..3088.081 rows=5579 loops=1)
-> Nested Loop (cost=2.16..581.58 rows=1 width=297) (actual time=548.032..3008.849 rows=5579 loops=1)
-> Nested Loop (cost=1.73..573.12 rows=1 width=308) (actual time=548.020..2980.460 rows=5579 loops=1)
Join Filter: [略]
-> Nested Loop (cost=1.29..570.17 rows=1 width=256) (actual time=544.342..2929.895 rows=6286 loops=1)
-> Nested Loop (cost=0.86..456.48 rows=85 width=245) (actual time=0.054..1044.752 rows=473464 loops=1)
-> Index Scan using idx_[略] on user_table (cost=0.43..8.46 rows=1 width=70) (actual time=0.031..0.139 rows=115 loops=1)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
-> Index Scan using idx_[略] (cost=0.43..446.91 rows=111 width=191) (actual time=0.015..4.347 rows=4117 loops=115)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..1.33 rows=1 width=22) (actual time=0.003..0.003 rows=0 loops=473464)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..2.92 rows=1 width=84) (actual time=0.006..0.006 rows=1 loops=6286)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 0
-> Index Scan using [略] (cost=0.43..8.45 rows=1 width=11) (actual time=0.004..0.004 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
-> Index Scan using [略] (cost=0.43..0.61 rows=1 width=42) (actual time=0.004..0.005 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
Total runtime: 3114.997 ms
?
12. 実行計画をみてみる
Sort (cost=582.43..582.44 rows=1 width=312) (actual time=3112.054..3113.061 rows=5579 loops=1)
Sort Key: [略]
Sort Method: quicksort Memory: 1674kB
-> Nested Loop (cost=2.59..582.42 rows=1 width=312) (actual time=548.120..3088.081 rows=5579 loops=1)
-> Nested Loop (cost=2.16..581.58 rows=1 width=297) (actual time=548.032..3008.849 rows=5579 loops=1)
-> Nested Loop (cost=1.73..573.12 rows=1 width=308) (actual time=548.020..2980.460 rows=5579 loops=1)
Join Filter: [略]
-> Nested Loop (cost=1.29..570.17 rows=1 width=256) (actual time=544.342..2929.895 rows=6286 loops=1)
-> Nested Loop (cost=0.86..456.48 rows=85 width=245) (actual time=0.054..1044.752 rows=473464 loops=1)
-> Index Scan using idx_[略] on user_table (cost=0.43..8.46 rows=1 width=70) (actual time=0.031..0.139 rows=115 loops=1)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
-> Index Scan using idx_[略] (cost=0.43..446.91 rows=111 width=191) (actual time=0.015..4.347 rows=4117 loops=115)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..1.33 rows=1 width=22) (actual time=0.003..0.003 rows=0 loops=473464)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..2.92 rows=1 width=84) (actual time=0.006..0.006 rows=1 loops=6286)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 0
-> Index Scan using [略] (cost=0.43..8.45 rows=1 width=11) (actual time=0.004..0.004 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
-> Index Scan using [略] (cost=0.43..0.61 rows=1 width=42) (actual time=0.004..0.005 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
Total runtime: 3114.997 ms
-> Nested Loop
(cost=0.86..456.48 rows=85 width=245)
(actual time=0.054..1044.752 rows=473464 loops=1)
13. 実行計画をみてみる
Sort (cost=582.43..582.44 rows=1 width=312) (actual time=3112.054..3113.061 rows=5579 loops=1)
Sort Key: [略]
Sort Method: quicksort Memory: 1674kB
-> Nested Loop (cost=2.59..582.42 rows=1 width=312) (actual time=548.120..3088.081 rows=5579 loops=1)
-> Nested Loop (cost=2.16..581.58 rows=1 width=297) (actual time=548.032..3008.849 rows=5579 loops=1)
-> Nested Loop (cost=1.73..573.12 rows=1 width=308) (actual time=548.020..2980.460 rows=5579 loops=1)
Join Filter: [略]
-> Nested Loop (cost=1.29..570.17 rows=1 width=256) (actual time=544.342..2929.895 rows=6286 loops=1)
-> Nested Loop (cost=0.86..456.48 rows=85 width=245) (actual time=0.054..1044.752 rows=473464 loops=1)
-> Index Scan using idx_[略] on user_table (cost=0.43..8.46 rows=1 width=70) (actual time=0.031..0.139 rows=115 loops=1)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
-> Index Scan using idx_[略] (cost=0.43..446.91 rows=111 width=191) (actual time=0.015..4.347 rows=4117 loops=115)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..1.33 rows=1 width=22) (actual time=0.003..0.003 rows=0 loops=473464)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..2.92 rows=1 width=84) (actual time=0.006..0.006 rows=1 loops=6286)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 0
-> Index Scan using [略] (cost=0.43..8.45 rows=1 width=11) (actual time=0.004..0.004 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
-> Index Scan using [略] (cost=0.43..0.61 rows=1 width=42) (actual time=0.004..0.005 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
Total runtime: 3114.997 ms
14. 実行計画をみてみる
Sort (cost=582.43..582.44 rows=1 width=312) (actual time=3112.054..3113.061 rows=5579 loops=1)
Sort Key: [略]
Sort Method: quicksort Memory: 1674kB
-> Nested Loop (cost=2.59..582.42 rows=1 width=312) (actual time=548.120..3088.081 rows=5579 loops=1)
-> Nested Loop (cost=2.16..581.58 rows=1 width=297) (actual time=548.032..3008.849 rows=5579 loops=1)
-> Nested Loop (cost=1.73..573.12 rows=1 width=308) (actual time=548.020..2980.460 rows=5579 loops=1)
Join Filter: [略]
-> Nested Loop (cost=1.29..570.17 rows=1 width=256) (actual time=544.342..2929.895 rows=6286 loops=1)
-> Nested Loop (cost=0.86..456.48 rows=85 width=245) (actual time=0.054..1044.752 rows=473464 loops=1)
-> Index Scan using idx_[略] on user_table (cost=0.43..8.46 rows=1 width=70) (actual time=0.031..0.139 rows=115 loops=1)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
-> Index Scan using idx_[略] (cost=0.43..446.91 rows=111 width=191) (actual time=0.015..4.347 rows=4117 loops=115)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..1.33 rows=1 width=22) (actual time=0.003..0.003 rows=0 loops=473464)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..2.92 rows=1 width=84) (actual time=0.006..0.006 rows=1 loops=6286)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 0
-> Index Scan using [略] (cost=0.43..8.45 rows=1 width=11) (actual time=0.004..0.004 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
-> Index Scan using [略] (cost=0.43..0.61 rows=1 width=42) (actual time=0.004..0.005 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
Total runtime: 3114.997 ms
-> Index Scan using idx_[略] on user_table
(cost=0.43..8.46 rows=1 width=70)
(actual time=0.031..0.139 rows=115 loops=1)
Index Cond:
(((company_id)::text = 'sansan'::text)
AND ((user_id)::text = 'kabata'::text))
27. テーブル単位の行数
● pg_class の reltuples カラムに保持される ※1。
● VACUUM, ANALYZE, CREATE INDEX 等で更新される。
relname reltuples
user_table 1424335
SELECT relname, reltuples FROM pg_class
WHERE relname = ‘user_table’;
※1 VACUUM, ANALYZE等ではテーブル全体を操作しないため、概算値が保持される。
28. カラム単位の最頻値・最頻度と度数分布
● pg_statistic に Array型で格納される。※1
● ANALYZE, VACUUM ANALYZE で更新される。
※1 Arrayの最大長(=保持する最大数)をカラムごとまたはグローバルに設定できる(default:100)。
※2 pg_statistic はシステムカタログであり読みづらいため、参照する際はpg_stats ビューを利用する。
most_common_vals most_common_freqs histogram_bounds
{yontendo, gaagle, yanoo,... {0.1349666714668274,0.130199998
61717224,0.08696666359901428,...
{twitto, bacefook, linkedout, ….
SELECT most_common_vals, most_common_freqs, histogram_bounds
FROM pg_stats ※2
WHERE tablename = 'user_table' and attname = 'company_id';
34. 行数推定のアルゴリズム:条件なし
SELECT * FROM user_table;
Seq Scan on user_table
(cost=0.00..84330.35 rows=1424335 width=355)
(actual time=0.041..749.988 rows=1424450 loops=1)
1. 選択度は1となる。
2. user_tableの濃度を推定行数とする。
36. 行数推定のアルゴリズム:範囲条件 ※1
SELECT * FROM user_table
WHERE crttimestamp < ‘2015-10-10 10:10:10’;
※1 本当はもう少し複雑。 https://www.postgresql.jp/document/9.5/html/row-estimation-examples.html
※2 pg_operator の oprrest カラムに保持されている。今回はscalarltsel関数。
1. 選択度関数を決定する※2。
2. pg_statistic から user_id の度数分布を取り出す。
3. 「<」で占められる度数分布率を選択度とする。
4. user_tableの濃度と選択度の積を推定行数とする。
37. column < c で占められる度数分布率
frequency = (1 + (c - bucket[ ic
].min) / (bucket[ ic
].max - bucket[ ic
].min))
/ bucket.length
0 8616 32 48 64 70
bucket[1]
bucket[2]
bucket[2].min bucket[2].min
例:対象の値 c = 20 、度数分布が右図の場合、
度数分布率は以下で計算される。
frequency = (1 + (20 - 16) / (32 -16)) / 6
≒ 0.21
※各バケット内での線形分布を仮定。
38. 行数推定のアルゴリズム:範囲条件
SELECT * FROM user_table
WHERE crttimestamp < ‘2015-10-10 10:10:10’;
Seq Scan on user_table
(cost=0.00..87891.19 rows=508065 width=355)
(actual time=0.015..266.237 rows=503049 loops=1)
Filter: (crttimestamp < '2015-10-10 10:10:10+09'::timestamp with time zone)
Rows Removed by Filter: 921401
40. 行数推定のアルゴリズム:一致条件
SELECT * FROM user_table
WHERE company_id = ‘sansan’;
1. 選択度関数を決定する※1。
2. “sansan” という値が最頻値に含まれるか調べる。
3. 以下により選択度を決定する。
a. 最頻値に含まれる場合 :最頻度を選択度とする。
b. 最頻値に含まれない場合: がんばってしぼりだす 。
4. user_tableの濃度と選択度の積を推定行数とする。
※1 今回は eqsel関数。
41. 最頻値に含まれない値の選択度
selectivity = (1 - sum(mvf)) / (num_distinct - num_mcv)
例:対象のカラムのとりうる値が 100通りで、MCV・MCFが
右表の場合、”sansan” の選択度は以下で計算される。
selectivity = (1 - (0.32 + 0.18 + 0.09 + 0.04 + 0.01))
/ (100 - 5)
≒ 0.0038
※MCV以外の列の一様分布を仮定。
MCV MCF
yontendo 0.32
yanoo 0.18
gaagle 0.09
hitaz 0.04
basefook 0.01
42. 行数推定のアルゴリズム:一致条件
SELECT * FROM user_table
WHERE company_id = ‘sansan’;
Bitmap Heap Scan on user_table
(cost=2836.15..77415.72 rows=108867 width=355)
(actual time=19.782..68.394 rows=109436 loops=1)
Index Cond: ((company_id)::text = 'sansan'::text)
44. 行数推定のアルゴリズム:複合条件
SELECT * FROM user_table
WHERE company_id = ‘sansan’ AND user_id = ‘kabata’
1. 各条件の選択度を計算する。
2. user_tableの濃度、各条件の選択度の積を推定行数とする。
※各条件は独立していると仮定。
45. 行数推定のアルゴリズム:複合条件
SELECT * FROM user_table
WHERE company_id = ‘sansan’ AND user_id = ‘kabata’
Index Scan using user_table_pkey on user_table
(cost=0.43..8.46 rows=1 width=70)
(actual time=0.031..0.139 rows=115 loops=1)
Index Cond: (((company_id)::text = 'sansan'::text)
AND ((user_id)::text = 'kabata'::text))
46. 行数推定のアルゴリズム:まとめ
選択度(selectivity)
● 条件なし : 1
● 範囲条件 : (1 + (c - b[ ic
].min) / (b[ ic
].max - b[ ic
].min) / b.length
● 一致条件
○ 最頻値に含まれる : mcfc
○ 最頻値に含まれない : (1 - sum(mcf)) / (num_distinct - num_mcv)
● 複合条件 : selectivity(a) * selectivity(b)
推定行数
rows = rel_cardinarity * selectivity
48. 実行計画をみてみる(再掲)
Sort (cost=582.43..582.44 rows=1 width=312) (actual time=3112.054..3113.061 rows=5579 loops=1)
Sort Key: [略]
Sort Method: quicksort Memory: 1674kB
-> Nested Loop (cost=2.59..582.42 rows=1 width=312) (actual time=548.120..3088.081 rows=5579 loops=1)
-> Nested Loop (cost=2.16..581.58 rows=1 width=297) (actual time=548.032..3008.849 rows=5579 loops=1)
-> Nested Loop (cost=1.73..573.12 rows=1 width=308) (actual time=548.020..2980.460 rows=5579 loops=1)
Join Filter: [略]
-> Nested Loop (cost=1.29..570.17 rows=1 width=256) (actual time=544.342..2929.895 rows=6286 loops=1)
-> Nested Loop (cost=0.86..456.48 rows=85 width=245) (actual time=0.054..1044.752 rows=473464 loops=1)
-> Index Scan using idx_[略] on user_table (cost=0.43..8.46 rows=1 width=70) (actual time=0.031..0.139 rows=115 loops=1)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
-> Index Scan using idx_[略] (cost=0.43..446.91 rows=111 width=191) (actual time=0.015..4.347 rows=4117 loops=115)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..1.33 rows=1 width=22) (actual time=0.003..0.003 rows=0 loops=473464)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..2.92 rows=1 width=84) (actual time=0.006..0.006 rows=1 loops=6286)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 0
-> Index Scan using [略] (cost=0.43..8.45 rows=1 width=11) (actual time=0.004..0.004 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
-> Index Scan using [略] (cost=0.43..0.61 rows=1 width=42) (actual time=0.004..0.005 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
Total runtime: 3114.997 ms
-> Index Scan using idx_[略] on user_table
(cost=0.43..8.46 rows=1 width=70)
(actual time=0.031..0.139 rows=115 loops=1)
Index Cond:
(((company_id)::text = 'sansan'::text)
AND ((user_id)::text = 'kabata'::text))
49. 実行計画をみてみる(再掲)
Sort (cost=582.43..582.44 rows=1 width=312) (actual time=3112.054..3113.061 rows=5579 loops=1)
Sort Key: [略]
Sort Method: quicksort Memory: 1674kB
-> Nested Loop (cost=2.59..582.42 rows=1 width=312) (actual time=548.120..3088.081 rows=5579 loops=1)
-> Nested Loop (cost=2.16..581.58 rows=1 width=297) (actual time=548.032..3008.849 rows=5579 loops=1)
-> Nested Loop (cost=1.73..573.12 rows=1 width=308) (actual time=548.020..2980.460 rows=5579 loops=1)
Join Filter: [略]
-> Nested Loop (cost=1.29..570.17 rows=1 width=256) (actual time=544.342..2929.895 rows=6286 loops=1)
-> Nested Loop (cost=0.86..456.48 rows=85 width=245) (actual time=0.054..1044.752 rows=473464 loops=1)
-> Index Scan using idx_[略] on user_table (cost=0.43..8.46 rows=1 width=70) (actual time=0.031..0.139 rows=115 loops=1)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
-> Index Scan using idx_[略] (cost=0.43..446.91 rows=111 width=191) (actual time=0.015..4.347 rows=4117 loops=115)
Index Cond: (((company_id)::text = 'sansan'::text) AND ((user_id)::text = 'kabata'::text))
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..1.33 rows=1 width=22) (actual time=0.003..0.003 rows=0 loops=473464)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 1
-> Index Scan using [略] (cost=0.43..2.92 rows=1 width=84) (actual time=0.006..0.006 rows=1 loops=6286)
Index Cond: [略]
Filter: [略]
Rows Removed by Filter: 0
-> Index Scan using [略] (cost=0.43..8.45 rows=1 width=11) (actual time=0.004..0.004 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
-> Index Scan using [略] (cost=0.43..0.61 rows=1 width=42) (actual time=0.004..0.005 rows=1 loops=5579)
Index Cond: [略]
Filter: [略]
Total runtime: 3114.997 ms
-> Index Scan using idx_[略] on user_table
(cost=0.43..8.46 rows=1 width=70)
(actual time=0.031..0.139 rows=115 loops=1)
Index Cond:
(((company_id)::text = 'sansan'::text)
AND ((user_id)::text = 'kabata'::text))
一致条件 + 一致条件 の複合条件
55. 各条件による実際の行数を取得する
SELECT count(*) FROM user_table
WHERE company_id = ‘sansan’ and user_id = ‘kabata’;
SELECT count(*) FROM user_table
WHERE company_id = ‘sansan’;
SELECT count(*) FROM user_table
WHERE user_id = ‘kabata’;
56. 各条件による実際の行数を取得する
SELECT count(*) FROM user_table
WHERE company_id = ‘sansan’ and user_id = ‘kabata’;
SELECT count(*) FROM user_table
WHERE company_id = ‘sansan’;
SELECT count(*) FROM user_table
WHERE user_id = ‘kabata’;
322
109436
322
76. 補足:pg_statistic からMCV/MCFを取得するSQL
SELECT * FROM pg_statistic s
JOIN pg_class c ON (c.oid = s.starelid)
JOIN pg_attribute a
ON (c.oid = attrelid AND attnum = s.staattnum)
LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
WHERE relname = ‘user_table’ AND attname = ‘user_id’
AND stakind1 = 1;