web-dev-qa-db-ja.com

PostgreSQLの隠し機能

これがまだ投稿されていないことに驚いています。 Postgresで知っている興味深いトリックはありますか?あいまいな構成オプションとスケーリング/パフォーマンストリックは特に歓迎されます。

対応する MySQLスレッド の9つのコメントに勝てると確信しています。

79
ramanujan

PostgresはMySQLよりも正気であるため、レポートする「トリック」はそれほど多くありません;-)

manual にはいくつかの素晴らしい performance のヒントがあります。

心に留めておくべき他のいくつかのパフォーマンス関連事項:

  • 自動バキュームがオンになっていることを確認してください
  • Postgres.conf(効果的なキャッシュサイズ、共有バッファ、作業メモリ...そこにチューニングするためのオプションがたくさんあります)を完了したことを確認してください。
  • Pgpoolまたは pgbouncer を使用して、「実際の」データベース接続を最小限に保ちます
  • [〜#〜] explain [〜#〜] とEXPLAIN ANALYZEがどのように機能するかを学びます。出力の読み方を学びます。
  • [〜#〜] cluster [〜#〜] インデックスに従ってディスク上のデータをソートします。大きな(主に)読み取り専用テーブルのパフォーマンスを劇的に改善できます。クラスタリングは1回限りの操作です。テーブルが後で更新されても、変更はクラスタリングされません。

ここでは、構成やパフォーマンス自体に関係のない、便利なものをいくつか紹介します。

現在何が起こっているかを確認するには:

select * from pg_stat_activity;

その他の機能を検索:

select * from pg_proc WHERE proname ~* '^pg_.*'

データベースのサイズを見つける:

select pg_database_size('postgres');
select pg_size_pretty(pg_database_size('postgres'));

すべてのデータベースのサイズを見つける:

select datname, pg_size_pretty(pg_database_size(datname)) as size
  from pg_database;

テーブルとインデックスのサイズを確認します。

select pg_size_pretty(pg_relation_size('public.customer'));

または、すべてのテーブルとインデックスを一覧表示するには(おそらくこれを表示する方が簡単です):

select schemaname, relname,
    pg_size_pretty(pg_relation_size(schemaname || '.' || relname)) as size
  from (select schemaname, relname, 'table' as type
          from pg_stat_user_tables
        union all
        select schemaname, relname, 'index' as type
          from pg_stat_user_indexes) x;

ああ、そしてあなたはトランザクションをネストでき、部分的なトランザクションをロールバックします++

test=# begin;
BEGIN
test=# select count(*) from customer where name='test';
 count 
-------
     0
(1 row)
test=# insert into customer (name) values ('test');
INSERT 0 1
test=# savepoint foo;
SAVEPOINT
test=# update customer set name='john';
UPDATE 3
test=# rollback to savepoint foo;
ROLLBACK
test=# commit;
COMMIT
test=# select count(*) from customer where name='test';
 count 
-------
     1
(1 row)
75
tommym

(もちろん適切なインデックスの設定と使用は別として)postgresqlのパフォーマンスを大幅に向上させる最も簡単なトリックは、より多くのRAM to (まだ行っていない場合)で作業します。ほとんどのデフォルトのインストールでは、shared_buffersの値が(私が思うに)非常に低いです。セットする

shared_buffers

postgresql.conf。この数を128で割ると、postgresが要求できるメモリ量(MB単位)の概算が得られます。あなたがそれを十分に上げるならば、これはpostgresqlを飛ばすでしょう。 postgresqlを再起動することを忘れないでください。

Linuxシステムでは、postgresqlが再び起動しない場合は、おそらくkernel.shmmax制限に達しているでしょう。高く設定してください

sysctl -w kernel.shmmax=xxxx

これをブート間で持続させるには、/ etc/sysctl.confにkernel.shmmaxエントリを追加します。

Postgresqlのトリックのすべてがここにあります

23
ChristopheD

Postgresは、そのINTERVALサポートのおかげで、非常に強力な日時処理機能を備えています。

例えば:

select NOW(), NOW() + '1 hour';
              now              |           ?column?            
-------------------------------+-------------------------------
 2009-04-18 01:37:49.116614+00 | 2009-04-18 02:37:49.116614+00
(1 row)



select current_date ,(current_date +  interval '1 year')::date;
    date             |  date            
---------------------+----------------
 2014-10-17          | 2015-10-17
(1 row)

多くの文字列をINTERVAL型にキャストできます。

17
Yann Ramin

[〜#〜]コピー[〜#〜]

始めます。 SQLiteからPostgresに切り替えるたびに、通常、非常に大きなデータセットがいくつかあります。重要なのは、INSERTSを実行するのではなく、COPY FROMを使用してテーブルをロードすることです。ドキュメントを参照してください:

http://www.postgresql.org/docs/8.1/static/sql-copy.html

次の例では、フィールド区切り文字として縦棒(|)を使用して、クライアントにテーブルをコピーします。

COPY country TO STDOUT WITH DELIMITER '|';

ファイルから国テーブルにデータをコピーするには:

COPY country FROM '/usr1/proj/bray/sql/country_data';

こちらもご覧ください: sqlite3での一括挿入の高速化?

15
ramanujan
  • 私の断然のお気に入りはgenerate_series:ダミー行セットを生成するためのクリーンな方法。
  • サブクエリのLIMIT句で相関値を使用する機能:

    SELECT  (
            SELECT  exp_Word
            FROM    mytable
            OFFSET id
            LIMIT 1
            )
    FROM    othertable
    
  • カスタムアグリゲーションで複数のパラメーターを使用する能力(ドキュメントではカバーされていません): 私のブログの記事 の例を参照してください。
12
Quassnoi

Postgresで本当に気に入っている点の1つは、列でサポートされているデータ型のいくつかです。たとえば、 Network Addresses および Arrays を格納するために作成された列タイプがあります。これらの列タイプに対応する関数( Network Addresses / Arrays )を使用すると、クエリ内の多くの複雑な操作を実行できます。 MySQLまたはその他のデータベースエンジン。

9
Chad Birch

あなたがそれらを知るようになれば、配列は本当にクールです。ページ間のハイパーリンクを保存したいとしましょう。次のようなテーブルの作成について考えることから始めます。

CREATE TABLE hyper.links (
     tail INT4,
     head INT4
);

tail列にインデックスを付ける必要があり、たとえば200,000,000のリンク行がある場合(ウィキペディアで得られるように)、巨大なテーブルと巨大なインデックスが表示されます。

ただし、PostgreSQLでは、代わりにこのテーブル形式を使用できます。

CREATE TABLE hyper.links (
     tail INT4,
     head INT4[],
     PRIMARY KEY(tail)
);

リンクのすべてのヘッドを取得するには、次のようなコマンドを送信します(unnest()は8.4以降の標準です)。

SELECT unnest(head) FROM hyper.links WHERE tail = $1;

このクエリは、最初のオプションと比較すると驚くほど高速です(unnest()は高速で、インデックスはかなり小さくなっています)。さらに、特に配列が長すぎてトーストテーブルに圧縮される場合は、テーブルとインデックスのRAMメモリとHDスペースの使用量が大幅に少なくなります。配列は本当に強力です。

注:unnest()は配列から行を生成しますが、array_agg()は行を配列に集約します。

8

マテリアライズドビューは設定がかなり簡単です:

CREATE VIEW my_view AS SELECT id, AVG(my_col) FROM my_table GROUP BY id;
CREATE TABLE my_matview AS SELECT * FROM my_view;

これにより、my_viewの列と値を含む新しいテーブルmy_matviewが作成されます。次に、トリガーまたはcronスクリプトを設定して、データを最新の状態に保つことができます。

TRUNCATE my_matview;
INSERT INTO my_matview SELECT * FROM my_view;
6
Cameron
  • Inheritance..infact複数の継承(多くのWebフレームワークがpostgresで作業するときに実装する1対1の関係の継承ではなく、親子の「継承」の場合と同様)。

  • PostGIS(空間拡張)。ジオメトリ関数の包括的なセットを提供し、すぐにストレージを調整できる素晴らしいアドオンです。多くのオープンソースの地理ライブラリ(OpenLayers、MapServer、Mapnikなど)で広く使用されており、MySQLの空間拡張よりも確実に優れています。

  • 異なる言語で手順を書く(例: C、Python、Perlなど(開発者であり、db-adminではない場合、コードを書くのが楽になります)。

    また、すべてのプロシージャは外部に(モジュールとして)保存でき、指定した引数によって実行時に呼び出しまたはインポートできます。このようにして、コードをソース管理し、コードを簡単にデバッグできます。

  • データベースに実装されたすべてのオブジェクト(テーブル、制約、インデックスなど)に関する巨大で包括的なカタログ。

    いくつかのクエリを実行してすべてのメタ情報を取得することは常に非常に役立ちます。 、制約名とそれらが実装されているフィールド、インデックス名など.

    新しいデータを読み込んだり、大きなテーブルで大規模な更新を行ったり(トリガーを自動的に無効にしてインデックスを削除したり)し、処理が完了した後で簡単に再作成する必要がある場合、私にとってはすべて非常に便利です。誰かがこれらのクエリをいくつか書くという優れた仕事をしました。

    http://www.alberton.info/postgresql_meta_info.html

  • 1つのデータベースの下に複数のスキーマがあります。データベースに多数のテーブルがある場合は、スキーマをカテゴリとして考えることができます。すべてのテーブル(スキーマに関係なく)は、親dbに存在する他のすべてのテーブルおよび関数にアクセスできます。

6
Nakh

「explain analyze」の出力を解読する方法を学ぶ必要はありません。ツールがあります http://explain.depesz.com

5
AAS
select pg_size_pretty(200 * 1024)
4
Michael Buen

pgcrypto :多くのプログラミング言語の暗号モジュールが提供するよりも多くの暗号化機能があり、すべてデータベースから直接アクセスできます。それは、Just Get Rightを暗号化することを信じられないほど簡単にします。

3
kquinn

データベースは次の方法でコピーできます。

createdb -T old_db new_db

ドキュメントは言う:

これは(まだ)汎用の「COPY DATABASE」機能としては意図されていません

しかし、それは私にとってはうまく機能し、よりもはるかに高速です

createdb new_db

pg_dump old_db | psql new_db

3
Kim Rutherford

使い捨てデータ/グローバル変数のメモリストレージ

RAMに存在するテーブルスペースを作成し、そのテーブルスペースにテーブル(おそらく9.1ではログなし)を作成して、セッション間で共有する使い捨てのデータ/グローバル変数を格納できます。

http://magazine.redhat.com/2007/12/12/tip-from-an-rhce-memory-storage-on-postgresql/

アドバイザリロック

これらはマニュアルのあいまいな領域に文書化されています:

http://www.postgresql.org/docs/9.0/interactive/functions-admin.html

行レベルのロックを多数取得するよりも高速な場合があり、FOR UPDATEが実装されていない場合(再帰的なCTEクエリなど)を回避するために使用できます。

2

1。)クエリに通知を追加する必要がある場合は、ネストされたコメントを使用できます

SELECT /* my comments, that I would to see in PostgreSQL log */
       a, b, c
   FROM mytab;

2。)データベースのすべてのtextおよびvarcharフィールドから末尾のスペースを削除します。

do $$
declare
    selectrow record;
begin
for selectrow in
select 
       'UPDATE '||c.table_name||' SET '||c.COLUMN_NAME||'=TRIM('||c.COLUMN_NAME||')  WHERE '||c.COLUMN_NAME||' ILIKE ''% '' ' as script
from (
       select 
          table_name,COLUMN_NAME
       from 
          INFORMATION_SCHEMA.COLUMNS 
       where 
          table_name LIKE 'tbl%'  and (data_type='text' or data_type='character varying' )
     ) c
loop
execute selectrow.script;
end loop;
end;
$$;

3。)ウィンドウ関数を使用して、重複行を非常に効果的に削除できます:

DELETE FROM tab 
  WHERE id IN (SELECT id 
                  FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), id 
                           FROM tab) x 
                 WHERE x.row_number > 1);

