2008/07/24
カテゴリ : Tech
PostgreSQL
PostgreSQLの手軽なSQLチューニング
こんばんは、牧野です。
今日は前々回の話題に戻って、PostgreSQLのチューニングの話です。
この前は重いSQLをどうやって見つけるか紹介しました。今回は処理を速くするためのSQLの具体例を紹介します。
1.インデックスを使う
以前も書いたので省略しますが、データ数が多くなってくると(数万件以上とか)インデックスが正しく使えているかどうかで負荷のかかり方が大きく変わってきます。
複数カラムの条件検索の場合は、必要に応じて複合インデックスを作成します。その時は、プログラムの方でWHERE句の順番に気をつけましょう。
2.VACUUMとREINDEX
特にバッチプログラムで頻繁にデータ更新を行うようなテーブルがある時は要注意です。VACUUMをしないままで運用していくと大変なことになる場合があります。バッチ処理の後等定期的にVACUUMするようにしましょう。
自動バキュームを使うのも有効です。8.1、8.2の場合はpostgresql.confのautovacuumをonにします。
8.3だとデフォルトで自動バキュームがオンになっています。あと、8.3にするとvacuumそのものの必要性がかなり低下します。
データ更新が頻繁にあるテーブルでは、定期的にREINDEXもしましょう。
REINDEX TABLE テーブル名;
大量のデータを更新してしまっている場合、VACUUMだけではあまり改善しないことがあります。
そんな時はVACUUM FULLを実行します。
10万件余りのデータを10回INSERT、DELETEした場合の比較
また、テーブルのデータを全部消す時は、TRUNCATEを使った方がいい場合がほとんどです。DELETE時に実行するトリガはTRUNCATE時には実行されませんが、TRUNCATEだとVACUUM、VACUUM FULLの必要がなくDELETEよりも高速です。
3.COUNT()、MAX()、MIN()、DISTINCTに注意
COUNT()、MAX()、MIN()関数やDISTINCTを使うと、対象カラムにインデックスが張ってあってもデータ数が多くなってくると重くなることがあります。
MAX()、MIN()はORDER BYで、DISTINCTはGROUP BYで代用可能です。
COUNT()は、あまりいい方法を知りません。重くてどうしようもなかったら、データ数を入れるテーブルを作る、とか。。。何か他にうまい方法を知っている方がいましたらぜひ教えて下さい。
以上、気付いたことから順に挙げました。
MySQLではサブクエリを使うと極端に遅くなることがあった(100倍以上時間がかかった)のでPostgreSQLでも試してみましたが、PostgreSQLではそんなことはないようです。
今日は前々回の話題に戻って、PostgreSQLのチューニングの話です。
この前は重いSQLをどうやって見つけるか紹介しました。今回は処理を速くするためのSQLの具体例を紹介します。
1.インデックスを使う
以前も書いたので省略しますが、データ数が多くなってくると(数万件以上とか)インデックスが正しく使えているかどうかで負荷のかかり方が大きく変わってきます。
複数カラムの条件検索の場合は、必要に応じて複合インデックスを作成します。その時は、プログラムの方でWHERE句の順番に気をつけましょう。
2.VACUUMとREINDEX
特にバッチプログラムで頻繁にデータ更新を行うようなテーブルがある時は要注意です。VACUUMをしないままで運用していくと大変なことになる場合があります。バッチ処理の後等定期的にVACUUMするようにしましょう。
自動バキュームを使うのも有効です。8.1、8.2の場合はpostgresql.confのautovacuumをonにします。
8.3だとデフォルトで自動バキュームがオンになっています。あと、8.3にするとvacuumそのものの必要性がかなり低下します。
データ更新が頻繁にあるテーブルでは、定期的にREINDEXもしましょう。
REINDEX TABLE テーブル名;
大量のデータを更新してしまっている場合、VACUUMだけではあまり改善しないことがあります。
そんな時はVACUUM FULLを実行します。
10万件余りのデータを10回INSERT、DELETEした場合の比較
- test=>
explain select count(*) from music; QUERY PLAN - --------------------------------------------------------------------
Aggregate (cost=10747.35..10747.35 rows=1 width=0) -> Seq Scan on music (cost=0.00..10476.28 rows=108428 width=0) - (2
rows) - test=>
vacuum full music; - VACUUM
- test=>
explain select count(*) from music; QUERY PLAN - -------------------------------------------------------------------
Aggregate (cost=2209.35..2209.35 rows=1 width=0) -> Seq Scan on music (cost=0.00..1938.28 rows=108428 width=0) - (2
rows)
また、テーブルのデータを全部消す時は、TRUNCATEを使った方がいい場合がほとんどです。DELETE時に実行するトリガはTRUNCATE時には実行されませんが、TRUNCATEだとVACUUM、VACUUM FULLの必要がなくDELETEよりも高速です。
3.COUNT()、MAX()、MIN()、DISTINCTに注意
COUNT()、MAX()、MIN()関数やDISTINCTを使うと、対象カラムにインデックスが張ってあってもデータ数が多くなってくると重くなることがあります。
MAX()、MIN()はORDER BYで、DISTINCTはGROUP BYで代用可能です。
- test=>
explain select max(second) from music; QUERY PLAN - -------------------------------------------------------------------
Aggregate (cost=2209.35..2209.35 rows=1 width=4) -> Seq Scan on music (cost=0.00..1938.28 rows=108428 width=4) - (2
rows) - test=>
explain select second from music order by second desc limit 1; QUERY PLAN - ---------------------------------------------------------------------------
Limit (cost=0.00..0.05 rows=1 width=4) -> Index Scan Backward using music_second_index on music (cost=0.00..5263.88 rows=108428 width=4) - (2
rows)
- test=>
explain select distinct artist_id from music; QUERY PLAN - -------------------------------------------------------------------------
Unique (cost=11006.32..11548.46 rows=2143 width=4) -> Sort (cost=11006.32..11277.39 rows=108428 width=4) Sort Key: artist_id -> Seq Scan on music (cost=0.00..1938.28 rows=108428 width=4) - (4
rows) - test=>
explain select artist_id from music group by artist_id; QUERY PLAN - -------------------------------------------------------------------
HashAggregate (cost=2209.35..2209.35 rows=2143 width=4) -> Seq Scan on music (cost=0.00..1938.28 rows=108428 width=4) - (2
rows)
COUNT()は、あまりいい方法を知りません。重くてどうしようもなかったら、データ数を入れるテーブルを作る、とか。。。何か他にうまい方法を知っている方がいましたらぜひ教えて下さい。
以上、気付いたことから順に挙げました。
MySQLではサブクエリを使うと極端に遅くなることがあった(100倍以上時間がかかった)のでPostgreSQLでも試してみましたが、PostgreSQLではそんなことはないようです。
コメント
コメントフォーム
トラックバックURI
-
- [sql]count(*)をトリガを使って from iakioの日記
- COUNT()は、あまりいい方法を知りません。重くてどうしようもなかった...
2008年07月28日 17:28
最近の記事
システム開発エンジニア募集! [2012年02月03日 : 小林有佳]
OpenVPNで細々便利な設定 [2012年01月31日 : 門脇優児]
【iOS】Viewの開発・デバッグに役立つ色々 [2012年01月23日 : 中川善樹]
PHPDocumentorの利用方法まとめ [2012年01月19日 : 笹亀弘]
Google Chart Toolsを使ってサイトマップを描こう! [2011年12月21日 : 志田仁美]
stumpwm設定v2 [2011年12月19日 : 門脇優児]
Mashup Awards 7の授賞式が行われました [2011年12月16日 : 中川善樹]
社員旅行に行きました [2011年12月12日 : 大橋寛子]
iCloud風のアイコンを作成する(Fireworks) [2011年12月07日 : 和田記光]
iScroll4でネイティブに近いスマホ向けHTMLページを作成する [2011年12月02日 : 松田惇]













count(*)ではなく
count(カラム名)にしたほうが
はやいそうです。
10万件くらいデータの入っているmusicテーブルで調べてみたのですが、自分の環境では違いが出ませんでした。。
テーブルのカラム数が少なくて、データ量も小さいので差が出なかったのかもしれません。