sql >> データベース >  >> RDS >> PostgreSQL

私のお気に入りのPostgreSQL拡張機能-パート2-

    これは、私のブログ「My Favorite PostgreSQL Extensions」の第2部で、postgres_fdwとpg_partmanの2つのPostgreSQL拡張機能を紹介しました。このパートでは、さらに3つ探求します。

    pgAudit

    次に関心のあるPostgreSQLの拡張機能は、さまざまな政府、金融機関、およびISO、BSI、FISCAMなどの他の認証機関による監査要件を満たすことを目的としています。PostgreSQLがネイティブに提供する標準のロギング機能with log_statement =allは監視に役立ちますが、監査に準拠または直面するために必要な詳細は提供されません。 pgAudit拡張機能は、データベースがアプリケーションの要求を満たしている間に、内部で起こったことの詳細に焦点を当てています。

    監査証跡または監査ログは、詳細なセッションおよび/またはオブジェクト監査ログを提供するPostgreSQLが提供する標準のログ機能によって作成および更新されます。 pgAuditによって作成された監査証跡は、監査設定によってはサイズが非常に大きくなる可能性があるため、事前に必要な監査の内容と量を慎重に決定する必要があります。次のセクションの簡単なデモは、pgAuditがどのように構成されて使用されるかを示しています。

    ログ証跡は、PGDATA / logの場所にあるPostgreSQLデータベースクラスターログ内に作成されますが、監査ログメッセージには、通常のデータベースバックグラウンドメッセージと監査ログを区別するための「AUDIT:」ラベルが接頭辞として付けられます。記録。

    デモ

    pgAuditの公式ドキュメントでは、すべてのPostgreSQLリリースで導入された新機能をサポートするために、PostgreSQLのメジャーバージョンごとに個別のバージョンのpgAuditが存在すると説明されています。このデモのPostgreSQLのバージョンは11であるため、pgAuditのバージョンは1.3.Xブランチのものになります。 pgaudit.logは、ログに記録されるステートメントのクラスを制御するために設定される基本的なパラメーターです。これは、セッションレベルのSETを使用して、またはグローバルに適用されるpostgresql.confファイル内で設定できます。

    postgres=# set pgaudit.log = 'read, write, role, ddl, misc';
    
    SET
    
    
    
    cat $PGDATA/pgaudit.log
    
    pgaudit.log = 'read, write, role, ddl, misc'
    
    
    
    db_replica=# show pgaudit.log;
    
             pgaudit.log
    
    ------------------------------
    
     read, write, role, ddl, misc
    
    (1 row)
    
    
    
    2020-01-29 22:51:49.289 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,3,1,MISC,SHOW,,,show pgaudit.log;,<not logged>
    
    
    
    db_replica=# create table t1 (f1 integer, f2 varchar);
    
    CREATE TABLE
    
    
    
    2020-01-29 22:52:08.327 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,4,1,DDL,CREATE TABLE,,,"create table t1 (f1 integer, f2 varchar);",<not logged>
    
    
    
    db_replica=#  insert into t1 values (1,'one');
    
    INSERT 0 1
    
    db_replica=#  insert into t1 values (2,'two');
    
    INSERT 0 1
    
    db_replica=#  insert into t1 values (3,'three');
    
    INSERT 0 1
    
    2020-01-29 22:52:19.261 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,5,1,WRITE,INSERT,,,"insert into t1 values (1,'one');",<not logged>
    
    20-01-29 22:52:38.145 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,6,1,WRITE,INSERT,,,"insert into t1 values (2,'two');",<not logged>
    
    2020-01-29 22:52:44.988 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,7,1,WRITE,INSERT,,,"insert into t1 values (3,'three');",<not logged>
    
    
    
    db_replica=# select * from t1 where f1 >= 2;
    
     f1 |  f2
    
    ----+-------
    
      2 | two
    
      3 | three
    
    (2 rows)
    
    
    
    2020-01-29 22:53:09.161 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,9,1,READ,SELECT,,,select * from t1 where f1 >= 2;,<not logged>
    
    
    
    db_replica=# grant select on t1 to usr_replica;
    
    GRANT
    
    
    
    2020-01-29 22:54:25.283 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,13,1,ROLE,GRANT,,,grant select on t1 to usr_replica;,<not logged>
    
    
    
    db_replica=# alter table t1 add f3 date;
    
    ALTER TABLE
    
    
    
    2020-01-29 22:55:17.440 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,23,1,DDL,ALTER TABLE,,,alter table t1 add f3 date;,<not logged>
    
    
    
    db_replica=# checkpoint;
    
    CHECKPOINT
    
    
    
    2020-01-29 22:55:50.349 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,33,1,MISC,CHECKPOINT,,,checkpoint;,<not logged>
    
    
    
    db_replica=# vacuum t1;
    
    VACUUM
    
    
    
    2020-01-29 22:56:03.007 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,34,1,MISC,VACUUM,,,vacuum t1;,<not logged>
    
    
    
    db_replica=# show log_statement;
    
     log_statement
    
    ---------------
    
     none
    
    
    
    2020-01-29 22:56:14.740 AEDT 4710 db_replica postgres [local] psql LOG:  AUDIT: SESSION,36,1,MISC,SHOW,,,show log_statement;,<not logged>

    上記のデモに示されているように、ログエントリは、パラメータlog_statementが設定されている場合にのみサーバーのバックグラウンドログファイルに書き込まれますが、この場合は構成されていませんが、監査メッセージはvirtueによって書き込まれます。デモで証明されているようにpgaudit.logパラメータの。 PostgreSQL内のすべてのデータベース監査要件を満たすために利用できるより強力なオプションがあります。これは、ここまたはgithubリポジトリのpgauditの公式ドキュメントに従って構成できます。pg_repack

    これは、PostgreSQLクラスターの一般的な状態の管理と維持に直接関与している多くのPostgreSQLエンジニアの間で人気のある拡張機能です。その理由については後で説明しますが、この拡張機能は、データベースの再編成を必要とする非常に大規模なPostgreSQLデータベースクラスターの厄介な懸念の1つである、PostgreSQLデータベース内のデータベースの肥大化を取り除く機能を提供します。

    PostgreSQLデータベースは絶え間なく大量の書き込み(更新と削除)を受けるため、新しいバージョンの行が挿入されている間、古いデータは削除済みとしてマークされますが、古いデータは実際にはデータブロック。これには、バキュームと呼ばれる定期的なメンテナンス操作が必要です。これは、「削除済みとしてマークされた」行をすべてクリアするバックグラウンドで実行される自動化された手順です。このプロセスは、口語的にガベージコレクションと呼ばれることもあります。

    バキューム処理は、通常、忙しい時間帯のデータベース操作に取って代わられます。データベース操作を優先する最も制限の少ないバキューム方法では、「削除済みとしてマークされた」行が多数発生し、データベースが「データベースの肥大化」と呼ばれる不均衡に成長します。 VACUUM FULLと呼ばれる強力なバキューム処理がありますが、その結果、処理中のデータベースオブジェクトの排他ロックが取得され、そのオブジェクトのデータベース操作が停止します。

    pg_repack

    pg_repackは、通常のバキュームプロセスの役割を果たしますが、データベースの排他ロックを取得しないことでVACUUM FULLの効率を提供するため、PostgreSQLDBAおよびエンジニアの間でヒットしているのはこのためです。オブジェクト、要するに、それはオンラインで動作します。ここの公式ドキュメントでは、データベースを再編成する他の方法について詳しく説明していますが、以下の簡単なデモでは、理解を深めるために適切な状況を説明します。ターゲットテーブルには、PRIMARY KEYとして定義された少なくとも1つの列が必要です。これは、ほとんどの本番データベース設定の一般的な基準です。

    デモ

    基本的なデモは、テスト環境でのpg_repackのインストールと使用法を示しています。このデモでは、このブログの公開時点でのこの拡張機能の最新バージョンであるpg_repackのバージョン1.4.5を使用しています。デモテーブルt1には、最初は80000行があり、テーブルの5行ごとに削除するdeleteの大規模な操作が実行されます。 pg_repackを実行すると、前後のテーブルのサイズが表示されます。

    mydb=# CREATE EXTENSION pg_repack;
    
    CREATE EXTENSION
    
    
    
    mydb=# create table t1 (no integer primary key, f_name VARCHAR(20), l_name VARCHAR(20), d_o_b date);
    
    CREATE TABLE
    
    mydb=# insert into t1 (select generate_series(1,1000000,1),'a'||
    
    mydb(# generate_series(1,1000000,1),'a'||generate_series(1000000,1,-1),
    
    mydb(# cast( now() - '1 year'::interval * random()  as date ));
    
    INSERT 0 1000000
    
    
    
    mydb=# SELECT pg_size_pretty( pg_total_relation_size('t1'));
    
     pg_size_pretty
    
    ----------------
    
     71 MB
    
    (1 row)
    
    
    
    mydb=# CREATE or replace FUNCTION delete5() RETURNS void AS $$
    
    mydb$# declare
    
    mydb$# counter integer := 0;
    
    mydb$# BEGIN
    
    mydb$#
    
    mydb$#  while counter <= 1000000
    
    mydb$# loop
    
    mydb$# delete from t1 where no=counter;
    
    mydb$# counter := counter + 5;
    
    mydb$# END LOOP;
    
    mydb$# END;
    
    mydb$# $$ LANGUAGE plpgsql;
    
    CREATE FUNCTION

    delete5関数は、5カウントをインクリメントするカウンターを使用してt1テーブルから200000行を削除します

    mydb=# select delete5();
    
     delete5
    
    ------
    
    
    
    (1 row)
    
    mydb=# SELECT pg_size_pretty( pg_total_relation_size('t1'));
    
     pg_size_pretty
    
    ----------------
    
     71 MB
    
    (1 row)
    
    
    
    $ pg_repack -t t1 -N -n -d mydb -p 5433
    
    INFO: Dry run enabled, not executing repack
    
    INFO: repacking table "public.t1"
    
    
    
    $ pg_repack -t t1 -n -d mydb -p 5433
    
    INFO: repacking table "public.t1"
    
    
    
    mydb=# SELECT pg_size_pretty( pg_total_relation_size('t1'));
    
     pg_size_pretty
    
    ----------------
    
     57 MB
    
    (1 row)

    上記のように、delete5関数を実行した後、テーブルの元のサイズは変更されません。これは、行がテーブルにまだ存在していることを示しています。 pg_repackを実行すると、t1テーブルから「削除済みとしてマークされた」行がクリアされ、t1テーブルのサイズが57MBに減少します。 pg_repackのもう1つの優れた点は、-Nフラグを使用したドライランのオプションです。これを使用すると、実際の実行中に何が実行されるかを確認できます。

    HypoPG

    次の興味深い拡張機能は、プロプライエタリデータベースサーバー間での非表示インデックスと呼ばれる一般的な概念と同じです。 HypoPG拡張機能を使用すると、DBAは、仮想インデックス(存在しない)を導入した場合の効果と、1つ以上のクエリのパフォーマンスが向上するかどうかを確認できます。そのため、HypoPGという名前が付けられています。

    架空のインデックスの作成にはCPUまたはディスクリソースは必要ありませんが、接続のプライベートメモリを消費します。仮想インデックスはデータベースカタログテーブルに格納されないため、テーブルの肥大化の影響はありません。このため、EXPLAIN ANALYZEステートメントでは架空のインデックスを使用できませんが、問題のある特定のクエリで潜在的なインデックスが使用されるかどうかを評価するには、プレーンなEXPLAINが適しています。これは、HypoPGがどのように機能するかを説明する簡単なデモです。

    デモ

    generate_seriesを使用して100000行を含むテーブルを作成し、いくつかの簡単なクエリを実行して、仮想インデックスがある場合とない場合のコスト見積もりの​​違いを示します。

    olap=# CREATE EXTENSION hypopg;
    
    CREATE EXTENSION
    
    
    
    olap=# CREATE TABLE stock (id integer, line text);
    
    CREATE TABLE
    
    
    
    olap=# INSERT INTO stock SELECT i, 'line ' || i FROM generate_series(1, 100000) i;
    
    INSERT 0 100000
    
    
    
    olap=# ANALYZE STOCK;
    
    ANALYZE
    
    
    
    olap=#  EXPLAIN SELECT line FROM stock WHERE id = 1;
    
                           QUERY PLAN
    
    ---------------------------------------------------------
    
     Seq Scan on stock  (cost=0.00..1791.00 rows=1 width=10)
    
       Filter: (id = 1)
    
    (2 rows)
    
    olap=# SELECT * FROM hypopg_create_index('CREATE INDEX ON stock (id)') ;
    
     indexrelid |       indexname
    
    ------------+-----------------------
    
          25398 | <25398>btree_stock_id
    
    (1 row)
    
    
    
    olap=# EXPLAIN SELECT line FROM stock WHERE id = 1;
    
                                         QUERY PLAN
    
    ------------------------------------------------------------------------------------
    
     Index Scan using <25398>btree_stock_id on stock  (cost=0.04..8.06 rows=1 width=10)
    
       Index Cond: (id = 1)
    
    (2 rows)
    
    
    
    olap=# EXPLAIN ANALYZE SELECT line FROM stock WHERE id = 1;
    
                                                 QUERY PLAN
    
    ----------------------------------------------------------------------------------------------------
    
     Seq Scan on stock  (cost=0.00..1791.00 rows=1 width=10) (actual time=0.028..41.877 rows=1 loops=1)
    
       Filter: (id = 1)
    
       Rows Removed by Filter: 99999
    
     Planning time: 0.057 ms
    
     Execution time: 41.902 ms
    
    (5 rows)
    
    
    
    olap=# SELECT indexname, pg_size_pretty(hypopg_relation_size(indexrelid))
    
    olap-#   FROM hypopg_list_indexes() ;
    
           indexname       | pg_size_pretty
    
    -----------------------+----------------
    
     <25398>btree_stock_id | 2544 kB
    
    (1 row)
    
    
    
    olap=# SELECT pg_size_pretty(pg_relation_size('stock'));
    
     pg_size_pretty
    
    ----------------
    
     4328 kB
    
    (1 row)

    上記の展示は、単純なクエリを最適化するためにテーブルの「id」フィールドにインデックスを追加することにより、推定総コストを1791から8.06に削減する方法を示しています。また、クエリをリアルタイムで実行するEXPLAIN ANALYZEを使用してクエリを実行すると、インデックスが実際には使用されないことも証明されます。拡張機能のhypopg_list_indexes関数を使用して、インデックスが占めるディスク容量のおおよその量を確認する方法もあります。

    HypoPGには、仮想インデックスを管理するための他のいくつかの機能があり、それに加えて、テーブルをパーティション化することで、大きなデータセットをフェッチするクエリのパフォーマンスが向上するかどうかを確認する方法も提供します。 HypoPG拡張機能には架空のパーティショニングオプションがあり、公式ドキュメントを参照することでさらに多くのオプションを利用できます。

    結論

    パート1で述べたように、PostgreSQLは何年にもわたって進化し、ネイティブソースコードとプラグアンドプレイ拡張機能の両方で急速に開発され、より大きく、より良く、より速くなっています。新しいPostgreSQLのオープンソースバージョンは、IT CAPEXとOPEXを削減するために、主要なプロプライエタリデータベースサーバーの1つを実行している多くのITショップに最適です。

    監視から高可用性、スケーリングから人間が読める形式へのバイナリデータファイルのダンプまで、さまざまな機能を提供するPostgreSQL拡張機能がたくさんあります。上記のデモンストレーションが、PostgreSQLデータベースの最大の可能性とパワーに大きな光を当てることが期待されています。


    1. Linuxのカスタムキーストアで保護されたデータの使用

    2. postgresフロントエンドでタブを指定する方法COPY

    3. 各行の列値に基づいて行を複製する

    4. WHERE条件の別のテーブルをJOINで使用したSQLDELETE