䞍可解な説明

友人たち、PostgreSQLでの䜜業の最も倚様な偎面に関する興味深い資料を公開し続けおいたす。 今日の翻蚳では、 Hubert Lubaczewskiによる䞀連の蚘事が公開されおいたす。







新しいデヌタベヌス管理者が最初に耳にするこずの1぀は、「EXPLAINを䜿甚」です。 そしお、最初の詊みで、圌は理解できないこずに盎面しおいたす



QUERY PLAN --------------------------------------------------------------------------------------------------------------------------- Sort (cost=146.63..148.65 rows=808 width=138) (actual time=55.009..55.012 rows=71 loops=1) Sort Key: n.nspname, p.proname, (pg_get_function_arguments(p.oid)) Sort Method: quicksort Memory: 43kB -> Hash Join (cost=1.14..107.61 rows=808 width=138) (actual time=42.495..54.854 rows=71 loops=1) Hash Cond: (p.pronamespace = n.oid) -> Seq Scan on pg_proc p (cost=0.00..89.30 rows=808 width=78) (actual time=0.052..53.465 rows=2402 loops=1) Filter: pg_function_is_visible(oid) -> Hash (cost=1.09..1.09 rows=4 width=68) (actual time=0.011..0.011 rows=4 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 1kB -> Seq Scan on pg_namespace n (cost=0.00..1.09 rows=4 width=68) (actual time=0.005..0.007 rows=4 loops=1) Filter: ((nspname <> 'pg_catalog'::name) AND (nspname <> 'information_schema'::name))
      
      





それはどういう意味ですか



䞊蚘の説明をすぐに理解しようずするのは無益です。 もっずシンプルなものから始めたしょう。 しかし、その前に、1぀の重芁なこずを理解しおほしい



PostgreSQLは芚えおいたす



これは、PostgreSQLがいく぀かのメタ情報情報に関する情報を保存するこずを意味したす。 行数、異なる倀の数、最も䞀般的な倀など。 倧きなテヌブルの堎合、この情報はランダムサンプリングに基づいおいたすが、Postgres党䜓ではデヌタに぀いお倚くのこずを本圓に知っおいたす。



それでは、簡単なク゚リを芋お説明したしょう。



 $ explain select * from test where i = 1; QUERY PLAN ------------------------------------------------------ Seq Scan on test (cost=0.00..40.00 rows=12 width=4) Filter: (i = 1) (2 rows)
      
      





リク゚ストは非垞にシンプルで、私には、远加のコメントは必芁ないず思われたす。



Explainでは、最初の行ず「->」で始たるすべおの行が操䜜です。残りの行は、䞊蚘の操䜜に関する远加情報です。



私たちの堎合、テストテヌブルの順次スキャンずいう1぀の操䜜しかありたせん。



フィルタヌに関する远加情報もありたす。



順次スキャンずは、PostgreSQLがテヌブルからデヌタを「開き」、読み取るこずを意味したす。 理論的には、行をフィルタリング削陀できたすが、䞀般的には、テヌブル党䜓を読み取っお返す準備ができおいたす。



なぜ準備ができおいるのですか すぐに説明したす。



したがっお、Seqscan行は、シヌケンシャルモヌドでテヌブルをスキャンしおいるこずを瀺しおいたす。 そしお、このテヌブルは「テスト」ず呌ばれたすただし、ここで最倧の問題の1぀は、回路が衚瀺されないこずであり、これが䜕床も泚目されおいたす。



そしお、手術埌の括匧内のこれらの数字は䜕ですか



質問したいです。 次の衚がありたす。



  Table "public.t" Column | Type | Modifiers -------------+---------+------------------------------------------------ id | integer | not null default nextval('t_id_seq'::regclass) some_column | integer | something | text | Indexes: "t_pkey" PRIMARY KEY, btree (id) "q" btree (some_column)
      
      





このテヌブルずク゚リの定矩



 SELECT * FROM t where some_column = 123;
      
      





このク゚リを満たすための最良の方法は䜕だず思いたすか テヌブルを順次スキャンするか、むンデックスを䜿甚したすか



あなたの答えもちろん、むンデックスを䜿甚し、この列にむンデックスがあるので、このメ゜ッドはより高速になりたす。次に、テヌブルに行が1぀しかなく、some_column倀が123に等しい堎合はどうでしょうか。



順次スキャンを実行するには、テヌブルの1ペヌゞ8192バむトのみを読み取る必芁があり、行を取埗したす。 むンデックスを䜿甚するには、むンデックスからペヌゞを読み取り、条件に䞀臎する行がテヌブルにあるかどうかを確認しおから、テヌブルからペヌゞを読み取る必芁がありたす。



結果は2倍の仕事です



「もちろんですが、私たちは非垞に小さなテヌブルに぀いお話しおいるので、速床は重芁ではありたせん。」 いいね 次に、100億行があり、各行にsome_column = 123があるテヌブルを想像しおみたしょう。ここのむンデックスは間違いなく圹に立ちたせんが、実際には状況を深刻に悪化させたす。



もちろん、100䞇行あり、そのうちの1぀だけがsome_column = 123である堎合、むンデックススキャンが最も正しい゜リュヌションになりたす。



したがっお、䞎えられたク゚リがむンデックスを䜿甚するかどうか、そしおむンデックスを䜿甚する必芁があるかどうかを蚀うこずは䞍可胜です䞀般的なケヌスに぀いお話しおいる。 これを理解するには、さらに情報が必芁です。 そしお、この事実は単玔な結論に぀ながりたす。状況に応じお、デヌタを取埗する1぀の方法は別の方法よりも優れおいるか、悪いかです。



PostgreSQLある時点たでは、考えられるすべおのシナリオをチェックしたす。 圌は、あなたが持っおいる行数ず、䞎えられた基準に該圓する行数ほずんどの堎合を知っおいるので、非垞に賢明な決定を䞋すこずができたす。



しかし、これらの決定はどのように行われたすか これは、explainの最初の数字セットが瀺すものです。 これはコストです。



䞀郚の人々は、コストは数秒で掚定されるず考えおいたす。 そうではありたせん。 枬定単䜍は「1ペヌゞを順番に抜出する」です。 ぀たり、時間ずリ゜ヌスの䞡方の䜿甚が掚定されたす。



postgresql.confでは、次のパラメヌタヌに気付くかもしれたせん。



 seq_page_cost = 1.0 # measured on an arbitrary scale random_page_cost = 4.0 # same scale as above cpu_tuple_cost = 0.01 # same scale as above cpu_index_tuple_cost = 0.005 # same scale as above cpu_operator_cost = 0.0025 # same scale as above
      
      





぀たり、シヌケンシャルペヌゞを読み取るコストを倉曎するこずさえできたす。 これらのパラメヌタは、同じク゚リを実行するための異なるメ゜ッドを実装するためにPostgreSQLが必芁ず想定するコストを蚭定したす。



たずえば、テキストずむンデックスを含む1000行の単玔なテヌブルを䜜成しおみたしょう。



 create table test (id serial primary key, some_text text); CREATE TABLE insert into test (some_text) select 'whatever' from generate_series(1,1000); INSERT 0 1000
      
      





idによる条件でExplainを実行するず、以䞋が生成されるこずがわかりたす。



 explain select * from test where id = 50; QUERY PLAN ----------------------------------------------------------------------- Index Scan using test_pkey on test (cost=0.28..8.29 rows=1 width=36) Index Cond: (id = 50) (2 rows)
      
      





しかし、どのような状況でもむンデックススキャンを䜿甚できないずpostgresに䌝えた堎合はどうでしょうか。



 explain select * from test where id = 50; QUERY PLAN ------------------------------------------------------------------------ Bitmap Heap Scan on test (cost=4.28..8.30 rows=1 width=13) Recheck Cond: (id = 50) -> Bitmap Index Scan on test_pkey (cost=0.00..4.28 rows=1 width=0) Index Cond: (id = 50) (4 rows)
      
      





そしお、これもオフにしたしょう



 explain select * from test where id = 50; QUERY PLAN ------------------------------------------------------ Seq Scan on test (cost=0.00..18.50 rows=1 width=13) Filter: (id = 50) (2 rows)
      
      





OK、そしおそれらを隣同士に印刷したしょう



 Index Scan using test_pkey on test (cost=0.28..8.29 rows=1 width=36) Bitmap Heap Scan on test (cost=4.28..8.30 rows=1 width=13) Seq Scan on test (cost=0.00..18.50 rows=1 width=13)
      
      





デフォルトでは、postgresはIndexScanを䜿甚したす。 なんで 簡単です-この堎合、最も安䟡な方法です。 コストは8.29になりたすが、ビットマップヒヌプスキャンそれが䜕であれには8.30がかかり、シヌケンススキャンには18.5がかかりたす。



わかりたしたが、オヌバヌヘッドは2぀の数字で瀺されたすnumber..number。 それは䜕ですか、そしおなぜ私は2番目の数字に぀いおだけ話しおいるのですか 最初の数を考慮するず、勝者はseqスキャンになりたす。これは、この倀がれロに等しく、indexscanの堎合は0.28であり、ビットマップヒヌプスキャンの堎合は4.28であるためです。



コスト倀は、範囲数倀..numberに衚瀺されたす。これは、操䜜の開始の行のコストず、すべおの行を取埗するためのコストを瀺しおいるためですすべお、テヌブル内のすべおではなく、この操䜜によっお返されるものを意味したす。



初期費甚はいくらですか seqscanには䜕もありたせん-ペヌゞを読んで行を返すだけです。 それだけです。 ただし、たずえば、デヌタセットを䞊べ替えるには、すべおのデヌタを読み取っお実際に䞊べ替えおから、最初の行を怜蚎する必芁がありたす。 これは、以䞋の説明で明確に芋られたす。



  QUERY PLAN ------------------------------------------------------------------- Sort (cost=22.88..23.61 rows=292 width=202) Sort Key: relfilenode -> Seq Scan on pg_class (cost=0.00..10.92 rows=292 width=202) (3 rows)
      
      





゜ヌトの初期コストは22.88ですが、合蚈コストは23.61に過ぎないこずに泚意しおください。 したがっお、Sortから行を返すこずはコストの点では重芁ではありたせんが、行を䞊べ替えるこずはできたす。



Explainの次の情報は「行」です。これは、PostgreSQLが返すこずができるず考えるおおよその行数ですたずえば、LIMITがある堎合、より少ない行を返すこずができたす。これは、結合などの䞀郚の操䜜でも非垞に重芁です結合合蚈20行の2぀のテヌブルの結合はさたざたな方法で実行できたすが、䞀般的にはどちらを䜿甚しおもかたいたせんが、100䞇行のテヌブルず10億行のテヌブルを組み合わせる堎合は、行うこずは非垞に重芁です私は話しおいる 内郚結合/巊結合ではなく、ハッシュ結合、ネストされたルヌプ、マヌゞ結合に関するものです。その内容がわからない堎合は心配しないで、埌ですべお説明したす。



もちろん、この数はさたざたな理由で誀っお掚定される可胜性がありたす。 時々それは重芁ではなく、時にはそれは重芁です。 しかし、誀った評䟡に぀いおは埌で説明したす。



最埌の情報は幅です。 これは、この操䜜の䞀郚ずしお返される単䞀の行に含たれるバむト数の平均的なPostgreSQLの掚定倀です。 䟋



 explain select * from pg_class; QUERY PLAN ------------------------------------------------------------- Seq Scan on pg_class (cost=0.00..10.92 rows=292 width=202) (1 row) explain select relname, relkind from pg_class; QUERY PLAN ------------------------------------------------------------ Seq Scan on pg_class (cost=0.00..10.92 rows=292 width=65) (1 row)
      
      





ご芧のずおり、フィヌルド数の制限により幅が倉曎され、その結果、リク゚ストの実行を通過する必芁があるデヌタの量が倉曎されたした。



そしお今、泚意、最も重芁な情報説明は朚です。 最䞊䜍ノヌドには、その䞋のノヌドからのデヌタが必芁です。



この蚈画を芋おみたしょう。



゜ヌト、ハッシュ結合、seqスキャン、ハッシュ、そしお再びseqスキャンの5぀の操䜜がありたす。 PostgreSQLは最䞊䜍の操䜜を実行したす-゜ヌトは、その盎䞋にある次の操䜜ハッシュ結合を実行し、そこからデヌタを受け取りたす。 ゜ヌトするデヌタを返すハッシュ結合では、seq scanpg_procによるずhash4を実行する必芁がありたす。 最埌に、ハッシュは、デヌタを返すために、pg_namespaceによっおseq scanを実行する必芁がありたす。



䞀郚の操䜜では、デヌタを即座に、たたはさらに重芁なこずずしお埐々に返すこずができるこずを理解するこずが非垞に重芁です。 たずえば、Seq Scan。 そしお、いく぀かはできたせん。 たずえば、ここでは、ハッシュ4の初期オヌバヌヘッドが「すべおの行」の「サブオペレヌション」シヌケンススキャンず同じであるこずがわかりたす。 ぀たり、ハッシュ操䜜を開始するには少なくずも1行返すこずができるように、そのサブ操䜜からすべおの行を読み取る必芁がありたす。



緩やかな改行郚分は、関数の䜜成を開始するずきに特に重芁になりたす。 この関数を芋おみたしょう



 CREATE OR REPLACE FUNCTION public.test() RETURNS SETOF integer LANGUAGE plpgsql AS $function$ declare i int4; begin for i in 1..3 loop return next i; perform pg_sleep(1); end loop; return; end; $function$;
      
      





䜕もわからなくおも心配しないでください。 この関数は3行を返したす。各行には1、2、3の敎数が含たれたす。重芁なこずは、各行が戻っおから1秒間スリヌプ状態になるこずです。



これは、私がこれを奜きなら



 select * from test();
      
      





結果が出るたで3秒埅たなければなりたせん。



しかし、この状況で埩垰を埅぀のにどれくらい時間がかかりたすか



 select * from test() limit 1;
      
      





芋おみたしょう



 \timing Timing is on. select * from test() limit 1; test ------ 1 (1 row) Time: 3005.334 ms
      
      





同じ3秒。 なんで PL / pgSQLおよびすべおではないにしおもほずんどのPL / *蚀語は郚分的な結果を返すこずができないためです。 「次ぞ戻る」の助けを借りお-圌らはできるようですが、実際にはすべおの結果はバッファに保存され、関数の実行が終了したずきに䞀緒に返されたす。



䞀方、「通垞の」操䜜では通垞、郚分的なデヌタを返すこずができたす。 これは、難しいテヌブルでシヌケンシャルスキャンなどの簡単な操䜜を実行した堎合に芋られたす。



 create table t as select i as id, repeat('depesz', 100)::text as payload from generate_series(1,1000000) i;
      
      





この衚はそれを瀺しおいたす



 explain analyze select * from t; QUERY PLAN ---------------------------------------------------------------------------------------------------------------- Seq Scan on t (cost=0.00..185834.82 rows=10250082 width=36) (actual time=0.015..232.380 rows=1000000 loops=1) Total runtime: 269.666 ms (2 rows) explain analyze select * from t limit 1; QUERY PLAN -------------------------------------------------------------------------------------------------------------- Limit (cost=0.00..0.02 rows=1 width=36) (actual time=0.003..0.003 rows=1 loops=1) -> Seq Scan on t (cost=0.00..185834.82 rows=10250082 width=36) (actual time=0.003..0.003 rows=1 loops=1) Total runtime: 0.016 ms (3 rows)
      
      





これたでは「合蚈ランタむム..」のみをご芧ください



ご芧のずおり、シヌケンシャルスキャンは非垞に迅速に終了したした-LIMIT食欲がちょうど1行で満たされるずすぐに。



ここで、オヌバヌヘッドク゚リを比范するための最良の基準ではないでさえ、最䞊䜍ノヌド最初のク゚リでのseqスキャンず2番目の制限がすべおの行を返すための倀が非垞に異なるこずを瀺しおいるこずに泚意しおください-185834.82察0.02



したがっお、任意の操䜜の最初の4぀の数倀2぀のコスト芋積もり、行数ず幅は近䌌倀です。 それらは正しい堎合ずそうでない堎合がありたす。



「EXPLAIN ANALYZEク゚リ」たたは「EXPLAINANALYZE onク゚リ」を実行するず埗られる他の4぀の数倀は、実際の結果を瀺しおいたす。



時間はただ範囲で瀺されたすが、今回はリアルタむムです。 これは、PostgreSQLがこの操䜜の凊理に費やした時間です同じ操䜜を䜕床も実行する可胜性があるため。 コストず同様に、時間は範囲で衚されたす。操䜜を開始する時間ずすべおのデヌタを返す時間です。 この蚈画を確認したしょう



 $ explain analyze select * from t limit 100; QUERY PLAN -------------------------------------------------------------------------------------------------------------- Limit (cost=0.00..9.33 rows=100 width=608) (actual time=0.008..0.152 rows=100 loops=1) -> Seq Scan on t (cost=0.00..93333.86 rows=999986 width=608) (actual time=0.007..0.133 rows=100 loops=1) Total runtime: 0.181 ms (3 rows)
      
      





ご芧のずおり、Limitの操䜜開始時間は0.008ですここでの枬定単䜍はミリ秒です。 これは、Seqスキャンデヌタを取埗するためにLimitが呌び出すが最初の行を返すのに0.007msかかり、その埌、さらに0.001msが制限内で凊理を開始したために発生したす。



さらに最初の行が戻った埌、100行を受信するたで、制限はSeqスキャンからデヌタを受信し続けたした。 その埌、圌はシヌケンシャルスキャンを停止しこれはリク゚ストの開始から0.133ms埌に起こりたした、さらに0.019ms埌に終了したした。



名前が瀺すように、実際の行数は、この操䜜で返された平均行数を瀺したす。 ルヌプは、この操䜜が実行された回数を瀺したす。



どの堎合、操䜜は耇数回呌び出されたすか たずえば、堎合によっおは、結合ク゚リたたはネストされたク゚リを䜿甚したす。 この蚈画のようになりたす。



3番目の操䜜では2サむクルしかないこずに泚意しおください。 ぀たり、このseqスキャンは2回実行され、平均で1行が返され、平均で0.160msが必芁でした。 したがっお、この特定の操䜜に費やされた合蚈時間2 * 0.160ms = 0.32msexplain.depesz.comの排他的/包括的列に瀺されおいるように。



非垞に倚くの堎合、ク゚リのパフォヌマンスが䜎いのは、䜕らかのサブク゚リを䜕床も繰り返す必芁があるためです。 たずえば、次のようになりたす 。



もちろん、これは、そのような蚈画を遞んだpostgresがすべおを責めるこずを意味するものではありたせん。おそらく、他に遞択肢がなかったか、さらにコストが高いこずが刀明したのでしょう。



䞊蚘の䟋では、操䜜3の実際の時間はわずか0.003msですが、この操䜜は26,000回以䞊実行され、最終的にほが79msの時間が費やされるずいう事実に泚意する䟡倀がありたす。



これは、explain'ovを読むのに必芁な理論的な情報だず思いたす。 ほずんどの堎合、操䜜やその他の情報の意味はただわかりたせんが、少なくずも数字の意味ず説明の違いはわかりたすランダムな抂算に基づいお抜象的な枬定単䜍でコストが衚瀺されたす 䟋および分析の説明実際の時間、行数、実行時間を枬定単䜍で衚瀺し、異なるク゚リを比范できるようにしたす。



い぀ものように、重芁なこずをたくさん逃したのではないかず心配しおいたすが、目を匕くこずはできたせんでした。たたはさらに悪いこずにそれらを「自明」だず考えたした。 䜕か䞍足しおいるず思われる堎合はお知らせください。すみやかにギャップを埋めようずしたす。



ただし、このテキストをさらに2〜3冊の出版物で開発する予定であり、さらに詳しく説明したす。



この蚘事がお圹に立おば幞いです。 賌読し、出版物に埓っおください。 すぐにブログで次の゚ピ゜ヌドの翻蚳がありたす い぀ものように、フィヌドバックや提案をお気軜にお寄せください。 最も興味深いのは、次のPG Day'16ロシアのプログラムに含たれたす :)



All Articles