いくつかのPostgreSQLの最適化バージョン(ctid付き):

DELETE FROM tab 
  WHERE ctid = ANY(ARRAY(SELECT ctid 
                  FROM (SELECT row_number() OVER (PARTITION BY column_with_duplicate_values), ctid 
                           FROM tab) x 
                 WHERE x.row_number > 1));

4。)サーバーの状態を特定する必要がある場合は、次の関数を使用できます:

SELECT pg_is_in_recovery();

5。)関数のDDLコマンドを取得します。

select pg_get_functiondef((select oid from pg_proc where proname = 'f1'));

6。)PostgreSQLの列のデータ型を安全に変更する

create table test(id varchar );
insert into test values('1');
insert into test values('11');
insert into test values('12');

select * from test
--Result--
id
character varying
--------------------------
1
11
12

上記の表から、データタイプを使用したことがわかります。「id」には「文字の変化」
カラム。しかし、私は常に整数を整数として与えているので、それは間違いでした。したがって、ここでvarcharを使用することは悪い習慣です。それでは、列のタイプを整数に変更してみましょう。

ALTER TABLE test ALTER COLUMN id TYPE integer;

しかし、それは戻ります:

エラー:列「id」を整数のSQL状態に自動的にキャストすることはできません:42804ヒント:USING式を指定して変換を実行してください

