PostgreSQLの隠された機能[クローズ]


80

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

対応するMySQLスレッドの9つのコメントを打ち負かすことができると確信しています:)

回答:


76

postgresはMySQLよりもはるかに正気なので、報告する「トリック」はそれほど多くありません;-)

マニュアルにはいくつかの素晴らしい持っているパフォーマンスのヒントを。

覚えておくべき他のいくつかのパフォーマンス関連事項:

  • 自動真空がオンになっていることを確認します
  • postgres.conf(有効なキャッシュサイズ、共有バッファー、作業メモリ...調整するための多くのオプション)を確認してください。
  • pgpoolまたはpgbouncerを使用して、「実際の」データベース接続を最小限に抑えます
  • EXPLAINおよびEXPLAINANALYZEがどのように機能するかを学びます。出力の読み方を学びます。
  • 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)

ありがとう。編集:クラスターに関する情報を追加しました。
tommym 2009

データベースサイズを表示することは、8.4ベータpsqlの「\ l」の機能の1つであることに気づきました。それまでは、8.3にはサイズをバイト単位でプリティファイするpg_size_pretty()関数があると思います。
araqnid 2009

ヒントをありがとう!pg_size_prettyを認識していませんでした。私はそれを含むように私の答えを更新しました。
tommym 2009

3
replace(answer、 'per say'、 'se se')
asjo 2009年

23

最も簡単なトリックは、PostgreSQLが(まだ行っていない場合)とそれを仕事に多くのRAMを与えることだけである(別に設定すると当然の適切なインデックスを使用してから)かなり良く実行できます。ほとんどのデフォルトのインストールでは、shared_buffersの値が低すぎます(私の意見では)。設定できます

shared_buffers

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

Linuxシステムでは、postgresqlが再び起動しない場合は、kernel.shmmaxの制限に達している可能性があります。で高く設定する

sysctl -w kernel.shmmax=xxxx

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

Postgresqlのトリックの全体はここで見つけることができます


17

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型にキャストできます。


15

コピー

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

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での一括挿入の高速化?


2
これは、csvのインポートにも役立ちます。
ChristopheD

最近のリリース(少なくとも8.3、場合によってはそれ以前)では、COPYと同じトランザクションで入力するテーブルをCREATEまたはTRUNCATEすると、WALログに影響を与えず、さらに高速なパフォーマンスが得られます。postgresql.org/docs/8.3/static/populate.html
TREE

12
  • 私の一番のお気に入りはgenerate_series、ついにダミーの行セットを生成するクリーンな方法です。
  • LIMITサブクエリの句で相関値を使用する機能:

    SELECT  (
            SELECT  exp_word
            FROM    mytable
            OFFSET id
            LIMIT 1
            )
    FROM    othertable
    
  • カスタムアグリゲートで複数のパラメーターを使用する意欲(ドキュメントではカバーされていません):例については、ブログの記事を参照してください

1
+ 1、generate_series()は、かなりの数のことに必要なものです(たとえば、「ダミーテーブル」が必要なときはいつでも)。2番目のスニペットも興味をそそるように見えます。
j_random_hacker 2009

9

Postgresで私が本当に気に入っていることの1つは、列でサポートされているデータ型のいくつかです。たとえば、ネットワークアドレス配列を格納するために作成された列タイプがあります。これらの列タイプに対応する関数(ネットワークアドレス/配列)を使用すると、MySQLまたは他のデータベースエンジンのコードを介して結果を処理することによって実行する必要があるクエリ内で多くの複雑な操作を実行できます。


2
また、標準のタイプが合わない場合は、独自のタイプを簡単に作成できます。
bortzmeyer 2009

8

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

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

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

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

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

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

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

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

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


6

マテリアライズドビューの設定は非常に簡単です。

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
  • Inheritance..infact多重継承(多くのWebフレームワークがpostgresを操作するときに実装する1対1の関係継承ではなく、親子の「継承」のように)。

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

  • C、Python、Perlなどのさまざまな言語でプロシージャを作成します(db-adminではなく開発者であれば、コーディングが容易になります)。

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

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

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

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

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

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


+1多重継承がこれほど下にあるとは信じられません。
Adam Gent


4
select pg_size_pretty(200 * 1024)

PostgreSQL 9.3キャッチエラーでこれを試しました
Vivek S.

@WingedPantherあなたのエラーは何ですか?ここでは9.3、それはまた、エラーが発生しました(当時2009エラーの方法を持っていない)、この修正は、あなたが大きな整数に整数にキャストする必要がある:pg_size_pretty((200 * 1024)::bigint)
マイケル・ブエン

はい、それが問題です
Vivek S.

3

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


3

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

createdb -T old_db new_db

ドキュメントには次のように書かれています。

これは(まだ)汎用の「COPYDATABASE」機能として意図されたものではありません。

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

createdb new_db

pg_dump old_db | psql new_db


2

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

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クエリなど)を回避するために使用できます。


4
RAMに表領域を作成することは非常に悪い考えです。これを行わないでください。データベース全体に深刻で回復不能な破損が発生するリスクがあります。UNLOGGEDテーブルを使用します。
クレイグリンガー

2

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

トランザクション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');

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

1つのステートメントで、削除、挿入、および選択を行うことができます。

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テーブルに移動してから、移動された行を表示します。


2

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

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

2.)データベースのすべてのフィールドtextvarcharフィールドから末尾のスペースを削除します。

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」
列に「文字変化」というデータ型を使用したことがわかります。しかし、私は常に整数を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

ここでよく説明されています


+1のために2369

0

mysqlではなく、古いデータベースの名前を変更すると便利です。次のコマンドを使用するだけです。

ALTER DATABASE name RENAME TO new_name
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.