つまり、データがすでに列にあるため、単純にデータ型を変更することはできません。データは「文字が変化する」タイプであるため、整数のみを入力しましたが、postgresはそれを整数として期待できません。したがって、postgresが示唆したように、「USING」式を使用してデータを整数にキャストできます。

ALTER TABLE test ALTER COLUMN id  TYPE integer USING (id ::integer);

できます。

7。)データベースに接続しているユーザーを知る
これは多かれ少なかれ監視コマンドです。 IPとポートを含むどのデータベースにどのユーザーが接続しているかを知るには、次のSQLを使用します。

SELECT datname,usename,client_addr,client_port FROM pg_stat_activity ;

8。)サーバーを再起動せずにPostgreSQL構成ファイルをリロードする

PostgreSQL構成パラメータは、postgresql.confやpg_hba.confなどの特別なファイルにあります。多くの場合、これらのパラメーターを変更する必要があります。ただし、一部のパラメーターを有効にするには、多くの場合、構成ファイルを再ロードする必要があります。もちろん、サーバーを再起動すれば実行されます。しかし、実稼働環境では、いくつかのパラメーターを設定するためだけに、数千人が使用しているデータベースを再始動することは好ましくありません。このような状況では、次の関数を使用して、サーバーを再起動せずに構成ファイルを再ロードできます。

select pg_reload_conf();

これはすべてのパラメーターに対して機能しないことに注意してください。一部のパラメーターの変更を有効にするには、サーバーを完全に再起動する必要があります。

9。)現在のデータベースクラスターのデータディレクトリパスの取得

システムでは、PostgreSQLの複数のインスタンス(クラスター)が、通常、異なるポートなどに設定されている可能性があります。このような場合、どのインスタンスがどのディレクトリ(物理ストレージディレクトリ)を使用しているかを見つけるのは、大変な作業です。このような場合、対象のクラスター内の任意のデータベースで次のコマンドを使用して、ディレクトリパスを取得できます。

SHOW data_directory;

同じ関数を使用してクラスターのデータディレクトリを変更できますが、サーバーを再起動する必要があります。

SET data_directory to new_directory_path;

10。)CHARがDATEであるかどうかを検索します

create or replace function is_date(s varchar) returns boolean as $$
begin
  perform s::date;
  return true;
exception when others then
  return false;
end;
$$ language plpgsql;

使用法:以下はTrueを返します

select is_date('12-12-2014')
select is_date('12/12/2014')
select is_date('20141212')
select is_date('2014.12.12')
select is_date('2014,12,12')

11。)PostgreSQLの所有者を変更します

REASSIGN OWNED BY sa  TO postgres;

12。)PGADMIN PLPGSQL DEBUGGER

よく説明された ここ

2
Vivek S.

これは、あまり知られていない機能の私のお気に入りのリストです。

トランザクションDDL

Postgresでは、ほぼすべてのSQLステートメントがトランザクションです。自動コミットをオフにすると、次のことが可能になります。

drop table customer_orders;
rollback;
select *
from customer_orders;

範囲タイプと除外制約

私の知る限りでは、Postgresは、2つの範囲が重複しているかどうかをチェックする制約を作成できる唯一のRDBMSです。例は、「有効開始日」と「有効期限」の日付を持つ製品価格を含むテーブルです。

create table product_price
(
   price_id      serial        not null primary key,
   product_id    integer       not null references products,
   price         numeric(16,4) not null,
   valid_during  daterange not null
);

NoSQLの機能

hstore拡張機能は、データベースの一部を「スキーマレス」にする必要がある場合に使用できる、柔軟で非常に高速なキー/値ストアを提供します。 JSONは、スキーマレスな方法でデータを保存するもう1つのオプションです。

insert into product_price 
  (product_id, price, valid_during)
values 
  (1, 100.0, '[2013-01-01,2014-01-01)'),
  (1,  90.0, '[2014-01-01,)');


-- querying is simply and can use an index on the valid_during column
select price
from product_price
where product_id = 42
  and valid_during @> date '2014-10-17';

700.000行のテーブルでの上記の実行プラン:

Index Scan using check_price_range on public.product_price  (cost=0.29..3.29 rows=1 width=6) (actual time=0.605..0.728 rows=1 loops=1)
  Output: price
  Index Cond: ((product_price.valid_during @> '2014-10-17'::date) AND (product_price.product_id = 42))
  Buffers: shared hit=17
Total runtime: 0.772 ms

重複する有効範囲を持つ行の挿入を回避するために、単純な(そして効率的な)一意制約を定義できます。

alter table product_price
  add constraint check_price_range 
  exclude using Gist (product_id with =, valid_during with &&)

無限

はるか将来の「実際の」日付を要求する代わりに、Postgresは日付を無限大と比較できます。例えば。日付範囲を使用しない場合、次のことができます

insert into product_price 
  (product_id, price, valid_from, valid_until)
values 
  (1,  90.0, date '2014-01-01', date 'infinity');

書き込み可能な共通テーブル式

単一のステートメントで削除、挿入、および選択できます。

with old_orders as (
   delete from orders
   where order_date < current_date - interval '10' year
   returning *
), archived_rows as (
   insert into archived_orders 
   select * 
   from old_orders
   returning *
)
select *
from archived_rows;

上記により、10年以上経過したすべての注文が削除され、archived_ordersテーブルに移動し、移動された行を表示します。

Mysqlができるよりも古いデータベースの名前を変更する方が便利です。次のコマンドを使用するだけです。

ALTER DATABASE name RENAME TO new_name
0
Moon_of_father