<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>サードパーティーソフトウェア &#8211; SRA OSS Tech Blog</title>
	<atom:link href="https://www.sraoss.co.jp/tech-blog/tag/d8507fd66e295f5cffc9dba6587a4af2/feed/index.xml" rel="self" type="application/rss+xml" />
	<link>https://www.sraoss.co.jp/tech-blog/</link>
	<description>OSS専門の技術ブログ。PostgreSQL、Zabbixを中心に10種類以上のOSSの解説、アップデート情報、運用ヒントを提供。</description>
	<lastBuildDate>Tue, 13 Oct 2026 06:09:56 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/07/blog-icon-150x150.png</url>
	<title>サードパーティーソフトウェア &#8211; SRA OSS Tech Blog</title>
	<link>https://www.sraoss.co.jp/tech-blog/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>多機能ミドルウェア「Pgpool-II」セミナー【3/24 開催】</title>
		<link>https://www.sraoss.co.jp/tech-blog/info/seminar_20260324/</link>
		
		<dc:creator><![CDATA[sraoss]]></dc:creator>
		<pubDate>Tue, 10 Mar 2026 05:50:40 +0000</pubDate>
				<category><![CDATA[おしらせ]]></category>
		<category><![CDATA[Pgpool-II]]></category>
		<category><![CDATA[イベント]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=20183</guid>

					<description><![CDATA[&#8211; PostgreSQLの可用性・性能・運用性を“もう一段”引き上げたい方へ &#8211; PostgreSQLをより強化したい方に向けて、20年以上進化を続ける 多機能ミドルウェア「Pgpool-II」の ...]]></description>
										<content:encoded><![CDATA[<div style="font-family: 'Helvetica Neue', Arial, 'Hiragino Kaku Gothic ProN', 'Hiragino Sans', Meiryo, sans-serif; color: #333; line-height: 1.8; max-width: 800px; margin: 0 auto; padding: 10px;">
<div style="text-align: center; margin-bottom: 25px;">
<p style="color: #000; font-size: 1.15rem; margin-bottom: 15px; font-weight: bold; text-align: left;">&#8211; PostgreSQLの可用性・性能・運用性を“もう一段”引き上げたい方へ &#8211;</p>
<p style="font-size: 1.05rem; font-weight: normal; color: #000; line-height: 1.4; text-align: left;">PostgreSQLをより強化したい方に向けて、20年以上進化を続ける<br />
多機能ミドルウェア「Pgpool-II」の全容をぎゅっと詰め込んだウェビナーを開催します！</p>
</div>
<div style="margin-bottom: 20px; text-align: center;">
<p><a href="https://www.sraoss.co.jp/event_seminar/detail/2026_0324/?utm_source=techblog&amp;utm_medium=email&amp;utm_campaign=pgpool0324" target="_blank" rel="noopener"><img fetchpriority="high" decoding="async" class="alignnone" style="width: 100%; height: auto; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); max-width: 750px;" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2026/03/Pgpool-II-seminar_banner_fix_20260324.png" alt="Pgpool-IIセミナー 3/24開催" width="1280" height="720" /></a></p>
</div>
<div style="text-align: center; margin-bottom: 35px; width: 100%; display: flex; justify-content: center;"><a style="background: #fff; color: #0056b3; border: 2px solid #0056b3; text-decoration: none; padding: 12px 35px; border-radius: 50px; font-weight: bold; display: inline-block; box-shadow: 0 2px 5px rgba(0,0,0,0.05); margin: 0 auto;" href="https://www.sraoss.co.jp/event_seminar/detail/2026_0324/?utm_source=techblog&amp;utm_medium=email&amp;utm_campaign=pgpool0324" target="_blank" rel="noopener"> セミナーの内容を見てみる </a></div>
<p><span id="more-20183"></span></p>
<hr style="border: 0; border-top: 1px solid #eee; margin: 40px 0;" />
<h3 style="font-size: 1.2rem; border-left: 5px solid #0056b3; padding-left: 12px; margin-bottom: 20px; color: #000;">開催概要</h3>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 40px; border: 1px solid #ddd; color: #000;">
<tbody>
<tr style="border-bottom: 1px solid #ddd;">
<th style="text-align: left; padding: 15px; background: #fafafa; width: 20%; border-right: 1px solid #ddd; color: #000;">タイトル</th>
<td style="padding: 15px; font-weight: normal; color: #000; font-size: 1rem;">PostgreSQLを止めない：Pgpool-IIで実現する高可用・高性能 PostgreSQL運用 <span style="font-size: 1rem;">～ Pgpool-II開発者が語る最新動向 ～</span></td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<th style="text-align: left; padding: 15px; background: #fafafa; border-right: 1px solid #ddd; color: #000;">日時</th>
<td style="padding: 15px; color: #000;">2026 年 3 月 24 日（火） 14:00 ～ 15:20</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<th style="text-align: left; padding: 15px; background: #fafafa; border-right: 1px solid #ddd; color: #000;">視聴方法</th>
<td style="padding: 15px; color: #000;">Zoom Webinar によるライブ配信</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<th style="text-align: left; padding: 15px; background: #fafafa; border-right: 1px solid #ddd; color: #000;">費用</th>
<td style="padding: 15px; color: #000;">無料（事前登録制）</td>
</tr>
<tr style="border-bottom: 1px solid #ddd;">
<th style="text-align: left; padding: 15px; background: #fafafa; border-right: 1px solid #ddd; color: #000;">主催</th>
<td style="padding: 15px; color: #000;">株式会社SRA OSS</td>
</tr>
</tbody>
</table>
<div style="background: #fdfaf5; border: 1px solid #f3e5d0; border-radius: 8px; padding: 25px; margin-bottom: 40px;">
<h3 style="margin-top: 0; color: #d35400; font-size: 1.2rem;">★ ここがポイント</h3>
<ul style="list-style: none; padding: 0; margin: 0;">
<li style="margin-bottom: 10px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Pgpool-IIを活用した可用性・クラスタ構成の“具体像”がつかめます</li>
<li style="margin-bottom: 10px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> オンプレ／クラウドの事例から、構成のヒントを得られます</li>
<li style="margin-bottom: 10px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Pgpool-II 4.7の新機能＆コミュニティ動向をまとめてキャッチできます</li>
<li style="margin-bottom: 10px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 疑問に思ったことは ”すぐ聞ける”Live Q&amp;Aを実施</li>
<li style="margin-bottom: 0;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> セミナーにご参加＆アンケート回答いただいた方には、講演資料をプレゼント</li>
</ul>
</div>
<div style="margin-bottom: 40px; padding: 0 10px;">
<p>PostgreSQLは、信頼性と拡張性に優れたオープンソースのRDBMSとして広く活用されていますが、標準機能だけでは運用上の課題に直面することがあります。</p>
<p>そこで、PostgreSQLの可用性と運用性を補完するために生まれたツールがPgpool-IIです。<br />
Pgpool-IIは、PostgreSQLに冗長化・負荷分散・フェイルオーバーを提供する多機能ミドルウェアです。<br />
2003年に公開されてから、20年以上にわたり改善を重ねながら進化し、現在ではミッションクリティカルなシステムにも採用されるソフトウェアへと成長しました。</p>
<p>本セミナーでは、<strong>「Pgpool-IIがなぜ必要なのか」「どのような構成で効果を発揮するのか」「オンプレ／クラウドでどう活用できるのか」</strong>を、事例とともに解説します。<br />
ぜひライブでご覧ください！</p>
</div>
<h3 style="font-size: 1.2rem; border-left: 5px solid #0056b3; padding-left: 12px; margin-bottom: 25px;">プログラム</h3>
<div style="margin-bottom: 40px; background: #fff; border: 1px solid #eee; padding: 20px; border-radius: 8px;">
<h4 style="margin: 0 0 15px 0; color: #0047BB; font-size: 1.1rem;">Pgpool-IIの歩みと進化 ー 基本機能から最新ユースケースまで</h4>
<div style="display: flex; gap: 20px; align-items: flex-start; margin-bottom: 15px;">
<div style="width: 100px; height: 100px; flex-shrink: 0; background: #fff; border-radius: 50%; border: 1px solid #eee; display: flex; align-items: center; justify-content: center; overflow: hidden; padding: 2px;"><img decoding="async" style="width: 100%; height: 100%; object-fit: cover; object-position: center top; border-radius: 50%;" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2023/08/pen_2-213x300.jpg" alt="彭 博 様" /></div>
<div style="font-size: 0.95rem; line-height: 1.6;">
<p>Pgpool-IIはオンプレ用途に限らず、クラウド上での運用やマネージドサービスと組み合わせることで、多様なユースケースで活用できます。それらの代表的なケースと導入メリットを解説します。</p>
<p>また近年は、遠隔地での性能改善など、従来構成では顕在化しにくかった新たな要望も増えています。こうしたユーザニーズに対し、Pgpool-IIがどのように機能強化や改善の取り組みを進めているのか、今後の方向性についてもご紹介します。</p>
<p style="margin-top: 10px; color: #666;">講師：株式会社SRA OSS 彭 博（Pgpool-II開発者／コミュニティリーダー）</p>
</div>
</div>
</div>
<div style="margin-bottom: 40px; background: #fff; border: 1px solid #eee; padding: 20px; border-radius: 8px;">
<h4 style="margin: 0 0 15px 0; color: #0047BB; font-size: 1.1rem;">Pgpool-II 4.7 新機能解説</h4>
<div style="display: flex; gap: 20px; align-items: flex-start; margin-bottom: 15px;">
<div style="width: 100px; height: 100px; flex-shrink: 0; background: #fff; border-radius: 50%; border: 1px solid #eee; display: flex; align-items: center; justify-content: center; overflow: hidden; padding: 2px;"><img decoding="async" style="width: 100%; height: 100%; object-fit: cover; object-position: center top; border-radius: 50%;" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2025/06/6104221bfe5d36c6654d592df84bc603-218x300.jpg" alt="越野 太基 様" /></div>
<div style="font-size: 0.95rem; line-height: 1.6;">
<p>2025年12月にリリースされたPgpool-II 4.7では、性能向上や構成の柔軟性を高めるための改善が複数盛り込まれています。本セッションでは、4.7の主要な追加・改善点を整理しながら解説します。</p>
<p style="margin-top: 10px; color: #666;">講師：株式会社SRA OSS 越野 太基（Pgpool-II開発者）</p>
</div>
</div>
</div>
<div style="padding-left: 20px; border-left: 3px solid #eee; margin-bottom: 40px; font-size: 0.95rem;">
<ul>
<li>Pgpool-II活用を支えるSRA OSSのサービスご紹介</li>
<li>Live Q&amp;A　⇒ セミナー中にいただいたご質問に対して、講師よりまとめ・補足を行います。</li>
</ul>
</div>
<p>PostgreSQLを運用中、またはPostgreSQLの情報を収集中の方がお近くにいらっしゃいましたら、ぜひお声がけいただき、皆さまにご参加いただけますと幸いです。よろしくお願いいたします。</p>
<div style="text-align: center; background: #f8f9fa; padding: 25px 20px; border-radius: 12px; margin-top: 50px; border: 1px solid #eee;">
<p style="margin-bottom: 20px; font-weight: bold; font-size: 1.35rem; color: #000;">▼ セミナー申込はこちら</p>
<p><a style="background: #e67e22; color: #fff !important; text-decoration: none; padding: 22px 60px; font-size: 1.45rem; font-weight: bold; border-radius: 5px; display: inline-block; box-shadow: 0 4px 10px rgba(0,0,0,0.1); margin: 0 auto;" href="https://info.sraoss.co.jp/seminar/registration/20260324" target="_blank" rel="noopener">無料で申し込む</a></p>
<p style="margin-top: 30px; font-size: 0.85rem; color: #666;">お問い合わせ：株式会社SRA OSS セミナー担当<br />
E-mail: pub@sraoss.co.jp<br />
<a href="https://www.sraoss.co.jp/" target="_blank" rel="noopener">https://www.sraoss.co.jp/</a></p>
</div>
</div>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>pg_store_plans (PostgreSQL 実行計画記録ツール)</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/pg_store_plans/</link>
		
		<dc:creator><![CDATA[北山 貴広 (Takahiro Kitayama)]]></dc:creator>
		<pubDate>Mon, 05 Feb 2024 02:00:45 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[pg_store_plans]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=14365</guid>

					<description><![CDATA[pg_store_plans とは pg_store_plans は PostgreSQL の実行計画を取得するためのオープンソースソフトウェアで、PostgreSQL の拡張機能として実装されています。 開発元はNTT ...]]></description>
										<content:encoded><![CDATA[<h2>pg_store_plans とは</h2>
<p>pg_store_plans は PostgreSQL の実行計画を取得するためのオープンソースソフトウェアで、PostgreSQL の拡張機能として実装されています。<br />
開発元はNTT OSSセンターのDBチームで、pg_hint_plan、pg_bulkload、pg_rman、pg_statsinfo などの著名なソフトウェアを開発しています。</p>
<p><span id="more-14365"></span></p>
<p>公式サイトは<a href="https://github.com/ossc-db/pg_store_plans">こちら</a>で、ドキュメントは英語版のみ<a href="https://ossc-db.github.io/pg_store_plans/">存在</a>します。</p>
<h2>pg_store_plans の機能</h2>
<p>pg_store_plans は一言で言うと pg_stat_statements と一緒に使用して pg_stat_statements で取得できる情報に実行計画の情報を付加するものです。<br />
pg_store_plans では、実行計画(プラン)は plan 列でわかりますがSQL文本体の情報は無いため、SQL文の情報は pg_store_plans の queryid 列を使って pg_stat_statements の query 列を参照する必要がありますので、本記事では pg_store_plans とあわせて pg_stat_statements も一緒にインストールして検証します。</p>
<h3>pg_store_plansビュー</h3>
<p>下記に pg_store_plansビューの列を示します。<br />
pg_stat_statements と異なる pg_stat_plans ビュー独自の列は、planid(プランのハッシュコード)、plan(プランそのもの)、first_call(最も過去に呼ばれた時刻)、last_call(最も最近呼ばれた時刻)の4列のみで、その他の列は pg_stat_statements とほぼ同一の名称ですが実際に取得できる情報は、pg_store_plans では該当のプラン(実行計画)が使用された時、pg_stat_statements では該当のクエリが使用された時と違いがあり、後の実行例で違いを説明します。</p>
<h4>pg_store_plansビュー定義</h4>
<table>
<thead>
<tr>
<th>pg_store_plans 独自</th>
<th>列</th>
<th>型</th>
<th>参照先</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>userid</td>
<td>oid</td>
<td>pg_authid.oid</td>
<td>SQL文を実行したユーザのOID</td>
</tr>
<tr>
<td></td>
<td>dbid</td>
<td>oid</td>
<td>pg_database.oid</td>
<td>SQL文が実行されたデータベースのOID</td>
</tr>
<tr>
<td></td>
<td>queryid</td>
<td>bigint</td>
<td></td>
<td>コアによって生成されたクエリID。<br />
compute_query_id が「no」に設定されている場合、pg_store_plan は暗黙的に無効になります。 これは、pg_stat_statements との結合キーとして使用できます。</td>
</tr>
<tr>
<td>*</td>
<td>planid</td>
<td>bigint</td>
<td></td>
<td>正規化されたプラン表現から計算されるプランのハッシュコード</td>
</tr>
<tr>
<td>*</td>
<td>plan</td>
<td>text</td>
<td></td>
<td>代表的なプランのテキストです。書式は設定パラメータ pg_store_plans.plan_format で指定します。</td>
</tr>
<tr>
<td></td>
<td>calls</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文が実行された回数</td>
</tr>
<tr>
<td></td>
<td>total_time</td>
<td>double precision</td>
<td></td>
<td>プランを使用したSQL文の処理に費やした時間（ミリ秒単位）</td>
</tr>
<tr>
<td></td>
<td>rows</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によって取得された、あるいは影響を受けた行の総数</td>
</tr>
<tr>
<td></td>
<td>shared_blks_hit</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によってヒットした共有ブロックキャッシュの総数</td>
</tr>
<tr>
<td></td>
<td>shared_blks_read</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によって読み込まれた共有ブロックの総数</td>
</tr>
<tr>
<td></td>
<td>shared_blks_dirtied</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によってダーティ状態となった共有ブロックの総数</td>
</tr>
<tr>
<td></td>
<td>shared_blks_written</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によって書き込まれた共有ブロックの総数</td>
</tr>
<tr>
<td></td>
<td>local_blks_hit</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によってヒットしたローカルブロックキャッシュの総数</td>
</tr>
<tr>
<td></td>
<td>local_blks_read</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によって読み込まれたローカルブロックの総数</td>
</tr>
<tr>
<td></td>
<td>local_blks_dirtied</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によってダーティ状態となったローカルブロックの総数</td>
</tr>
<tr>
<td></td>
<td>local_blks_written</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によって書き込まれたローカルブロックの総数</td>
</tr>
<tr>
<td></td>
<td>temp_blks_read</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によって読み込まれた一時ブロックの総数</td>
</tr>
<tr>
<td></td>
<td>temp_blks_written</td>
<td>bigint</td>
<td></td>
<td>プランを使用したSQL文によって書き込まれた一時ブロックの総数</td>
</tr>
<tr>
<td></td>
<td>blk_read_time</td>
<td>double precision</td>
<td></td>
<td>プランを使用したSQL文がデータファイルブロックの読み取りに費やした総時間（ミリ秒単位）（track_io_timing が有効な場合。無効であればゼロ）</td>
</tr>
<tr>
<td></td>
<td>blk_write_time</td>
<td>double precision</td>
<td></td>
<td>プランを使用したSQL文がデータファイルブロックの書き出しに費やした総時間（ミリ秒単位）（track_io_timing が有効な場合。無効であればゼロ）</td>
</tr>
<tr>
<td></td>
<td>temp_blk_read_time</td>
<td>double precision</td>
<td></td>
<td>プランを使用したSQL文が一時ファイルブロックの読み取りに費やした総時間（ミリ秒単位）（track_io_timing が有効な場合。無効であればゼロ）</td>
</tr>
<tr>
<td></td>
<td>temp_blk_write_time</td>
<td>double precision</td>
<td></td>
<td>プランを使用したSQL文が一時ファイルブロックの書き出しに費やした総時間（ミリ秒単位） （track_io_timing が有効な場合。無効であればゼロ）</td>
</tr>
<tr>
<td>*</td>
<td>first_call</td>
<td>timestamp with time zone</td>
<td></td>
<td>このプランを使用したクエリの最も最近の呼び出しのタイムスタンプ</td>
</tr>
<tr>
<td>*</td>
<td>last_call</td>
<td>timestamp with time zone</td>
<td></td>
<td>このプランを使用したクエリの直近の呼び出しのタイムスタンプ</td>
</tr>
</tbody>
</table>
<h3>pg_store_plans_infoビュー</h3>
<p>pg_store_plans モジュール自体の統計は追跡され、pg_store_plans_info というビューを通して利用することができます。 このビューは、1行のみが含まれます。</p>
<h4>pg_store_plans_infoビュー定義</h4>
<table>
<thead>
<tr>
<th>列名</th>
<th>型</th>
<th>参照先</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>dealloc</td>
<td>bigint</td>
<td></td>
<td>pg_store_plans.max よりも多くの個別のステートメントが観察されたために、最も実行頻度の低いステートメントに関する pg_store_plans エントリの割り当てが解除された合計回数</td>
</tr>
<tr>
<td>stats_reset</td>
<td>timestamp with time zone</td>
<td></td>
<td>pg_store_plans ビュー内のすべての統計が最後にリセットされた時刻</td>
</tr>
</tbody>
</table>
<h3>主なpg_store_plans関数</h3>
<h4>pg_store_plans_reset() returns void</h4>
<p>pg_store_plans_resetは、pg_store_plansによってこれまでに収集された全ての統計情報を破棄します。デフォルトでは、スーパーユーザのみがこの関数を実行できます。</p>
<h3>pg_store_plans サポート対象バージョン</h3>
<p>pg_store_plans のバージョン体系は、 PostgreSQL 9.4 から PostgreSQL 11 までの複数のメジャーバージョンをサポート対象とする pg_store_plans 1.3.1 と、メジャーバージョンで導入された新機能に対応するため、単独の独立した PostgreSQL をサポートする pg_store_plans 1.4 以降とに分かれています。一旦リリースされた後はバグ修正のみが実施される PostgreSQL と同様のリリースモデルを採用しています。<br />
pg_store_plans のバージョンとサポート対象の PostgreSQL のバージョンの対応は以下の通りです。</p>
<table>
<thead>
<tr>
<th>pg_store_plans バージョン</th>
<th>サポート対象 PostgreSQL バージョン</th>
<th>rpm対象OS</th>
</tr>
</thead>
<tbody>
<tr>
<td>pg_store_plans 1.7</td>
<td>PostgreSQL 15</td>
<td>RHEL 8.x</td>
</tr>
<tr>
<td>pg_store_plans 1.6.1</td>
<td>PostgreSQL 14</td>
<td>RHEL/CentOS 7.x, RHEL 8.x, RHEL 9.x</td>
</tr>
<tr>
<td>pg_store_plans 1.5</td>
<td>PostgreSQL 13</td>
<td></td>
</tr>
<tr>
<td>pg_store_plans 1.4</td>
<td>PostgreSQL 12</td>
<td></td>
</tr>
<tr>
<td>pg_store_plans 1.3.1</td>
<td>PostgreSQL 9.4, 9.5, 9.6, 10, 11</td>
<td></td>
</tr>
</tbody>
</table>
<h2>pg_store_plans インストール方法(rpmパッケージ)</h2>
<p>pg_store_plans のインストール方法は、大きく分けてソースコードからのインストールと、pg_store_plansのサイトに掲載されている rpm からのインストールがあります。<br />
今回は rpm からのインストールをご紹介します。<br />
下記のインストールの実行例では Rocky Linux release 8.9, PostgreSQL 15.5, pg_store_plans 1.7, pg_stats_statements 1.10 を使用して説明します。<br />
Rocky Linux から直接インターネット接続が可能な環境を使用しています。<br />
PostgreSQL 公式 Yum リポジトリから PostgreSQL 15.5 は contrib も含めて既にインストールとDB初期化が済んでいるものとします。</p>
<h3>pg_store_plans のインストール</h3>
<p>PostgreSQL 15用の pg_store_plans は<a href="https://github.com/ossc-db/pg_store_plans/releases/tag/1.7">こちら</a>からダウンロード可能で、pg_store_plans15-1.7-1.el8.x86_64.rpm<br />
をローカルディスクにダウンロードした後、dnf localinstall で rpm ファイルをインストールします。</p>
<pre># wget https://github.com/ossc-db/pg_store_plans/releases/download/1.7/pg_store_plans15-1.7-1.el8.x86_64.rpm
:(略)
2023-12-25 15:19:00 (70.7 MB/s) - `pg_store_plans15-1.7-1.el8.x86_64.rpm' へ保存完了 [41492/41492]
# dnf localinstall pg_store_plans15-1.7-1.el8.x86_64.rpm
メタデータの期限切れの最終確認: 0:04:20 時間前の 2023年12月25日 15時15分07秒 に実施しました。
依存関係が解決しました。
=========================================================================================================================================
 パッケージ                           アーキテクチャー           バージョン                       リポジトリー                     サイズ
=========================================================================================================================================
インストール:
 pg_store_plans15                     x86_64                     1.7-1.el8                        @commandline                      41 k

トランザクションの概要
=========================================================================================================================================
インストール  1 パッケージ

合計サイズ: 41 k
インストール後のサイズ: 104 k
これでよろしいですか? [y/N]: y
パッケージのダウンロード:
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
  準備             :                                                                                                                 1/1
  インストール中   : pg_store_plans15-1.7-1.el8.x86_64                                                                               1/1
  scriptletの実行中: pg_store_plans15-1.7-1.el8.x86_64                                                                               1/1
  検証             : pg_store_plans15-1.7-1.el8.x86_64                                                                               1/1

インストール済み:
  pg_store_plans15-1.7-1.el8.x86_64

完了しました!
#
</pre>
<p>これで pg_store_plans がインストールできました。</p>
<h3>postgresql.conf の shared_preload_libraries に pg_store_plans と pg_stat_statements を追加して PostgreSQL を再起動、有効になっていることを確認</h3>
<p>shared_preload_libraries に共有ライブラリの pg_store_plans と pg_stat_statements を設定して PostgreSQL を再起動し、pg_store_plans と pg_stat_statements が有効になっていることを確認します。</p>
<pre>$ cd $PGDATA
$ vi postgresql.conf
shared_preload_libraries = 'pg_store_plans, pg_stat_statements'
# systemctl restart postgresql-15
$ psql
psql (15.5)
"help"でヘルプを表示します。

postgres=# show shared_preload_libraries;
     shared_preload_libraries
-----------------------------------
 pg_store_plans, pg_stat_statements
(1 行)
</pre>
<h3>CREATE EXTENSION を実行して拡張に登録</h3>
<p>検証に使う db1 データベースを作成後 psql で接続して、CREATE EXTENSION を実行します。拡張が登録されていることを確認します。</p>
<pre>$ createdb db1
$ psql db1
psql (15.5)
"help"でヘルプを表示します。

db1=# \dx
                    インストール済みの拡張一覧
  名前   | バージョン |  スキーマ  |             説明
---------+------------+------------+------------------------------
 plpgsql | 1.0        | pg_catalog | PL/pgSQL procedural language
(1 行)

db1=# CREATE EXTENSION pg_store_plans;
CREATE EXTENSION
db1=# CREATE EXTENSION pg_stat_statements;
db1=# \dx
                                              インストール済みの拡張一覧
        名前        | バージョン |  スキーマ  |                                  説明
--------------------+------------+------------+------------------------------------------------------------------------
 pg_stat_statements | 1.10       | public     | track planning and execution statistics of all SQL statements executed
 pg_store_plans     | 1.7        | public     | track plan statistics of all SQL statements executed
 plpgsql            | 1.0        | pg_catalog | PL/pgSQL procedural language
(3 行)

db1=# 
</pre>
<p>これで pg_store_plans と pg_stat_statements のインストールは完了しました。</p>
<h2>pg_store_plans と pg_stat_statements で取得可能な情報</h2>
<p>pgbench でDB初期化後、60秒間実行した後で、pg_store_plans と pg_stat_statements からのクエリと実行計画に関する情報を全実行時間の降順で並べて検索した例を以下に示します。<br />
全実行時間(total_exec_time)を呼び出し回数(calls)で割れば1実行当たりの実行時間の平均が算出可能です。<br />
このプランを使用した最初の実行時刻(first_call)と直近の実行時刻(last_call)でどの時間帯にどの実行計画が使われていたのかが判別できるので、この情報を定期的に保存しておけば自動ANALYZEにより途中で実行計画が変化してしまった場合に、通常時と悪化時の実行計画の両方が分かると思います。</p>
<pre>db1=# \x
拡張表示は on です。
db1=# SELECT ps.query, ps.calls, ps.total_plan_time, ps.total_exec_time, pp.first_call, pp.last_call, pp.plan FROM pg_store_plans pp, pg_stat_statements ps WHERE pp.queryid = ps.queryid ORDER BY ps.total_exec_time DESC;
-[ RECORD 1 ]---+------------------------------------------------------------------------------------------------------------------------
query           | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
calls           | 26618
total_plan_time | 0
total_exec_time | 1740.1858220000065
first_call      | 2024-01-11 14:29:53.134722+09
last_call       | 2024-01-11 14:30:53.116803+09
planid          | 2057223742
plan            | Update on pgbench_accounts  (cost=0.29..8.31 rows=0 width=0)
                |   -&gt;  Index Scan using pgbench_accounts_pkey on pgbench_accounts  (cost=0.29..8.31 rows=1 width=10)
                |         Index Cond: (aid = 75060)
-[ RECORD 2 ]---+------------------------------------------------------------------------------------------------------------------------
query           | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
calls           | 26618
total_plan_time | 0
total_exec_time | 335.3067220000009
first_call      | 2024-01-11 14:29:53.135509+09
last_call       | 2024-01-11 14:30:53.116962+09
planid          | 3391261630
plan            | Update on pgbench_tellers  (cost=0.00..1.13 rows=0 width=0)
                |   -&gt;  Seq Scan on pgbench_tellers  (cost=0.00..1.13 rows=1 width=10)
                |         Filter: (tid = 2)
-[ RECORD 3 ]---+------------------------------------------------------------------------------------------------------------------------
query           | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2
calls           | 26618
total_plan_time | 0
total_exec_time | 299.5543750000008
first_call      | 2024-01-11 14:29:53.138592+09
last_call       | 2024-01-11 14:30:53.117044+09
planid          | 1429560889
plan            | Update on pgbench_branches  (cost=0.00..1.01 rows=0 width=0)
                |   -&gt;  Seq Scan on pgbench_branches  (cost=0.00..1.01 rows=1 width=10)
                |         Filter: (bid = 1)
</pre>
<h3>同一のクエリで実行計画が変化した時の実行例</h3>
<p>同一のクエリで、複数の実行計画が使用された場合に pg_store_plans で取得できる情報と、pg_stat_statementsで取得できる情報の違いについて説明します。</p>
<p>10000件のtable1テーブルでインデックスが無い状態での検索とtable1_i1_idx インデックスを付加した状態での検索を実施してみます。<br />
テーブルを作成してデータを挿入した段階で、pg_store_plans_reset() 関数とpg_stat_statements_reset() 関数で pg_store_plans と pg_stat_statements の統計情報をリセットしています。</p>
<pre>db1=# CREATE TABLE table1 (i1 integer, t1 text);
CREATE TABLE
db1=# INSERT INTO table1 SELECT i, LEFT(MD5(i::text), 8) FROM generate_series(1,10000) as i;
INSERT 0 10000
db1=# SELECT pg_store_plans_reset();
 pg_store_plans_reset
----------------------

(1 行)

db1=# SELECT pg_stat_statements_reset();
 pg_stat_statements_reset
--------------------------

(1 行)

db1=# SELECT * FROM pg_store_plans_info;
 dealloc |          stats_reset
---------+-------------------------------
       0 | 2024-01-14 17:58:06.074107+09
(1 行)

db1=# SELECT * FROM pg_stat_statements_info;
 dealloc |         stats_reset
---------+------------------------------
       0 | 2024-01-14 17:58:10.57708+09
(1 行)
</pre>
<p>まず、インデックスが無い状態で検索しますので、Seq Scanの検索になります。その後、インデックスを付加した状態で検索しますので、Index Scanの検索になります。</p>
<pre>db1=# SELECT * FROM table1 WHERE i1 = 1;
 i1 |    t1
----+----------
  1 | c4ca4238
(1 行)

db1=# CREATE INDEX table1_i1_idx ON table1 (i1);
CREATE INDEX
db1=# SELECT * FROM table1 WHERE i1 = 1;
 i1 |    t1
----+----------
  1 | c4ca4238
(1 行)
</pre>
<p>行数が多いので見やすくするために拡張表示をonにして、該当の検索のみを抽出します。また、pg_store_plans の列には先頭に p_ 、pg_stat_statements の列には先頭に s_ を付けて区別しやすくします。</p>
<pre>db1=# \x
拡張表示は on です。
db1=# SELECT ps.query as s_query, pp.calls as p_calls, pp.total_time as p_total_time, ps.calls as s_calls, ps.total_plan_time s_total_plan_time, ps.total_exec_time as s_total_exec_time, pp.first_call as p_first_call, pp.last_call as p_last_call, pp.planid as p_planid, pp.plan as p_plan FROM pg_store_plans pp, pg_stat_statements ps WHERE pp.queryid = ps.queryid AND query LIKE 'SELECT * FROM table1%' ORDER BY ps.total_exec_time DESC;
-[ RECORD 1 ]-----+----------------------------------------------------------------------------
s_query           | SELECT * FROM table1 WHERE i1 = $1
p_calls           | 1
p_total_time      | 0.06921400000000001
s_calls           | 2
s_total_plan_time | 0
s_total_exec_time | 0.602927
p_first_call      | 2024-01-14 17:59:18.511485+09
p_last_call       | 2024-01-14 17:59:18.511485+09
p_planid          | 1345757884
p_plan            | Index Scan using table1_i1_idx on table1  (cost=0.29..8.30 rows=1 width=13)+
                  |   Index Cond: (i1 = 1)
-[ RECORD 2 ]-----+----------------------------------------------------------------------------
s_query           | SELECT * FROM table1 WHERE i1 = $1
p_calls           | 1
p_total_time      | 0.533713
s_calls           | 2
s_total_plan_time | 0
s_total_exec_time | 0.602927
p_first_call      | 2024-01-14 17:59:03.762229+09
p_last_call       | 2024-01-14 17:59:03.762229+09
p_planid          | 828508567
p_plan            | Seq Scan on table1  (cost=0.00..180.00 rows=1 width=13)                    +
                  |   Filter: (i1 = 1)

db1=#
</pre>
<p>pg_store_plans での calls(p_calls) と total_time(p_total_time )は該当の実行計画 (planid, plan) が使われた回数(1回ずつ)とその実行時間、pg_stat_statmentsでの calls(s_calls) と total_exec_time(s_total_exec_time )は(実行計画に関係無く)該当のクエリのすべての実行回数とすべての実行時間になるので、同じ calls でも取得される情報が異なることが分かります。<br />
また、first_call と last_call を見比べると、Seq Scan は 17:59:03、Index Scan は 17:59:18 になっていますので、Seq Scan の後に Index Scan が実行されたことが分かります。この first_call と last_call で該当の実行計画がいつ実行されたのかが分かります。</p>
<h2>まとめ</h2>
<p>pg_store_plans を pg_stat_statements と一緒に使った時に取得できる情報について説明しました。pg_stat_statements 単独だとクエリの実行回数と実行時間は取得できますが、pg_store_plans を一緒に使うことで、クエリの実行計画とその実行計画で実行された実行時間も分かりますので、遅延が発生した時の実行計画を調査するために有用な機能だと思います。</p>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>pgvectorの紹介</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/pgvector-intro/</link>
		
		<dc:creator><![CDATA[正野 裕大 (Yuta Masano)]]></dc:creator>
		<pubDate>Wed, 10 Jan 2024 00:42:45 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[pgvector]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=14204</guid>

					<description><![CDATA[pgvector(pgvector/pgvector: Open-source vector similarity search for Postgres)はベクトルデータの類似性検索機能を提供するPostgreSQLの ...]]></description>
										<content:encoded><![CDATA[<p>pgvector(<a href="https://github.com/pgvector/pgvector" target="_blank" rel="noopener">pgvector/pgvector: Open-source vector similarity search for Postgres</a>)はベクトルデータの類似性検索機能を提供するPostgreSQLの拡張機能です。データベース内にベクトルデータを格納し、特定のベクトルと最も近い要素を検索できます。近似値の算出はユークリッド距離、内積、コサイン距離による距離計算が提供されており、大規模なデータセットでの検索を高速化するためのインデックス機能も提供されています。レコメンデーションシステムや画像や文章の類似コンテンツ検索などのアプリケーション開発で有用になります。</p>
<p><span id="more-14204"></span></p>
<p><object data="https://gh-card.dev/repos/pgvector/pgvector.svg?link_target=_blank" type="image/svg+xml" width="300" height="150"></object></p>
<h2>pgvectorのインストールとセットアップ</h2>
<p>PostgreSQLにpgvectorをインストールします。OSはRocky Linux release 9.6、PostgreSQLは公式リポジトリからインストールしたバージョン16.1を使います。PostgreSQLを動作させるPATH等の環境変数は設定済みで、すでにPostgreSQLインスタンスは起動済みとします。</p>
<p>pgvectorはPostgreSQLの公式リポジトリ経由でPostgreSQL 16用のpgvector_16をパッケージからインストールします。</p>
<pre># dnf install pgvector_16</pre>
<p>続けて、testデータベースにpgvectorを登録します。</p>
<pre>$ psql -U postgres -d test
psql (16.1)
Type "help" for help.

test=# CREATE EXTENSION vector;
CREATE EXTENSION
test=# \dx
                             List of installed extensions
  Name   | Version |   Schema   |                     Description
---------+---------+------------+------------------------------------------------------
 plpgsql | 1.0     | pg_catalog | PL/pgSQL procedural language
 vector  | 0.5.1   | public     | vector data type and ivfflat and hnsw access methods</pre>
<h2>ベクトル列の作成とベクトルデータの挿入</h2>
<p>ベクトルデータを格納する列を作成します。pgvectorを登録すると、vector型が使えます。</p>
<pre>test=# CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3));</pre>
<p>vector(3)は3次元のベクトル、つまり、ベクトル空間を形成する基底ベクトルの個数が3つであることを意味します。ここでは3次元に指定していますが、最大で16000次元まで指定できます。<br />
異なる次元のベクトルを同じ列に保存したい場合は（vector(3)ではなく）vectorを使用できます。ただしその場合、<a href="https://www.postgresql.org/docs/16/indexes-expressional.html" target="_blank" rel="noopener">式に対するインデックス</a>や<a href="https://www.postgresql.org/docs/16/indexes-partial.html" target="_blank" rel="noopener">部分インデックス</a>を使って同じ次元数の行にしかインデックスを作成できません。</p>
<p>また、vector型の各要素はPostgreSQLのreal型と同様の単精度浮動小数点数で格納されます。より高精度のベクトルデータ格納したい場合は、vector型ではなく、double precision[]やnumeric[]型が使えます。ただし、高精度の値を格納はできますが、pgvectorが提供する機能を使う場合はvector型に<a href="https://www.postgresql.org/docs/16/sql-expressions.html#SQL-SYNTAX-TYPE-CASTS" target="_blank" rel="noopener">キャスト</a>する必要があり、格納したデータの検索時に精度値が喪失します。</p>
<pre>test=# CREATE TABLE items2 (id bigserial PRIMARY KEY, embedding double precision[]);</pre>
<p>データがvector型に変換可能であり、かつ、想定の次元数であることを保証するために<a href="https://www.postgresql.org/docs/16/ddl-constraints.html" target="_blank" rel="noopener">チェック制約</a>を追加してもよいでしょう。</p>
<pre>test=# ALTER TABLE items2 ADD CHECK (vector_dims(embedding::vector) = 3);</pre>
<p>次に、作成した列にベクトルデータを挿入します。vector型は以下のようにARRAYキーワードを使った配列や、[要素1,要素2,&#8230;]といった典型的な配列を表す文字列定数を解釈できます。<br />
なお、vector型ではなく、double precision[]やnumeric[]型を定義した場合は、ベクトルデータをPostgreSQLの配列リテラル定数で格納できます。</p>
<pre>test=# INSERT INTO items (embedding)
SELECT
  ARRAY[
  random() * 1000, -- 最初の次元のランダム値
  random() * 1000, -- 二番目の次元のランダム値
  random() * 1000  -- 三番目の次元のランダム値
]
FROM
  generate_series(1 , 10000);</pre>
<p>itemsテーブルに、乱数を使った3次元のベクトルデータを10000行挿入しました。</p>
<pre>test=# SELECT * FROM items LIMIT 5;
 id |            embedding
----+---------------------------------
  1 | [747.1946,30.812382,226.11743]
  2 | [389.10852,188.70128,151.08716]
  3 | [176.6698,954.8121,233.49654]
  4 | [295.3235,539.09283,721.9699]
  5 | [786.6494,895.81323,160.88304]
(5 rows)</pre>
<h2>最近傍検索の実行</h2>
<p>最近傍検索を実行して、特定のベクトルに最も近いデータのトップ3を検索します。</p>
<pre>test=# SELECT * FROM items ORDER BY embedding &lt;#&gt; '[10.0, 2, 0.3]' LIMIT 3;
  id  |            embedding
------+---------------------------------
 7945 | [998.16565,992.58215,926.16675]
 4633 | [996.8208,985.77405,989.8139]
 3345 | [997.30383,966.3805,949.19257]
(3 rows)</pre>
<p>&lt;#&gt;演算子はベクトル間の負の内積を表す演算子です。似ているベクトルデータほど小さい数値となります。これは&lt;#&gt;演算子が昇順のインデックススキャンのみに対応しているためです。昇順ソートで最近似のベクトルデータをインデックスを効かせて取り出せます。</p>
<h2>提供される演算子と関数</h2>
<p>pgvectorは2つのベクトルの乗算、減算、乗算、ユークリッド距離、内積、コサイン距離等の演算子が用意されています。</p>
<table>
<thead>
<tr>
<th style="text-align: center;">演算子</th>
<th style="text-align: center;">説明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">+</td>
<td style="text-align: center;">2つのベクトルの各要素同士を加算します</td>
</tr>
<tr>
<td style="text-align: center;">&#8211;</td>
<td style="text-align: center;">2つのベクトルの各要素同士を減算します</td>
</tr>
<tr>
<td style="text-align: center;">*</td>
<td style="text-align: center;">2つのベクトルの各要素同士を乗算します</td>
</tr>
<tr>
<td style="text-align: center;">&lt;-&gt;</td>
<td style="text-align: center;">2つのベクトルのユークリッド距離を計測します</td>
</tr>
<tr>
<td style="text-align: center;">&lt;#&gt;</td>
<td style="text-align: center;">2つのベクトルの内積に-1を乗算します</td>
</tr>
<tr>
<td style="text-align: center;">&lt;=&gt;</td>
<td style="text-align: center;">2つのベクトルのコサイン距離を計測します</td>
</tr>
</tbody>
</table>
<p>同様の機能を持った関数もあります。</p>
<table>
<thead>
<tr>
<th style="text-align: center;">関数</th>
<th style="text-align: center;">説明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">cosine_distance(vector, vector) → double precision</td>
<td style="text-align: center;">コサイン距離</td>
</tr>
<tr>
<td style="text-align: center;">inner_product(vector, vector) → double precision</td>
<td style="text-align: center;">内積</td>
</tr>
<tr>
<td style="text-align: center;">l2_distance(vector, vector) → double precision</td>
<td style="text-align: center;">ユークリッド距離</td>
</tr>
<tr>
<td style="text-align: center;">l1_distance(vector, vector) → double precision</td>
<td style="text-align: center;">マンハッタン距離</td>
</tr>
<tr>
<td style="text-align: center;">vector_dims(vector) → integer</td>
<td style="text-align: center;">次元数</td>
</tr>
<tr>
<td style="text-align: center;">vector_norm(vector) → double precision</td>
<td style="text-align: center;">ユークリッドノルム</td>
</tr>
</tbody>
</table>
<p>また、avg(vector) および sum(vector) でベクトルデータの平均と合計を算出する集約関数もあります。</p>
<h2>ベクトルデータのインデックス</h2>
<p>デフォルトのpgvectorは最近傍検索を行うので、リコール率（近傍アイテムを取りこぼさない確率）は100%ですが、線形探索のために速度が問題になる場合があります。<br />
そのため、pgvectorには近似最近傍検索を用いた、IVFFlatとHNSWの2種類のインデックスが追加できます。ただし、正確な最近傍検索でないので高速にデータを取得できますが、実行時間・メモリ・精度のトレードオフが発生します。</p>
<table>
<thead>
<tr>
<th style="text-align: center;">インデックス</th>
<th style="text-align: center;">説明</th>
<th style="text-align: center;">インデックス構築時間</th>
<th style="text-align: center;">メモリ使用量</th>
<th style="text-align: center;">クエリのパフォーマンス</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">IVFFlat</td>
<td style="text-align: center;">ベクトルをリストに分割してクエリベクトルに最も近いリストのサブセットを検索する</td>
<td style="text-align: center;">短い</td>
<td style="text-align: center;">少ない</td>
<td style="text-align: center;">速度とリコールのトレードオフが大きい</td>
</tr>
<tr>
<td style="text-align: center;">HNSW</td>
<td style="text-align: center;">最下層に全てのベクトル点が含まれ上の層になるほどベクトル点が少なくなる層状のグラフ構造で上層から下層にアイテムを辿っていく</td>
<td style="text-align: center;">長い</td>
<td style="text-align: center;">多い</td>
<td style="text-align: center;">速度とリコールのトレードオフが少ない</td>
</tr>
</tbody>
</table>
<p>IVFFlatはHNSWよりもインデックス構築時間は短く、メモリ使用量は少ないですが、ベクトルデータがある程度テーブルに格納されていないとインデックスが構築できません。また、速度とリコールのトレードオフが大きく、速度を重視するとリコールが下がりやすくなり、データの取りこぼしが発生しやすくなります。<br />
一方、HNSWはIVFFlatよりもインデックス構築時間は長く、メモリ使用量は多くなりますが、テーブルにトレーニングデータを格納しておく必要はありません。また、速度とリコールのトレードオフが少なく、速度を重視してもリコール率の低下が見られず、両者のバランスが取れています。</p>
<p>また、2000次元以上のベクトルをインデックス化することはpgvector 0.5.1時点ではできません。ベクトルデータを次元削減して対応する必要があります。</p>
<p>ここではHNSWインデックスを構築します。</p>
<h3>HNSWインデックス</h3>
<p>インデックスの構築は<a href="https://www.postgresql.org/docs/16/sql-createindex.html" target="_blank" rel="noopener">CREATE INDEX文</a>で行います。<br />
利用できる演算子クラスは以下です。</p>
<p>演算子クラス説明vector_cosine_opsコサイン距離vector_ip_ops内積vector_l2_opsユークリッド距離</p>
<p>たとえば、以下でitemsテーブルのembedding列にコサイン距離に基づくHNSWインデックスを構築します。</p>
<pre>test=# CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops);</pre>
<h3>HNSWインデックのスオプション</h3>
<p>CREATE INDEX文にWITH句を加えてHNSWインデクスの<a href="https://www.postgresql.org/docs/16/sql-createindex.html#SQL-CREATEINDEX-STORAGE-PARAMETERS" target="_blank" rel="noopener">インデックスパラメータ</a>を指定できます。</p>
<p>パラメータ説明m層ごとの最大接続数（デフォルトは16）ef_constructionグラフ構築用の動的候補リストのサイズ（デフォルトは64）</p>
<pre>test=# CREATE INDEX ON items USING hnsw (embedding vector_l2_ops) WITH (m = 16, ef_construction = 64);</pre>
<p>m、ef_constructionの値が高いほど、インデックスの構築時間、挿入速度を犠牲にしてリコールが向上します。また、ef_constructionはm*2未満だとエラーとなります。</p>
<h3>HNSWインデックのクエリオプション</h3>
<p><a href="https://www.postgresql.org/docs/16/sql-set.html" target="_blank" rel="noopener">SET文</a>で検索用の動的候補リストのサイズを指定します。デフォルトは40です。</p>
<pre>SET hnsw.ef_search = 100;</pre>
<p>高い値はリコールを向上させますが、速度を犠牲にします。</p>
<p>トランザクション内でSET LOCALを使用して、単一のクエリに設定します。</p>
<pre>test=# BEGIN;
test=# SET LOCAL hnsw.ef_search = 100;
test=# SELECT ...
test=# COMMIT;</pre>
<h3>HNSWインデックのス作成進行状況</h3>
<p>PostgreSQL 12以上であれば<a href="https://www.postgresql.org/docs/16/progress-reporting.html#CREATE-INDEX-PROGRESS-REPORTING" target="_blank" rel="noopener">インデックス作成の進行状況</a>を、たとえば以下のSQL文で、インデックスを作成中のバックエンド毎に確認できます。</p>
<pre>test=# SELECT phase, round(100.0 * blocks_done / nullif(blocks_total, 0), 1) AS "%" FROM pg_stat_progress_create_index;</pre>
<p>HNSWインデックスの場合、pg_stat_progress_create_indexのphase列はinitializing（初期化中）またはloading tuples（タプルの読み込み中）が表示されます。</p>
<h2>終わりに</h2>
<p>pgvectorは、PostgreSQLの拡張機能としてベクトルデータの効率的な管理と類似性検索を可能にします。この記事では、pgvectorのインストールから設定、ベクトルデータの操作方法、最近傍検索の実行、さらにはインデックスの構築と最適化について解説しました。<br />
pgvectorは近年目覚ましい技術発展を遂げている、大規模言語モデルによる文書生成にも利用可能です。大量のテキストデータに含まれるパターンや構造をベクトルデータとして抽出し、それを効率的に検索・分析することで文書の自動生成において重要な役割を果たします。<br />
データベース技術やAI分野に関心がある方は、ぜひpgvectorを使ってみてください</p>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PostgreSQL のマテリアライズドビューを高速に最新化する：＜コミュニティへの機能提案と拡張モジュール pg_ivm ＞</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/postgresql_ivm/</link>
		
		<dc:creator><![CDATA[長田 悠吾 (Yugo Nagata)]]></dc:creator>
		<pubDate>Fri, 23 Dec 2022 00:00:22 +0000</pubDate>
				<category><![CDATA[OSS Development]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[IVM]]></category>
		<category><![CDATA[pg_ivm]]></category>
		<category><![CDATA[PostreSQL]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<category><![CDATA[拡張モジュール]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=10040</guid>

					<description><![CDATA[この記事は株式会社SRAの技術情報誌「GSLetterNeo Vol.173（2022年12月発行）」の記事「PostgreSQL のマテリアライズドビューを高速に最新化する」を許可を得て転載したものです。 はじめに P ...]]></description>
										<content:encoded><![CDATA[<p>この記事は株式会社SRAの技術情報誌「<a href="https://www.sra.co.jp/public/sra/gsletter/">GSLetterNeo</a> Vol.173（2022年12月発行）」の記事「PostgreSQL のマテリアライズドビューを高速に最新化する」を許可を得て転載したものです。</p>
<p><span id="more-10040"></span></p>
<h2>はじめに</h2>
<p>PostgreSQLは広く使用されているオープンソース・ソフトウェアのRDBMSです。SRA OSS LLC ではPostgreSQLに関するサポートやコンサルティングのサービスを提供する一方、技術開発室という部署ではPostgreSQL開発コミュニティへの貢献やPostgreSQLに関する研究開発といった活動を行っています。本稿ではその中から「増分ビューメンテナンス（Incremental View Maintenance, IVM）」と呼ばれる機能の実装について紹介します。これは一言で言うならば、マテリアライズド・ビューの最新化を高速に行う技術です。</p>
<h2>マテリアライズド・ビュー</h2>
<p>マテリアライズド・ビューとは、そのビューを定義するSQLクエリ（SELECT 文）の実行結果をデータベース内に保存しているようなビューです。PostgreSQL では例えば以下のようなコマンドでマテリアライズド・ビューを作成することができます。</p>
<pre>CREATE MATERIALIZED VIEW myview AS
 SELECT * FROM weather JOIN cities ON city = name;</pre>
<p>通常のビューはアクセスされる度に定義クエリの実行を行いますが、マテリアライズド・ビューへのアクセスは保存されている実行結果をクライアントに返すだけなので高速な応答が可能です。その一方で、ビュー定義に使われているテーブルが更新された場合にはマテリアライズド・ビューの内容は古くなり、実際にビュー定義クエリを実行した結果と一致しなくなってしまいます。そのため、テーブルが更新された後にはマテリアライズド・ビューの内容を最新の状態に更新する必要が出てきます。この操作はビューのメンテナンスと呼ばれています。</p>
<h2>増分ビューメンテナンスとは</h2>
<p>ビューをメンテナンスする最も単純な方法は、マテリアライズド・ビューの内容を「再計算」することです。例えば、PostgreSQLでは以下のコマンドを実行するとビュー定義クエリを再実行し、その結果でマテリアライズド・ビューの古い内容を置き換えることができます。</p>
<pre>REFRESH MATERIALIZED VIEW myview;</pre>
<p>しかし、この方法ではマテリアライズド・ビューの全てのデータを再生成することになるため、あまり効率的とは言えません。テーブルの更新がごく一部の場合には、マテリアライズド・ビューの全体ではなく一部分を更新するだけで最新の状態を実現した方が効率がよいはずです。このようなビューのメンテンスの方法は、「増分ビューメンテナンス（Incremental View Maintenance, IVM）」と呼ばれています。</p>
<h2>PostgreSQLにおける増分ビューメンテンス機能の実装</h2>
<p>増分ビューメンテナンス機能は強力な機能ですが、現在のPostgreSQLには実装されていません。そこで、著者らはPostgreSQLにこの機能を実装して開発コミュニティに提案しているところです。</p>
<p>この提案中の機能を使うと、増分メンテナンス可能なマテリアライズド・ビュー（Incrementally Maintainable Materialized View, IMMVと呼んでいます）を作成することができます。このように作られたマテリアライズド・ビューの内容は、テーブルが更新される度に自動的に増分メンテナンスされるため、常に最新のテーブルの内容を反映した状態になっています。実際のビューのメンテナンスにかかる時間はビュー定義やテーブル更新の内容にもよりますが、例えば REFRESH MATERIALIZED VIEW コマンドの実行に20秒程要したビューが、増分メンテナンスを使った場合にはテーブルの更新時間込みで15ミリ秒程度で最新化できます。</p>
<p>PostgreSQLへの増分ビューメンテナンス機能の提案はpgsql-hackers[1]というメーリングリストで議論中です。ここで行われる設計や実装の議論やバグの報告などを通してコードの修正や改善を進めています。現在提案中の機能では、結合と一部の組み込み集約関数（count, sum, avg, min, max）を含むSELECT文がビュー定義の中で使用可能です。コードの修正量が大きくなりすぎないよう現在の提案には含めてはいませんが、外部結合やサブクエリへの対応についても実装済です。また、これらの成果はPostgreSQLコミュニティの海外カンファレンス[2][3][4][5][6]および国内の学会発表[7][8]でも報告しています。</p>
<h2>pg_ivm: PostgreSQLに増分ビューメンテナンス機能を提供する拡張モジュール</h2>
<p>前節ではPostgreSQL本体の新機能として増分ビューメンテナンス機能を提案していることを紹介しましたが、提案の過程で「増分ビューメンテナンス機能を既存のPostgreSQLでも使用したい」という要望を受けることがありました。そこで、この機能をリリース済のPostgreSQLでも使用できるように拡張モジュールとして開発したのがpg_ivm[9]です。現状では、PostgreSQL 13, 14, そして最新の 15 に対応しています。</p>
<p>pg_ivmはさらに、PostgreSQLにおける増分ビューメンテナンス機能に対するフィードバックや認知を得る機会の向上、および、PostgreSQL本体への提案には含まれていない追加的な機能の提供も目的としています。実際に、最新のpg_ivm 1.3では本体に提案している機能では対応していないサブクエリに部分的に対応しています。</p>
<h2>pg_ivmの使用例</h2>
<p>pg_ivmはGitHubレポジトリ[9]からソースコードを入手してインストールすることもできますし、PostgreSQLコミュニティで提供されているレポジトリ[10]で提供されているrpmファイルを利用してyumやdnfコマンドでインストールすることもできます。</p>
<p>PostgreSQLでpg_ivmを有効にするにはまず CREATE EXTENSION コマンドを実行します。</p>
<pre>CREATE EXTENSION pg_ivm;</pre>
<p>pg_ivmで増分メンテナンス可能なマテリアライズド・ビューを作成するには、create_immvという関数を実行します。これは CREATE MATERIALIZED VIEW コマンドに相当する関数で、第1引数にビューの名前と列のリスト、第2引数にビューの定義を指定します。実行結果として生成されたビューの行数（以下の例では10,000,000行）が返されます。</p>
<pre>test=# SELECT create_immv('mv(aid, bid, abalance, bbalance)',
                          'SELECT a.aid, b.bid, a.abalance, b.bbalance
                           FROM pgbench_accounts a JOIN pgbench_branches b USING(bid)');
NOTICE: created index "mv_index" on immv "mv"
 create_mv
-------------
   10000000
(1 row)</pre>
<p>このビューの定義で使われているテーブルの1行を更新してみます。著者の環境では、その所要時間は 15.448 ミリ秒でした。その後にビューの内容を確認してみると、更新されたテーブルの内容が反映されていることがわかります。これと同じ定義の通常のマテリアライズド・ビューを作成して REFRESH MATERIALIZED VIEW コマンドを実行した場合には20秒以上かかりました。これと比較すると、今回の例では増分メンテナンスが効果的に働いていることがわかると思います。</p>
<pre>test=# UPDATE pgbench_accounts SET abalance = 1234 WHERE aid = 1;
UPDATE 1
Time: 15.448 mstoeic "ubuntu" ip windows
test=# SELECT * FROM mv WHERE aid = 1;
 aid | bid | abalance | bbalance
-----+-----+----------+----------
   1 |    1 |    1234 |        0
(1 row)</pre>
<h2>pg_ivm の性能</h2>
<p>増分メンテナンスの性能を示す別の例として、業界標準のベンチマークである TPC-H のクエリを用いた性能評価を紹介します。TPC-H のクエリの１つ Q01 は１つの巨大なテーブルに対する集約です。</p>
<pre>select
    l_returnflag,
    l_linestatus,
    sum(l_quantity) as sum_qty,
    sum(l_extendedprice) as sum_base_price,
    sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
    sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as
    sum_charge,
    avg(l_quantity) as avg_qty,
    avg(l_extendedprice) as avg_price,
    avg(l_discount) as avg_disc,
    count(*) as count_order
from
    lineitem
where
    l_shipdate &lt;= date '1998-12-01' - interval '78' day
group by
    l_returnflag,
    l_linestatus;</pre>
<p>このクエリで定義される通常のマテリアライズド・ビューを REFRESH MATERIALIZED VIEW コマンドで最新化した場合、筆者の環境では10秒以上かかりました。なお、TPC-Hのスケールファクタ は 1（lineitem テーブルの行数は600万行程度）を使用しました。一方、pg_ivmを使って増分メンテナンス可能なマテリアライズドビューを作って、lineitem テーブルを１行更新した場合の所要時間は20ミリ秒程度でした。</p>
<p>別のクエリ Q09 は6つのテーブルを結合して集約したクエリです。</p>
<pre>select
    nation,
    o_year,
    sum(amount) as sum_profit
from
    (
    select
        n_name as nation,
        extract(year from o_orderdate) as o_year,
        l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount
    from
        part, supplier, lineitem, partsupp, orders, nation
    where
        s_suppkey = l_suppkey
        and ps_suppkey = l_suppkey
        and ps_partkey = l_partkey
        and p_partkey = l_partkey
        and o_orderkey = l_orderkey
        and s_nationkey = n_nationkey
        and p_name like ''%sandy%''
    ) as profit
group by
    nation,
    o_year;</pre>
<p>このクエリの場合、通常のマテリアライズド・ビューの REFRESH MATERIALIZED VIEW コマンド実行は3秒程度かかりましたが、pg_ivmを使用した場合の lineitem テーブルの１行更新の所要時間は45ミリ秒程度でした。</p>
<p>以上のように、増分メンテナンスに要する時間はビュー定義など状況によって異なりますが、増分メンテナンスが REFRESH MATERIALIZED VIEW によるクエリの再実行より効果的であることがわかります。</p>
<p>一方で、現在の機能ではテーブルの更新の度にビューのメンテナンスが発生するため、テーブル更新の性能にはオーバヘッドがあります。実際に上の例では、増分ビューメンテナンス機能を使用しない場合は lineitem の１行更新は10ミリ秒ほどで完了していますが、増分ビューメンテナンスを行う場合にはQ01の場合で20ミリ秒、Q09の場合で45ミリ秒とかかっています。その他にも、データ不整合の発生を防止するため、複数トランザクションが同時にビューをメンテナンスしようとする場合には排他処理を行うなど、同時実行性能にも影響があります。これらの性質から、PostgreSQLに現在提案中の増分ビューメンテナンス機能、および pg_ivmは「テーブル更新頻度は低いが、更新があった際にはすぐに最新のクエリ結果が欲しい」という状況に有用なものと言えます。</p>
<p>pg_ivmについては先月開催された PostgreSQL Conference Japan 2022 でも紹介しました[11]。講演資料も公開されていますのでぜひ参照してください。</p>
<h2>今後の予定</h2>
<p>PostgreSQL本体への増分ビューメンテナンス実装は開発コミュニティに提案中の機能です。PostgreSQLへの機能追加はコミュニティでの議論を経てコンセンサスを得る必要があるため時間がかかるのですが、これからも引き続き本体への採用を目指して議論と改善を重ねていきます。</p>
<p>pg_ivmはこれまでに何回かのリリースで、ビュー定義として対応できるクエリの種類の拡充やバグ修正を行ってきました。今後も引き続きEXISTSサブクエリ、CTE（WITH句）、外部結合といったクエリに対応していくと共に、バグ修正などの改善を続けていきます。また、さらに進んだ機能も追加していきたいと考えており、その中の１つが「遅延メンテナンス」です。現在の提供しているのはビューのメンテナンスをテーブルの更新と同時に行う「即時メンテナンス」のみであるため、テーブルの更新性能への影響が目立ちます。これに対して遅延メンテナンスはテーブル更新とは別のタイミング（コマンドの実行や、バックグラウンドによる定期処理など）で増分ビューメンテナンスを行う方法で、テーブル更新性能への影響を抑えられることが期待されます。その他にも性能全般の改善やパーティション・テーブルなどの特殊なテーブルへの対応なども考えています。</p>
<h2>おわりに</h2>
<p>本稿ではPostgreSQLにおける増分ビューメンテナンス機能の提案と、拡張モジュールpg_ivmについて紹介しました。pg_ivmはGitHubレポジトリ[9]、PostgreSQL Yum レポジトリ[10]から入手可能な他、最近販売開始されましたPowerGres Plus V13[12]に同梱されています。Linux版の提供はもちろん、Windows版にも Windows で使用可能なビルド済バイナリが含まれています。評価版もありますので、是非試してみてください。</p>
<p>また、本プロジェクトはオープンソースプロジェクトです。開発コミュニティでの議論はもちろん、GitHub への Issue, Pull Request の投稿なども大歓迎ですので、何かしらのフィードバックを寄せて頂けましたら幸いです。</p>
<h2>参考文献・リンク等</h2>
<p>[1] pgsql-hackers: <a href="https://www.postgresql.org/list/pgsql-hackers/">https://www.postgresql.org/list/pgsql-hackers/</a></p>
<p>[2] Yugo Nagata, &#8220;Toward Implementing Incremental View Maintenance on PostgreSQL&#8221;, PGCon 2019.</p>
<p>[3] Yugo Nagata, &#8220;Toward Implementing Incremental View Maintenance on PostgreSQL&#8221;, PGConf.Asia 2019.</p>
<p>[4] Yugo Nagata, Takuma Hoshiai, &#8220;The Way for Updating Materialized Views Rapidly&#8221;, PGCon 2020.</p>
<p>[5] Yugo Nagata, &#8220;The Way for Updating Materialized Views Rapidly&#8221;, PostgresConf.CN &amp; PGConf.Asia 2020.</p>
<p>[6] Yugo Nagata, &#8220;Updating Materialized Views Automatically and Incrementally&#8221;, PGConf.Online 2021/PGConf.Russia 2021.</p>
<p>[7] 長田悠吾, 星合拓馬, 石井達夫, 増永良文, &#8220;タプル重複のもとで外部結合および準結合を含むビューの増分メンテナンスとそのPostgreSQL への実装,&#8221; 第 12 回データ工学と情報マネジメントに関するフォーラム (DEIM Forum), 2020.</p>
<p>[8] 長田悠吾, 星合拓馬, 石井達夫, 三島健, 増永良文, &#8220;PostgreSQL におけるビューの増分メンテナンス機能の実装とその評価,&#8221; 第 13 回データ工学と情報マネジメントに関するフォーラム (DEIM Forum), 2021.</p>
<p>[9] pg_ivm: <a href="https://github.com/sraoss/pg_ivm">https://github.com/sraoss/pg_ivm</a></p>
<p>[10] PostgreSQL Yum Repository: <a href="https://yum.postgresql.org/">https://yum.postgresql.org/</a></p>
<p>[11] 長田悠吾, &#8220;pg_ivm: マテリアライズドビューを高速に更新するための PostgreSQL 拡張モジュール&#8221;, PostgreSQL Conference Japan 2022. (<a href="https://www.postgresql.jp/jpug-pgcon2022#A3">https://www.postgresql.jp/jpug-pgcon2022#A3</a>)</p>
<p>[12] PowerGres Plus V13: <a href="https://powergres.sraoss.co.jp/product/plusv13/">https://powergres.sraoss.co.jp/product/plusv13/</a></p>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>pgAudit Log to File (pgAudit アドオン監査ログ専用ファイル分離ツール)</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/pgauditlogtofile/</link>
		
		<dc:creator><![CDATA[北山 貴広 (Takahiro Kitayama)]]></dc:creator>
		<pubDate>Thu, 10 Nov 2022 01:00:42 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[pgAudit]]></category>
		<category><![CDATA[PostgreSQL 14]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=11483</guid>

					<description><![CDATA[本記事では、PostgreSQL 監査ロギングツール「pgAudit」のアドオンとして使用する「pgAudit Log to File」のインストール方法、監査ログ取得方法について紹介します。 pgAudit Log t ...]]></description>
										<content:encoded><![CDATA[<p>本記事では、PostgreSQL 監査ロギングツール「pgAudit」のアドオンとして使用する「pgAudit Log to File」のインストール方法、監査ログ取得方法について紹介します。</p>
<p><span id="more-11483"></span></p>
<h2>pgAudit Log to File とは</h2>
<p>pgAudit Log to File とは PostgreSQL で監査ログを取得して PostgreSQL ログファイルに書き出す pgAudit のアドオンで、監査ログ専用ファイルを PostgreSQL ログファイルとは別の独立したファイルに分離して書き出してくれるソフトウェアです。</p>
<p>公式サイトは<a href="https://github.com/fmbiete/pgauditlogtofile">こちら</a>です。分かりやすいドキュメント(README.md)とともに公開されています。</p>
<p>pgAudit のブログ記事は<a href="https://www.sraoss.co.jp/tech-blog/pgsql/pgaudit/">こちら</a>になります。</p>
<h2>pgAudit Log to File サポート対象バージョン</h2>
<p>pgAudit Log to File がサポートする PostgreSQL / pgAudit のバージョンは特にドキュメントに記載はありませんので、使用する時点の最新バージョンを使用すればよいと思います。</p>
<p>現時点(2022年11月2日)の GitHub での最新バージョンは v1.5.8 で、<a href="https://github.com/fmbiete/pgauditlogtofile/releases">GitHubのリリースページ</a>からダウンロード可能です。<br />
また、PostgreSQL 公式リポジトリでは pgAudit Log to File が pgauditlogtofile_&lt;PostgreSQLメジャーバージョン&gt; というファイル名で rpm ファイルが配布されており、PostgreSQL 10,11,12,13,14,15 用バージョンが v1.5.6 となっています。</p>
<h2>pgAudit Log to File インストール方法(rpmパッケージ)</h2>
<p>pgAudit Log to File のインストール方法は、大きく分けてソースコードからのインストールと、PostgreSQL公式リポジトリの rpm からのインストールがあります。<br />
今回は rpm からのインストールをご紹介します。<br />
下記のインストールの実行例では Rocky Linux release 8.6, PostgreSQL 14.5, pgAudit v1.6.2, pgAudit Log to File v1.5.6 を使用して説明します。<br />
Rocky Linux から直接インターネット接続が可能な環境を使用しています。</p>
<h3>Rocky Linux に PostgreSQL 公式 Yum リポジトリを設定し、PostgreSQL 14をインストール</h3>
<p>Rocky Linux から PostgreSQL 公式 Yum リポジトリのパッケージを dnf でインストール可能とするために repo ファイルを導入します。<br />
<a href="https://www.postgresql.org/download/">PostgreSQL ダウンロードページ</a>の Packages and Installers で Linux と Red Hat/Rocky/CentOS を選択すると<a href="https://www.postgresql.org/download/linux/redhat/">このページ</a>が表示されます。PostgreSQL Yum Repository で使用する PostgreSQL バージョン(14)と OS のバージョン(Red Hat Enterprise, Rocky, or Oracle version 8)と CPU アーキテクチャ(x86_64)を選択すれば、repo ファイルを導入して PostgreSQL サーバを導入後有効にして起動するコマンドが表示されます。<br />
表示されたコマンドを参考にして、まず repo ファイルを導入します。</p>
<pre># dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
：
依存関係が解決しました。
================================================================================
パッケージ Arch バージョン リポジトリー サイズ
================================================================================
インストール:
pgdg-redhat-repo noarch 42.0-28 @commandline 13 k

トランザクションの概要
================================================================================
インストール 1 パッケージ

合計サイズ: 13 k
インストール後のサイズ: 14 k
パッケージのダウンロード:
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
準備 : 1/1
インストール中 : pgdg-redhat-repo-42.0-28.noarch 1/1
検証 : pgdg-redhat-repo-42.0-28.noarch 1/1

インストール済み:
pgdg-redhat-repo-42.0-28.noarch

完了しました!
#
</pre>
<p>次に、PostgreSQL公式サイトの PostgreSQL が導入できるように OS 組み込みの PostgreSQL を無効にします。これを実行しないと PostgreSQL 公式サイトの PostgreSQL が見つからないと言われてしまいます。</p>
<pre># dnf install -y postgresql14-server
すべての検出結果は引数のモジュラーフィルタリングに一致しません (filter out): postgresql14-server
エラー: 一致するものが見つかりません: postgresql14-server
#
# dnf -qy module disable postgresql
# dnf install -y postgresql14-server
依存関係が解決しました。
================================================================================
パッケージ Arch バージョン Repo サイズ
================================================================================
インストール:
postgresql14-server x86_64 14.5-1PGDG.rhel8 pgdg14 5.7 M
依存関係のインストール:
postgresql14 x86_64 14.5-1PGDG.rhel8 pgdg14 1.5 M
postgresql14-libs x86_64 14.5-1PGDG.rhel8 pgdg14 278 k

トランザクションの概要
================================================================================
インストール 3 パッケージ

ダウンロードサイズの合計: 7.5 M
インストール後のサイズ: 31 M
パッケージのダウンロード:
(1/3): postgresql14-libs-14.5-1PGDG.rhel8.x86_6 113 kB/s | 278 kB 00:02
(2/3): postgresql14-14.5-1PGDG.rhel8.x86_64.rpm 307 kB/s | 1.5 MB 00:05
(3/3): postgresql14-server-14.5-1PGDG.rhel8.x86 196 kB/s | 5.7 MB 00:29
--------------------------------------------------------------------------------
合計 258 kB/s | 7.5 MB 00:29
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
準備 : 1/1
インストール中 : postgresql14-libs-14.5-1PGDG.rhel8.x86_64 1/3
scriptletの実行中: postgresql14-libs-14.5-1PGDG.rhel8.x86_64 1/3
インストール中 : postgresql14-14.5-1PGDG.rhel8.x86_64 2/3
scriptletの実行中: postgresql14-14.5-1PGDG.rhel8.x86_64 2/3
scriptletの実行中: postgresql14-server-14.5-1PGDG.rhel8.x86_64 3/3
インストール中 : postgresql14-server-14.5-1PGDG.rhel8.x86_64 3/3
scriptletの実行中: postgresql14-server-14.5-1PGDG.rhel8.x86_64 3/3
検証 : postgresql14-14.5-1PGDG.rhel8.x86_64 1/3
検証 : postgresql14-libs-14.5-1PGDG.rhel8.x86_64 2/3
検証 : postgresql14-server-14.5-1PGDG.rhel8.x86_64 3/3

インストール済み:
postgresql14-14.5-1PGDG.rhel8.x86_64
postgresql14-libs-14.5-1PGDG.rhel8.x86_64
postgresql14-server-14.5-1PGDG.rhel8.x86_64

完了しました!
# dnf install -y postgresql14-contrib
依存関係が解決しました。
================================================================================
パッケージ Arch バージョン Repo サイズ
================================================================================
インストール:
postgresql14-contrib x86_64 14.5-1PGDG.rhel8 pgdg14 726 k

トランザクションの概要
================================================================================
インストール 1 パッケージ

ダウンロードサイズの合計: 726 k
インストール後のサイズ: 2.5 M
パッケージのダウンロード:
postgresql14-contrib-14.5-1PGDG.rhel8.x86_64.rp 203 kB/s | 726 kB 00:03
--------------------------------------------------------------------------------
合計 203 kB/s | 726 kB 00:03
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
準備 : 1/1
インストール中 : postgresql14-contrib-14.5-1PGDG.rhel8.x86_64 1/1
scriptletの実行中: postgresql14-contrib-14.5-1PGDG.rhel8.x86_64 1/1
検証 : postgresql14-contrib-14.5-1PGDG.rhel8.x86_64 1/1

インストール済み:
postgresql14-contrib-14.5-1PGDG.rhel8.x86_64

完了しました!
#
</pre>
<p>これで PostgreSQL 公式サイトの rpm から PostgreSQL 14.5 が導入出来ました。DBの初期化も実施しておきましょう。</p>
<pre># export PGSETUP_INITDB_OPTIONS="--encoding=UTF-8 --no-locale"
# /usr/pgsql-14/bin/postgresql-14-setup initdb
Initializing database ... OK

# systemctl enable postgresql-14
Created symlink /etc/systemd/system/multi-user.target.wants/postgresql-14.service → /usr/lib/systemd/system/postgresql-14.service.
# systemctl start postgresql-14
# systemctl status postgresql-14
● postgresql-14.service - PostgreSQL 14 database server
Loaded: loaded (/usr/lib/systemd/system/postgresql-14.service; enabled; vend&gt;
Active: active (running) since Wed 2022-11-02 05:19:19 JST; 4s ago
Docs: https://www.postgresql.org/docs/14/static/
Process: 8619 ExecStartPre=/usr/pgsql-14/bin/postgresql-14-check-db-dir ${PGD&gt;
Main PID: 8624 (postmaster)
Tasks: 8 (limit: 4783)
Memory: 19.9M
CGroup: /system.slice/postgresql-14.service
tq8624 /usr/pgsql-14/bin/postmaster -D /var/lib/pgsql/14/data/
tq8626 postgres: logger
tq8628 postgres: checkpointer
tq8629 postgres: background writer
tq8630 postgres: walwriter
tq8631 postgres: autovacuum launcher
tq8632 postgres: stats collector
mq8633 postgres: logical replication launcher

11月 02 05:19:19 rockey86 systemd[1]: Starting PostgreSQL 14 database server...
11月 02 05:19:19 rockey86 postmaster[8624]: 2022-11-02 05:19:19.463 JST [8624] &gt;
11月 02 05:19:19 rockey86 postmaster[8624]: 2022-11-02 05:19:19.463 JST [8624] &gt;
11月 02 05:19:19 rockey86 systemd[1]: Started PostgreSQL 14 database server.
#
# su - postgres
$ psql
psql (14.5)
"help"でヘルプを表示します。

postgres=#
</pre>
<p>無事 PostgreSQL 14 が起動しました。</p>
<h3>pgaudit と pgauditlogtofile のインストール</h3>
<p>PostgreSQL 公式リポジトリに含まれる pgaudit と pgauditlogtofile の rpm パッケージ名を調べてみます。PostgreSQL 14 用の pgaudit のパッケージは pgaudit16_14、pgauditlogtofile のパッケージは pgauditlogtofile_14 もしくは pgauditlogtofile_14-llvmjit (JITコンパイル対応)となっています。</p>
<pre># dnf list | grep pgaudit
pgaudit16_14.x86_64 1.6.2-1.rhel8 @pgdg14
pgauditlogtofile_14.x86_64 1.5.6-1.rhel8 @pgdg14
pgaudit12_10.x86_64 1.2.4-1.rhel8 pgdg10
pgaudit13_11.x86_64 1.3.4-1.rhel8 pgdg11
pgaudit14_12.x86_64 1.4.3-1.rhel8 pgdg12
pgaudit15_13.x86_64 1.5.2-1.rhel8 pgdg13
pgaudit17_15.x86_64 1.7.0-1.rhel8 pgdg15
pgaudit_analyze.x86_64 1.0.7-1.rhel8 pgdg-common
pgaudit_analyze.noarch 1.0.8-1.rhel8 pgdg-common
pgauditlogtofile-10.x86_64 1.0-1.rhel8 pgdg10
pgauditlogtofile-11.x86_64 1.0-1.rhel8 pgdg11
pgauditlogtofile-12.x86_64 1.0-1.rhel8 pgdg12
pgauditlogtofile-13.x86_64 1.0-1.rhel8 pgdg13
pgauditlogtofile_10.x86_64 1.5.6-1.rhel8 pgdg10
pgauditlogtofile_11.x86_64 1.5.6-1.rhel8 pgdg11
pgauditlogtofile_11-llvmjit.x86_64 1.5.6-1.rhel8 pgdg11
pgauditlogtofile_12.x86_64 1.5.6-1.rhel8 pgdg12
pgauditlogtofile_12-llvmjit.x86_64 1.5.6-1.rhel8 pgdg12
pgauditlogtofile_13.x86_64 1.5.6-1.rhel8 pgdg13
pgauditlogtofile_13-llvmjit.x86_64 1.5.6-1.rhel8 pgdg13
pgauditlogtofile_14-llvmjit.x86_64 1.5.6-1.rhel8 pgdg14
pgauditlogtofile_15.x86_64 1.5.6-2.rhel8 pgdg15
pgauditlogtofile_15-llvmjit.x86_64 1.5.6-2.rhel8 pgdg15
#
</pre>
<p>今回は pgaudit と pgauditlogtofile のパッケージを導入します。</p>
<pre># dnf install -y pgaudit16_14 pgauditlogtofile_14
依存関係が解決しました。
================================================================================
パッケージ Arch バージョン リポジトリー サイズ
================================================================================
インストール:
pgaudit16_14 x86_64 1.6.2-1.rhel8 pgdg14 56 k
pgauditlogtofile_14 x86_64 1.5.6-1.rhel8 pgdg14 18 k

トランザクションの概要
================================================================================
インストール 2 パッケージ

ダウンロードサイズの合計: 74 k
インストール後のサイズ: 124 k
パッケージのダウンロード:
(1/2): pgauditlogtofile_14-1.5.6-1.rhel8.x86_64 13 kB/s | 18 kB 00:01
(2/2): pgaudit16_14-1.6.2-1.rhel8.x86_64.rpm 37 kB/s | 56 kB 00:01
--------------------------------------------------------------------------------
合計 49 kB/s | 74 kB 00:01
トランザクションの確認を実行中
トランザクションの確認に成功しました。
トランザクションのテストを実行中
トランザクションのテストに成功しました。
トランザクションを実行中
準備 : 1/1
インストール中 : pgaudit16_14-1.6.2-1.rhel8.x86_64 1/2
インストール中 : pgauditlogtofile_14-1.5.6-1.rhel8.x86_64 2/2
scriptletの実行中: pgauditlogtofile_14-1.5.6-1.rhel8.x86_64 2/2
検証 : pgaudit16_14-1.6.2-1.rhel8.x86_64 1/2
検証 : pgauditlogtofile_14-1.5.6-1.rhel8.x86_64 2/2

インストール済み:
pgaudit16_14-1.6.2-1.rhel8.x86_64 pgauditlogtofile_14-1.5.6-1.rhel8.x86_64

完了しました!
#</pre>
<p>これで pgaudit と pgauditlogtofile がインストールされました。</p>
<h3>postgresql.conf の shared_preload_libraries の設定と有効化確認</h3>
<p>shared_preload_libraries に共有ライブラリの pgaudit,pgauditlogtofile を設定して PostgreSQL を再起動し、新規に db1 データベースを作成後、pgaudit が有効になっていることを確認します。</p>
<pre>$ cd $PGDATA
$ pwd
/var/lib/pgsql/14/data
$ vi postgresql.conf
shared_preload_libraries = 'pgaudit, pgauditlogtofile'
# systemctl restart postgresql-14.service
$ createdb db1
$ psql db1
psql (14.5)
"help"でヘルプを表示します。

db1=# show shared_preload_libraries;
shared_preload_libraries
---------------------------
pgaudit, pgauditlogtofile
(1 行)

</pre>
<h3>CREATE EXTENSION を実行してエクステンションに登録</h3>
<p>使用したいデータベースに psql で接続して、pgaudit と pgauditlogtofile で CREATE EXTENSION を実行します。エクステンションが登録されていることを確認します。</p>
<pre>db1=# CREATE EXTENSION pgaudit;
CREATE EXTENSION
db1=# CREATE EXTENSION pgauditlogtofile;
CREATE EXTENSION
db1=# \dx
インストール済みの拡張一覧
名前 | バージョン | スキーマ | 説明

------------------+------------+------------+------------------------------------------------------------
pgaudit | 1.6.2 | public | provides auditing functionality
pgauditlogtofile | 1.5 | public | pgAudit addon to redirect audit log to an independent file
plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language
(3 行)

db1=#
</pre>
<p>これで pgAudit と pgAudit Log to File のインストールは完了しました。</p>
<h2>セッション監査ロギングを有効にして分離した監査ログファイルを確認</h2>
<p>監査ログをセッション単位で取得するセッション監査ロギングを試して監査ログファイルが分離されることを確認します。postgresql.conf ファイルに pgaudit.log = &#8216;ALL&#8217; を追加し、PostgreSQL に設定を読み込ませます。</p>
<pre>$ cd $PGDATA
$ vi postgresql.conf
pgaudit.log = 'ALL'
# systemctl reload postgresql-14.service
$ cd $PGDATA/log
$ ls
postgresql-Wed.log
$ tail -3 postgresql-Wed.log
2022-11-02 05:30:01.537 JST [9842] LOG: database system is ready to accept connections
2022-11-02 05:37:08.831 JST [9842] LOG: received SIGHUP, reloading configuration files
2022-11-02 05:37:08.831 JST [9842] LOG: parameter "pgaudit.log" changed to "ALL"
</pre>
<p>table1 を作成してSELECT 文を実行したところ、無事 PostgreSQL ログとは別に新規に作成された監査ログファイル(audit-YYYYMMDD_0000.log)に記録されました。</p>
<pre>$ psql db1
db1=# CREATE TABLE table1 (i1 integer, t1 text);
CREATE TABLE
db1=# INSERT INTO table1 VALUES (1, 'aaa'),(2, 'bbb');
INSERT 0 2
db1=# SELECT * FROM table1;
i1 | t1
----+-----
1 | aaa
2 | bbb
(2 行)
db1=# \q
$ cd $PGDATA/log
$ ls -tl
合計 8
-rw-------. 1 postgres postgres 651 11月 2 05:39 audit-20221102_0000.log
-rw-------. 1 postgres postgres 1972 11月 2 05:37 postgresql-Wed.log
$ cat audit-20221102_0000.log
2022-11-02 05:39:32.716 JST,postgres,db1,10095,[local],636183eb.276f,1,CREATE TABLE,2022-11-02 05:39:07 JST,3/24,737,00000,SESSION,1,1,DDL,CREATE TABLE,TABLE,public.table1,"CREATE TABLE table1 (i1 integer, t1 text);",&lt;not logged&gt;,,,,,,,,,psql
2022-11-02 05:39:43.309 JST,postgres,db1,10095,[local],636183eb.276f,2,INSERT,2022-11-02 05:39:07 JST,3/25,0,00000,SESSION,2,1,WRITE,INSERT,,,"INSERT INTO table1 VALUES (1, 'aaa'),(2, 'bbb');",&lt;not logged&gt;,,,,,,,,,psql
2022-11-02 05:39:49.228 JST,postgres,db1,10095,[local],636183eb.276f,3,SELECT,2022-11-02 05:39:07 JST,3/26,0,00000,SESSION,3,1,READ,SELECT,,,SELECT * FROM table1;,&lt;not logged&gt;,,,,,,,,,psql
$
</pre>
<h3>pgaudit.log_connections, pgaudit.log_disconnections について</h3>
<p>postgresql.conf ファイルで log_connections = on と log_disconnections = on を設定した上で pg_audit.log_connections = on と pg_audit.log_disconnections = on と設定して PostgreSQL に設定を再読み込みさせると、監査ログファイルにログイン情報とログアウト情報が記録されます。</p>
<pre>$ cd $PGDATA
$ vi postgresql.conf
log_connections = on
log_disconnections = on
pg_audit.log_connections = on
pg_audit.log_disconnections = on
# systemctl reload postgresql-14.service
$ cd $PGDATA/log
$ tail -3 postgresql-Wed.log
2022-11-02 06:12:30.368 JST [9842] LOG: received SIGHUP, reloading configuration files
2022-11-02 06:12:30.368 JST [9842] LOG: parameter "log_connections" changed to "on"
2022-11-02 06:12:30.368 JST [9842] LOG: parameter "log_disconnections" changed to "on"
2022-11-02 06:12:30.368 JST [9842] LOG: parameter "pg_audit.log_connections" changed to "on"
2022-11-02 06:12:30.368 JST [9842] LOG: parameter "pg_audit.log_disconnections" changed to "on"
</pre>
<p>現在のバージョンでは、ログインが成功すると、監査ログファイルにのみ情報が記録されますが、ログインが失敗した場合は、ログインに失敗したこと自体は監査ログに記録されずに PostgreSQL ログのみに記録される点には注意が必要です。</p>
<pre>[ログイン/ログアウト成功時]
$ psql db1
db1=# \q
$ tail -f audit-20221102_0000.log
2022-11-02 06:13:15.781 JST,,,11664,[local],63618beb.2d90,1,,2022-11-02 06:13:15 JST,,0,00000,connection received: host=[local],,,,,,,,,
2022-11-02 06:13:15.782 JST,postgres,db1,11664,[local],63618beb.2d90,2,authentication,2022-11-02 06:13:15 JST,3/140,0,00000,connection authenticated: identity="postgres" method=peer (/var/lib/pgsql/14/data/pg_hba.conf:85),,,,,,,,,
2022-11-02 06:13:15.782 JST,postgres,db1,11664,[local],63618beb.2d90,3,authentication,2022-11-02 06:13:15 JST,3/140,0,00000,connection authorized: user=postgres database=db1 application_name=psql,,,,,,,,,
2022-11-02 06:13:22.678 JST,postgres,db1,11664,[local],63618beb.2d90,4,idle,2022-11-02 06:13:15 JST,,0,00000,disconnection: session time: 0:00:06.896 user=postgres database=db1 host=[local],,,,,,,,,psql
[ログイン失敗時]
$ psql db1 -U nouser
psql: エラー: ソケット"/var/run/postgresql/.s.PGSQL.5432"上でのサーバーへの接続に失敗しました: FATAL: Peer authentication failed for user "nouser"
$ tail -f audit-20221102_0000.log
2022-11-02 06:14:29.051 JST,,,11676,[local],63618c35.2d9c,1,,2022-11-02 06:14:29 JST,,0,00000,connection received: host=[local],,,,,,,,,
2022-11-02 06:14:29.052 JST,nouser,db1,11676,[local],63618c35.2d9c,2,authentication,2022-11-02 06:14:29 JST,3/145,0,00000,connection authenticated: identity="postgres" method=peer (/var/lib/pgsql/14/data/pg_hba.conf:85),,,,,,,,,
$ tail -f postgresql-Wed.log
2022-11-02 06:14:29.052 JST [11676] LOG: provided user name (nouser) and authenticated user name (postgres) do not match
2022-11-02 06:14:29.052 JST [11676] FATAL: Peer authentication failed for user "nouser"
2022-11-02 06:14:29.052 JST [11676] DETAIL: Connection matched pg_hba.conf line 85: "local all all peer"
$
</pre>
<h2>終わりに</h2>
<p>pgAudit 単体では監査の記録が PostgreSQL ログと混在していましたが、pgAudit Log To File を追加で導入することで、監査の記録を PostgreSQL ログとは別の独立した監査ログファイルに記録することが出来るため、PostgreSQL ログに混在した監査ログの分離が不要となり PostgreSQL ログの扱いがこれまでと同様に出来るため非常に便利です。<br />
pgAudit を新規に導入する際は pgAudit Log to File もセットで導入、pgAudit が既に導入されている環境には pgAudit Log to File を追加で導入することをお勧めします。</p>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>redis_fdw による PostgreSQL と Redis の連携</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/redis_fdw/</link>
		
		<dc:creator><![CDATA[佐藤 友章 (Tomoaki Sato)]]></dc:creator>
		<pubDate>Wed, 03 Aug 2022 05:06:26 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[redis_fdw]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=10712</guid>

					<description><![CDATA[redis_fdw は、リレーショナルデータベース PostgreSQL からキーバリューストア Redis にアクセスする拡張モジュールです。redis_fdw を使うことで、高速なデータの読み書きができる Redis ...]]></description>
										<content:encoded><![CDATA[<p>redis_fdw は、リレーショナルデータベース PostgreSQL からキーバリューストア Redis にアクセスする拡張モジュールです。redis_fdw を使うことで、高速なデータの読み書きができる Redis と、SQL で複雑なデータの操作ができる PostgreSQL 間で円滑なデータの連携ができます。</p>
<p>この記事では、redis_fdw の概要、インストール、基本的な使い方、テーブルオプションについて説明します。なお、Redis 自体の説明については <a href="https://www.sraoss.co.jp/tech-blog/redis/redis-introduction/">Redis の紹介</a> をお読みください。</p>
<p><span id="more-10712"></span></p>
<h2 id="about">redis_fdw とは</h2>
<p>redis_fdw の名前につく fdw は外部データラッパ (Foreign Data Wrapper) を表し、PostgreSQL から Oracle や CSV ファイルなど、PostgreSQL 外のデータに外部テーブルとしてアクセスする機能です。redis_fdw はこの機能を使って Redis 上のデータにアクセスします。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/07/redis_fdw-system.png" /></p>
<p><a href="https://github.com/pg-redis-fdw/redis_fdw">redis_fdw</a> は、もともと PostgreSQL 開発者の一人 Dave Page 氏が実験的に開発し、その後、Andrew Dunstan 氏が開発を引き継ぎ、本番利用向けに改修しました。</p>
<p>ソースコードは GitHub リポジトリ上で公開され、PostgreSQL ライセンスに従って配布されています。PostgreSQL の対応バージョンは 9.1 以降で、2022 年 8 月時点で最新の PostgreSQL 14 にも対応しています。</p>
<p>なお、PGDG リポジトリ<sup><a href="#footnote-1">[1]</a></sup> で同じ名前のパッケージが配布されていますが、ここで取り上げる redis_fdw とは異なるもので、便宜上、リポジトリ名をとって rw_redis_fdw と呼びます。</p>
<p><a href="https://github.com/nahanni/rw_redis_fdw">rw_redis_fdw</a> も Redis 用外部データラッパで、名前につく rw は読み書き (Read-Write) を表し、以前は書き込みに対応していなかった redis_fdw に対して名づけられたものと考えられますが、現在は redis_fdw も書き込みに対応しています。</p>
<p>rw_redis_fdw は redis_fdw に比べて機能は多いですが、ここでは、より広く使われている redis_fdw を取り上げます。</p>
<h2 id="install">redis_fdw のインストール</h2>
<p>redis_fdw をインストールします。</p>
<p>ここでは、Rocky Linux 8 上に PostgreSQL、Redis をセットアップした上、redis_fdw をソースコードからビルドし、インストールします。</p>
<ol>
<li>PGDG リポジトリをインストールします。
<pre>$ <b>sudo dnf -qy install https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %rhel-%_arch)/pgdg-redhat-repo-latest.noarch.rpm</b>

Installed:
  pgdg-redhat-repo-42.0-24.noarch

</pre>
<p>PostgreSQL は PGDG リポジトリからインストールします。</p>
<p>これ以降、<code>dnf</code> コマンドの実行時に署名つきパッケージの検証に必要な PGP キーの取り込みを表すメッセージが出力される場合があります。</li>
<li>PostgreSQL モジュールを無効にします。
<pre>$ <b>sudo dnf -qy module disable postgresql</b>
</pre>
<p>OS 標準のリポジトリからも PostgreSQL をインストールできますが、バージョンが古いので、PGDG リポジトリのほうを使うように、PostgreSQL モジュールを無効にしています。</li>
<li>PostgreSQL をインストールします。
<pre>$ <b>sudo dnf -qy install postgresql14-server</b>

Installed:
  lz4-1.8.3-3.el8_4.x86_64
  postgresql14-14.4-1PGDG.rhel8.x86_64
  postgresql14-libs-14.4-1PGDG.rhel8.x86_64
  postgresql14-server-14.4-1PGDG.rhel8.x86_64

</pre>
<p>ここでは、PostgreSQL 14 をインストールしています。</p>
<p>パッケージ名 <code>postgresql14-server</code> をはじめ、これ以降、コマンドラインに含まれる <code>14</code> は PostgreSQL のバージョンを表します。お使いになるバージョンに合わせ、適宜、読み替えてください。</li>
<li>データベースクラスタを作成します。
<pre>$ <b>PGSETUP_INITDB_OPTIONS="-E UTF8 --no-locale" sudo -E /usr/pgsql-14/bin/postgresql-14-setup initdb</b>
Initializing database ... OK

</pre>
<p>データベースクラスタはデータベースを格納する領域です。作成には通常 <code>initdb</code> コマンドを使いますが、ここでは、パッケージに付属のスクリプトを使います。</p>
<p>環境変数 <code>PGSETUP_INITDB_OPTIONS</code> には <code>initdb</code> コマンドに渡すオプションを指定します。ここでは、デフォルトのエンコーディングを UTF8、ロケールをなしに指定しています。</li>
<li>PostgreSQL のサービスを起動します。
<pre>$ <b>sudo systemctl start postgresql-14</b>
</pre>
</li>
<li>Redis をインストールします。
<pre>$ <b>sudo dnf -qy install redis</b>

Installed:
  redis-5.0.3-5.module+el8.5.0+657+2674830e.x86_64

</pre>
</li>
<li>Redis のサービスを起動します。
<pre>$ <b>sudo systemctl start redis</b>
</pre>
</li>
<li>EPEL リポジトリ<sup><a href="#footnote-2">[2]</a></sup> をインストールします。
<pre>$ <b>sudo dnf -qy install epel-release</b>

Installed:
  epel-release-8-13.el8.noarch

</pre>
<p>redis_fdw の動作に必要な Redis 用 C 言語ライブラリ Hiredis は EPEL リポジトリからインストールします。</li>
<li>redis_fdw のビルドに必要なパッケージをインストールします。
<pre>$ <b>sudo dnf -qy install make redhat-rpm-config postgresql14-devel hiredis-devel</b>

Upgraded:
  libgcc-8.5.0-10.1.el8_6.x86_64          libgomp-8.5.0-10.1.el8_6.x86_64
  libstdc++-8.5.0-10.1.el8_6.x86_64
Installed:
  annobin-10.29-3.el8.x86_64
  binutils-2.30-113.el8.x86_64
  clang-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  clang-devel-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  clang-libs-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  clang-resource-filesystem-13.0.1-2.module+el8.6.0+987+d36ea6a1.x86_64
  clang-tools-extra-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  cmake-filesystem-3.20.2-4.el8.x86_64
  compiler-rt-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  cpp-8.5.0-10.1.el8_6.x86_64
  dwz-0.12-10.el8.x86_64
  efi-srpm-macros-3-3.el8.noarch
  emacs-filesystem-1:26.1-7.el8.noarch
  gcc-8.5.0-10.1.el8_6.x86_64
  gcc-c++-8.5.0-10.1.el8_6.x86_64
  ghc-srpm-macros-1.4.2-7.el8.noarch
  glibc-devel-2.28-189.5.el8_6.x86_64
  glibc-headers-2.28-189.5.el8_6.x86_64
  go-srpm-macros-2-17.el8.noarch
  hiredis-0.13.3-13.el8.x86_64
  hiredis-devel-0.13.3-13.el8.x86_64
  isl-0.16.1-6.el8.x86_64
  kernel-headers-4.18.0-372.13.1.el8_6.x86_64
  libicu-devel-60.3-2.el8_1.x86_64
  libmpc-1.1.0-9.1.el8.x86_64
  libomp-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  libomp-devel-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  libstdc++-devel-8.5.0-10.1.el8_6.x86_64
  libxcrypt-devel-4.1.1-6.el8.x86_64
  llvm-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  llvm-devel-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  llvm-libs-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  llvm-static-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  llvm-test-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
  make-1:4.2.1-11.el8.x86_64
  ocaml-srpm-macros-5-4.el8.noarch
  openblas-srpm-macros-2-2.el8.noarch
  perl-srpm-macros-1-25.el8.noarch
  postgresql14-devel-14.4-1PGDG.rhel8.x86_64
  python-rpm-macros-3-41.el8.noarch
  python-srpm-macros-3-41.el8.noarch
  python3-rpm-macros-3-41.el8.noarch
  qt5-srpm-macros-5.15.2-1.el8.0.1.noarch
  redhat-rpm-config-129-1.el8.noarch
  rust-srpm-macros-5-2.el8.noarch
  unzip-6.0-46.el8.x86_64
  zip-3.0-23.el8.x86_64

</pre>
</li>
<li>redis_fdw のソースコードをダウンロードします。
<pre>$ <b>curl -LOs https://github.com/pg-redis-fdw/redis_fdw/archive/refs/heads/REL_14_STABLE.tar.gz</b>
</pre>
<p>redis_fdw のソースコードは PostgreSQL のバージョンごとにブランチが分かれていて、ダウンロード時はブランチ名を指定する必要があります。ここでは、PostgreSQL 14 に対応したブランチ <code>REL_14_STABLE</code> を指定しています。</li>
<li>redis_fdw のソースコードを展開します。
<pre>$ <b>tar -xzf REL_14_STABLE.tar.gz</b>
</pre>
</li>
<li>redis_fdw をビルドします。
<pre>$ <b>make -C redis_fdw-REL_14_STABLE USE_PGXS=1 PG_CONFIG=/usr/pgsql-14/bin/pg_config</b>
make: ディレクトリ '/home/vagrant/redis_fdw-REL_14_STABLE'　に入ります
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC -I. -I./ -I/usr/pgsql-14/include/server -I/usr/pgsql-14/include/internal  -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include  -c -o redis_fdw.o redis_fdw.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC -shared -o redis_fdw.so redis_fdw.o -L/usr/pgsql-14/lib  -Wl,--as-needed -L/usr/lib64  -L/usr/lib64 -Wl,--as-needed -Wl,-rpath,'/usr/pgsql-14/lib',--enable-new-dtags  -lhiredis
/usr/bin/clang -Wno-ignored-attributes -fno-strict-aliasing -fwrapv -Wno-unused-command-line-argument -Wno-compound-token-split-by-macro -O2  -I. -I./ -I/usr/pgsql-14/include/server -I/usr/pgsql-14/include/internal  -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include -flto=thin -emit-llvm -c -o redis_fdw.bc redis_fdw.c
make: ディレクトリ '/home/vagrant/redis_fdw-REL_14_STABLE' から出ます
</pre>
<p>引数の変数 <code>USE_PGXS</code> には PostgreSQL 用拡張モジュールの構築基盤 PGXS を使うことを表す <code>1</code>、<code>PG_CONFIG</code> には <code>pg_config</code> コマンドのパスを指定します。</li>
<li>redis_fdw をインストールします。
<pre>$ <b>sudo make -C redis_fdw-REL_14_STABLE install USE_PGXS=1 PG_CONFIG=/usr/pgsql-14/bin/pg_config</b>
make: ディレクトリ '/home/vagrant/redis_fdw-REL_14_STABLE'　に入ります
/usr/bin/mkdir -p '/usr/pgsql-14/lib'
/usr/bin/mkdir -p '/usr/pgsql-14/share/extension'
/usr/bin/mkdir -p '/usr/pgsql-14/share/extension'
/usr/bin/install -c -m 755  redis_fdw.so '/usr/pgsql-14/lib/redis_fdw.so'
/usr/bin/install -c -m 644 .//redis_fdw.control '/usr/pgsql-14/share/extension/'
/usr/bin/install -c -m 644 .//redis_fdw--1.0.sql  '/usr/pgsql-14/share/extension/'
/usr/bin/mkdir -p '/usr/pgsql-14/lib/bitcode/redis_fdw'
/usr/bin/mkdir -p '/usr/pgsql-14/lib/bitcode'/redis_fdw/
/usr/bin/install -c -m 644 redis_fdw.bc '/usr/pgsql-14/lib/bitcode'/redis_fdw/./
cd '/usr/pgsql-14/lib/bitcode' &amp;&amp; /usr/bin/llvm-lto -thinlto -thinlto-action=thinlink -o redis_fdw.index.bc redis_fdw/redis_fdw.bc
make: ディレクトリ '/home/vagrant/redis_fdw-REL_14_STABLE' から出ます
</pre>
<p>ソースコードと、それを展開したディレクトリはインストール後、削除しても構いません。</li>
</ol>
<h2 id="basic-usage">redis_fdw の基本的な使い方</h2>
<p>redis_fdw を使って PostgreSQL から Redis 上のデータを操作してみます。</p>
<ol>
<li>PostgreSQL のスーパーユーザに切り替わります。
<pre>$ <b>sudo su - postgres</b>
</pre>
</li>
<li>データベースを作成します。
<pre>$ <b>createdb redis_db</b>
</pre>
<p>ここでは、<code>redis_db</code> という名前のデータベースを作成しています。</li>
<li>データベースに接続します。
<pre>$ <b>psql redis_db</b>
psql (14.4)
"help"でヘルプを表示します。

=# 
</pre>
<p>ここでは、作成した <code>redis_db</code> データベースに接続しています。終了するには <code>\q</code> または <code>Ctrl</code> + <code>D</code> を入力します。</li>
<li>データベースに redis_fdw をインストールします。
<pre>=# <b>CREATE EXTENSION redis_fdw;</b>
CREATE EXTENSION
</pre>
<p>redis_fdw はプログラムとしてはインストール済みですが、実際に使うにはデータベースごとにインストールする必要があります。</p>
<p>ここでは、接続中の <code>redis_db</code> データベースにインストールしています。</li>
<li>外部サーバを作成します。
<pre>=# <b>CREATE SERVER redis_server
     FOREIGN DATA WRAPPER redis_fdw
     OPTIONS (address '127.0.0.1', port '6379');</b>
CREATE SERVER
</pre>
<p>外部サーバとは、外部データラッパを使って外部データにアクセスするのに必要な接続情報を定義したオブジェクトです。作成時には、外部サーバ名、外部データラッパ名、オプションを指定します。</p>
<p>redis_fdw では、以下のオプションが指定できます。</p>
<table>
<thead>
<tr>
<th>オプション</th>
<th>デフォルト値</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>address</code></td>
<td><code>127.0.0.1</code></td>
<td>Redis サーバの IP アドレスまたはホスト名</td>
</tr>
<tr>
<td><code>port</code></td>
<td><code>6379</code></td>
<td>Redis サーバのポート番号</td>
</tr>
</tbody>
</table>
<p>ここでは、<code>redis_server</code> という名前で redis_fdw の外部サーバを作成しています。オプションは例示のため、指定していますが、デフォルトでよければ、指定する必要はありません。</li>
<li>外部サーバのユーザマッピングを作成します。
<pre>=# <b>CREATE USER MAPPING FOR PUBLIC
     SERVER redis_server
     OPTIONS (password 'secret');</b>
CREATE USER MAPPING
</pre>
<p>外部サーバのユーザマッピングとは、外部サーバとユーザの対応づけを定義したオブジェクトです。作成時には、ユーザ名、外部サーバ名、オプションを指定します。</p>
<p>redis_fdw では、以下のオプションが指定できます。</p>
<table>
<thead>
<tr>
<th>オプション</th>
<th>デフォルト値</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>password</code></td>
<td>(なし)</td>
<td>Redis サーバ接続時の認証に使うパスワード</td>
</tr>
</tbody>
</table>
<p>ここでは、すべてのユーザを表す <code>PUBLIC</code> と外部サーバ <code>redis_server</code> を対応づけるユーザマッピングを作成しています。<code>password</code> オプションはパスワードが未設定であれば、指定する必要はありません。</li>
<li>外部テーブルを作成します。
<pre>=# <b>CREATE FOREIGN TABLE string_table (key text, val text)
     SERVER redis_server
     OPTIONS (database '0');</b>
CREATE FOREIGN TABLE
</pre>
<p>外部テーブルとは、外部データラッパを使って外部データにテーブルとしてアクセスできるように定義した特別なテーブルです。作成時には、テーブル定義、外部サーバ名、オプションを指定します。</p>
<p>redis_fdw では、以下のオプションが指定できます。</p>
<table>
<thead>
<tr>
<th>オプション</th>
<th>デフォルト値</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>database</code></td>
<td><code>0</code></td>
<td>Redis のデータベース番号。<code>0</code> からデフォルトでは <code>15</code> まで指定可能</td>
</tr>
<tr>
<td><code>tabletype</code></td>
<td>(なし)</td>
<td>Redis のデータ型。<code>list</code>、<code>hash</code>、<code>set</code>、<code>zset</code> が指定可能。未指定は <code>string</code></td>
</tr>
<tr>
<td><code>tablekeyprefix</code></td>
<td>(なし)</td>
<td>指定した接頭辞をもつキーに対象データを絞り込む</td>
</tr>
<tr>
<td><code>tablekeyset</code></td>
<td>(なし)</td>
<td>指定したキーをもつ集合に含まれるキーに対象データを絞り込む</td>
</tr>
<tr>
<td><code>singleton_key</code></td>
<td>(なし)</td>
<td>指定したキーの単一データを展開して対象データにする</td>
</tr>
</tbody>
</table>
<p><code>tabletype</code> オプションを指定しないと、文字列を表す <code>string</code> 型になりますが、値として <code>string</code> は指定できません。<code>teblekeyprefix</code>、<code>tablekeyset</code>、<code>singleton_key</code> はいずれも対象データを指定するオプションで、1 つのみ指定でき、指定しないと、すべてのデータが対象になります。詳しくは <a href="#table-options">redis_fdw のテーブルオプション</a> で説明します。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/07/redis_fdw-string.png" /></p>
<p>ここでは、<code>tabletype</code> オプションは指定せず、テーブル定義は <code>text</code> 型の <code>key</code> と <code>val</code> 列を定義しています。Redis のキーと <code>string</code> 型の値は PostgreSQL ではそれぞれ <code>text</code> 型に対応します。列名は任意です。<code>database</code> オプションはデフォルトでよければ、指定する必要はありません。</li>
<li>外部テーブルのデータを操作します。データの操作を受けて Redis サーバがどう動作するかを確認するため、Redis サーバで実行されたコマンドを出力するようにしておきます。終了するには <code>Ctrl</code> + <code>C</code> を入力します。
<pre>$ <b>redis-cli monitor</b>
OK
</pre>
<p>PostgreSQL 側でデータを 3 件登録します。</p>
<pre>=# <b>INSERT INTO string_table
     VALUES ('key1', 'val1'), ('key2', 'val2'), ('key3', 'val3');</b>
INSERT 0 3
</pre>
<p>Redis 側で実行されたコマンドが出力されます。</p>
<pre>1658092725.885750 [0 127.0.0.1:46042] "AUTH" "secret"
1658092725.886005 [0 127.0.0.1:46042] "SELECT" "0"
1658092725.886333 [0 127.0.0.1:46042] "EXISTS" "key1"
1658092725.886766 [0 127.0.0.1:46042] "SET" "key1" "val1"
1658092725.887016 [0 127.0.0.1:46042] "EXISTS" "key2"
1658092725.887387 [0 127.0.0.1:46042] "SET" "key2" "val2"
1658092725.887620 [0 127.0.0.1:46042] "EXISTS" "key3"
1658092725.887764 [0 127.0.0.1:46042] "SET" "key3" "val3"
</pre>
<p>PostgreSQL 側でデータを 1 件更新します。</p>
<pre>=# <b>UPDATE string_table SET val = 'val4' WHERE key = 'key1';</b>
UPDATE 1
</pre>
<p>Redis 側で実行されたコマンドが出力されます。</p>
<pre>1658092937.940434 [0 127.0.0.1:46044] "AUTH" "secret"
1658092937.940542 [0 127.0.0.1:46044] "SELECT" "0"
1658092937.940660 [0 127.0.0.1:46044] "DBSIZE"
1658092937.941136 [0 127.0.0.1:46046] "AUTH" "secret"
1658092937.941352 [0 127.0.0.1:46046] "SELECT" "0"
1658092937.941445 [0 127.0.0.1:46046] "EXISTS" "key1"
1658092937.941835 [0 127.0.0.1:46048] "AUTH" "secret"
1658092937.941976 [0 127.0.0.1:46048] "SELECT" "0"
1658092937.942118 [0 127.0.0.1:46046] "GET" "key1"
1658092937.942385 [0 127.0.0.1:46048] "SET" "key1" "val4"
</pre>
<p>PostgreSQL 側でデータを 1 件削除します。</p>
<pre>=# <b>DELETE FROM string_table WHERE key = 'key2';</b>
DELETE 1
</pre>
<p>Redis 側で実行されたコマンドが出力されます。</p>
<pre>1658092947.811658 [0 127.0.0.1:46050] "AUTH" "secret"
1658092947.811792 [0 127.0.0.1:46050] "SELECT" "0"
1658092947.811875 [0 127.0.0.1:46050] "DBSIZE"
1658092947.812375 [0 127.0.0.1:46052] "AUTH" "secret"
1658092947.812605 [0 127.0.0.1:46052] "SELECT" "0"
1658092947.812729 [0 127.0.0.1:46052] "EXISTS" "key2"
1658092947.813055 [0 127.0.0.1:46054] "AUTH" "secret"
1658092947.813197 [0 127.0.0.1:46054] "SELECT" "0"
1658092947.813342 [0 127.0.0.1:46052] "GET" "key2"
1658092947.813438 [0 127.0.0.1:46054] "DEL" "key2"
</pre>
<p>PostgreSQL 側でデータを 1 件取得します。</p>
<pre>=# <b>SELECT val FROM string_table WHERE key = 'key3';</b>
 val
------
 val3
(1 行)

</pre>
<p>Redis 側で実行されたコマンドが出力されます。</p>
<pre>1658092954.987736 [0 127.0.0.1:46056] "AUTH" "secret"
1658092954.987827 [0 127.0.0.1:46056] "SELECT" "0"
1658092954.987876 [0 127.0.0.1:46056] "DBSIZE"
1658092954.988113 [0 127.0.0.1:46058] "AUTH" "secret"
1658092954.988283 [0 127.0.0.1:46058] "SELECT" "0"
1658092954.988375 [0 127.0.0.1:46058] "EXISTS" "key3"
1658092954.988482 [0 127.0.0.1:46058] "GET" "key3"
</pre>
</li>
</ol>
<h2 id="table-options">redis_fdw のテーブルオプション</h2>
<p>redis_fdw を使って外部テーブルの定義時に指定できるオプションについて説明します。</p>
<h3 id="tabletype-option"><code>tabletype</code> オプション</h3>
<p><code>tabletype</code> は Redis のデータ型を指定するオプションです。オプションに指定できるデータ型について説明します。</p>
<h4 id="list-type"><code>list</code> 型</h4>
<p><code>list</code> は登録順に並べた文字列の集合を表すデータ型で、PostgreSQL では値を並べた <code>text</code> 型の配列に対応します。</p>
<pre>=# <b>CREATE FOREIGN TABLE list_table (key text, val text[])
     SERVER redis_server
     OPTIONS (database '1', tabletype 'list');</b>
CREATE FOREIGN TABLE
=# <b>INSERT INTO list_table
     VALUES ('key1', '{val1,val2,val3,val1}');</b>
INSERT 0 1
=# <b>SELECT * FROM list_table WHERE key = 'key1';</b>
 key  |          val
------+-----------------------
 key1 | {val1,val2,val3,val1}
(1 行)

=# <b>SELECT key, unnest(val) FROM list_table WHERE key = 'key1';</b>
 key  | unnest
------+--------
 key1 | val1
 key1 | val2
 key1 | val3
 key1 | val1
(4 行)

</pre>
<p>値 <code>val1</code>、<code>val2</code>、<code>val3</code>、<code>val1</code> を登録し、データを取得すると、値が登録順に並び、重複する値 <code>val1</code> が残っています。取得したデータは <code>unnest</code> 関数で行の集合に展開できます。</p>
<h4 id="hash-type"><code>hash</code> 型</h4>
<p><code>hash</code> はフィールドと値の文字列の組み合わせからなる集合を表すデータ型で、PostgreSQL ではフィールドと値を交互に並べた <code>text</code> 型の配列に対応します。</p>
<pre>=# <b>CREATE FOREIGN TABLE hash_table (key text, val text[])
     SERVER redis_server
     OPTIONS (database '2', tabletype 'hash');</b>
CREATE FOREIGN TABLE
=# <b>INSERT INTO hash_table
     VALUES ('key1', '{fld1,val1,fld2,val2,fld3,val3,fld1,val4}');</b>
INSERT 0 1
=# <b>SELECT * FROM hash_table WHERE key = 'key1';</b>
 key  |               val
------+---------------------------------
 key1 | {fld1,val4,fld2,val2,fld3,val3}
(1 行)

=# <b>SELECT key, json_object(val) FROM hash_table WHERE key = 'key1';</b>
 key  |                     json_object
------+-----------------------------------------------------
 key1 | {"fld1" : "val4", "fld2" : "val2", "fld3" : "val3"}
(1 行)

=# <b>SELECT key, (json_each_text(json_object(val))).*
     FROM hash_table WHERE key = 'key1';</b>
 key  | key  | value
------+------+-------
 key1 | fld1 | val4
 key1 | fld2 | val2
 key1 | fld3 | val3
(3 行)

</pre>
<p>フィールドと値の組み合わせ <code>fld1</code> と <code>val1</code>、<code>fld2</code> と <code>val2</code>、<code>fld3</code> と <code>val3</code>、<code>fld1</code> と <code>val4</code> を登録し、データを取得すると、フィールド <code>fld1</code> が重複する値 <code>val1</code>、<code>val4</code> はあとから登録した値 <code>val4</code> で上書きされています。取得したデータは <code>json_object</code> 関数で JSON オブジェクトに変換し、さらに <code>json_each_text</code> 関数でフィールドと値からなる行の集合に展開できます。</p>
<h4 id="set-type"><code>set</code> 型</h4>
<p><code>set</code> は順序づけがなく、重複がない文字列の集合を表すデータ型で、PostgreSQL では値を並べた <code>text</code> 型の配列に対応します。</p>
<pre>=# <b>CREATE FOREIGN TABLE set_table (key text, val text[])
     SERVER redis_server
     OPTIONS (database '3', tabletype 'set');</b>
CREATE FOREIGN TABLE
=# <b>INSERT INTO set_table
     VALUES ('key1', '{val1,val2,val3,val1}');</b>
INSERT 0 1
=# <b>SELECT * FROM set_table WHERE key = 'key1';</b>
 key  |       val
------+------------------
 key1 | {val1,val3,val2}
(1 行)

</pre>
<p>値 <code>val1</code>、<code>val2</code>、<code>val3</code>、<code>val1</code> を登録し、データを取得すると、重複する値 <code>val1</code> が取り除かれ、1 つになっています。</p>
<h4 id="zset-type"><code>zset</code> 型</h4>
<p><code>zset</code> はスコアで順序づけした、重複がない文字列の集合を表すデータ型で、PostgreSQL では値を並べた <code>text</code> 型の配列に対応します。</p>
<p><code>zset</code> 型の使い方は基本的に <code>set</code> 型と同じです。スコアは登録順に <code>0</code>、<code>1</code>、<code>2</code>、&#8230; が割り当てられます。スコアを PostgreSQL で扱うには、<a href="#singleton_key-option"><code>singleton_key</code> オプション</a> を指定する必要があります。</p>
<h3 id="tablekeyprefix-option"><code>tablekeyprefix</code> オプション</h3>
<p><code>tablekeyprefix</code> は指定した接頭辞をもつキーに対象データを絞り込むオプションで、1 つのデータベース内に異なる種類のデータをもたせ、別テーブルとしてアクセスするのに役立ちます。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/07/redis_fdw-keyprefix.png" /></p>
<pre>=# <b>CREATE FOREIGN TABLE keyprefix_table (key text, val text)
     SERVER redis_server
     OPTIONS (database '4', tablekeyprefix 'keyprefix_table:');</b>
CREATE FOREIGN TABLE
=# <b>INSERT INTO keyprefix_table VALUES ('keyprefix_table:key1', 'val1');</b>
INSERT 0 1
=# <b>INSERT INTO keyprefix_table VALUES ('keyprefix_table:key2', 'val2');</b>
INSERT 0 1
=# <b>INSERT INTO keyprefix_table VALUES ('key3', 'val3');</b>
ERROR:  key 'key3' does not match table key prefix 'keyprefix_table:'
=# <b>SELECT * FROM keyprefix_table;</b>
     key          | val
----------------------+------
 keyprefix_table:key2 | val2
 keyprefix_table:key1 | val1
(2 行)

=# <b>\! redis-cli -n 4 SET key3 val3</b>
OK
=# <b>\! redis-cli -n 4 KEYS \*</b>
1) "keyprefix_table:key2"
2) "key3"
3) "keyprefix_table:key1"
=# <b>SELECT * FROM keyprefix_table;</b>
     key          | val
----------------------+------
 keyprefix_table:key2 | val2
 keyprefix_table:key1 | val1
(2 行)

</pre>
<p><code>tablekeyprefix</code> オプションを指定すると、指定した接頭辞 <code>keyprefix_table:</code> をもつキー <code>keyprefix_table:key1</code>、<code>keyprefix_table:key2</code> のデータしか登録できず、接頭辞をもたないキー <code>key3</code> の登録はエラーになります。<code>\!</code> は PostgreSQL 外のコマンドを実行するコマンドで、Redis 上のデータを直接操作し、接頭辞をもたないキー <code>key3</code> を登録し、データを取得すると、接頭辞をもたないキー <code>key3</code> は結果に現れません。</p>
<p>大量のキーをもつデータベースでは、<code>tablekeyprefix</code> オプションではキーの検索に時間がかかるため、<code>tablekeyset</code> オプションでキーを集合に含まれるものに限ったほうがよいでしょう。</p>
<h3 id="tablekeyset-option"><code>tablekeyset</code> オプション</h3>
<p><code>tablekeyset</code> は指定したキーをもつ集合に含まれるキーに対象データを絞り込むオプションで、<code>tablekeyprefix</code> オプションと同じく、1 つのデータベース内で異なる種類のデータを扱うのに使います。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/07/redis_fdw-keyset.png" /></p>
<pre>=# <b>CREATE FOREIGN TABLE keyset_table (key text, val text)
     SERVER redis_server
     OPTIONS (database '5', tablekeyset 'keyset_table_keys');</b>
CREATE FOREIGN TABLE
=# <b>INSERT INTO keyset_table
     VALUES ('key1', 'val1'), ('key2', 'val2'), ('key3', 'val3');</b>
INSERT 0 3
=# <b>SELECT * FROM keyset_table;</b>
 key  | val
------+------
 key3 | val3
 key2 | val2
 key1 | val1
(3 行)

=# <b>\! redis-cli -n 5 SREM keyset_table_keys key2</b>
(integer) 1
=# <b>\! redis-cli -n 5 SMEMBERS keyset_table_keys</b>
1) "key3"
2) "key1"
=# <b>SELECT * FROM keyset_table;</b>
 key  | val
------+------
 key3 | val3
 key1 | val1
(2 行)

</pre>
<p><code>tablekeyset</code> オプションを指定すると、指定したキー <code>keyset_table_keys</code> をもつ集合に登録したキー <code>key1</code>、<code>key2</code>、<code>key3</code> が自動的に登録され、取得されるデータは集合に含まれるキーに限られます。Redis 上のデータを直接操作し、キー <code>key2</code> を削除し、データを取得すると、削除したキー <code>key2</code> は結果に現れなくなります。</p>
<h3 id="singleton_key-option"><code>singleton_key</code> オプション</h3>
<p><code>singleton_key</code> は指定したキーの単一データを展開して対象データにするオプションで、<code>tableytpe</code> オプション指定なしの <code>string</code> 型は 1 行 1 列、指定ありの <code>list</code> または <code>set</code> 型は行の集合、<code>hash</code> 型はフィールドと値からなる行の集合、<code>zset</code> 型は値とスコアからなる行の集合に展開されます。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/07/redis_fdw-singleton.png" /></p>
<pre>=# <b>CREATE FOREIGN TABLE singleton_table (val text, sc numeric)
     SERVER redis_server
     OPTIONS (database '6', tabletype 'zset', singleton_key 'singleton_table');</b>
CREATE FOREIGN TABLE
=# <b>INSERT INTO singleton_table
     VALUES ('val1', 10), ('val2', 20), ('val3', 30);</b>
INSERT 0 3
=# <b>SELECT * FROM singleton_table;</b>
 val  | sc
------+----
 val1 | 10
 val2 | 20
 val3 | 30
(3 行)

=# <b>UPDATE singleton_table SET sc = 40 WHERE val = 'val1';</b>
UPDATE 1
=# <b>SELECT * FROM singleton_table;</b>
 val  | sc
------+----
 val2 | 20
 val3 | 30
 val1 | 40
(3 行)

</pre>
<p><code>singleton_key</code> オプションと併せて <code>tabletype</code> オプションに <code>zset</code> 型を指定すると、PostgreSQL でスコアを扱えるようになります。スコアは PostgreSQL では <code>numeric</code> 型に対応します。<code>ORDER BY</code> 句で並び順を指定しなければ、データはスコアの昇順に取得されます。値 <code>val1</code> のスコアを <code>10</code> から <code>40</code> に更新すると、並び順が更新したスコア順に変わり、値 <code>val1</code> は最後に来るようになります。</p>
<h2 id="conclusion">おわりに</h2>
<p>この記事では、Redis 用外部データラッパ redis_fdw の概要、インストール、基本的な使い方、テーブルオプションについて説明しました。redis_fdw を使うことで、高速なデータの読み書きができる Redis と、SQL で複雑なデータの操作ができる PostgreSQL の特長を互いに活かし、さらに利用の幅が広がることでしょう。</p>
<p>SRA OSS では、PostgreSQL や Redis、redis_fdw のサポートをはじめとして、オープンソースに関する様々なサービスを提供しています。SRA OSS のサービスに興味がありましたら、お気軽に <a href="https://www.sraoss.co.jp/contact.php">こちら</a> からお問い合わせください。</p>
<hr />
<ol>
<li id="footnote-1">PostgreSQL Global Development Group (PGDG) が運営する PostgreSQL 関連パッケージの Yum リポジトリ</li>
<li id="footnote-2">Fedora Project が運営するエンタープライズ Linux 向け追加パッケージ (Extra Packages for Enterprise Linux; EPEL) の Yum リポジトリ</li>
</ol>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/oss-pro-support/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-oss" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、各種OSS のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>IvorySQL の紹介</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/ivorysql-intro/</link>
		
		<dc:creator><![CDATA[高塚 遥 (Haruka Takatsuka)]]></dc:creator>
		<pubDate>Mon, 17 Jan 2022 01:43:48 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[IvorySQL]]></category>
		<category><![CDATA[OSS]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=8103</guid>

					<description><![CDATA[更新日:2022年2月14日 IvorySQL は HighGo社を中心に開発されているオープンソース（Apache License）の Oracle互換データベースです。2021年 12月にバージョン 1.0 がリリー ...]]></description>
										<content:encoded><![CDATA[<p>更新日:2022年2月14日</p>
<p><a href="https://www.ivorysql.org/" target="_blank" rel="noopener">IvorySQL</a> は HighGo社を中心に開発されているオープンソース（Apache License）の Oracle互換データベースです。2021年 12月にバージョン 1.0 がリリースされました。IvorySQL は PostgreSQL と Oracle互換機能を提供するサードパーティ拡張 <a href="https://github.com/orafce/orafce" target="_blank" rel="noopener">orafce</a> の組み合わせをベースとしており、そこに更に独自の Oracle互換機能を追加しています。</p>
<p>本稿では、IvorySQL 1.0 の導入方法、機能、使用方法を解説します。また、Oracle Database 移行先の選択肢としての現時点の見解を記載します。</p>
<p><span id="more-8103"></span></p>
<p>2021年 1月 25日に IvorySQL 1.1 がリリースされました。本バージョンについてに補足も記載します。</p>
<h2>IvorySQL の導入方法</h2>
<h3>インストール</h3>
<p><a href="https://github.com/IvorySQL/IvorySQL" target="_blank" rel="noopener">IvorySQL は github で開発リポジトリが公開</a>されていて、そこからソースコードを取得できます。また、RHEL7、RHEL8 向けの rpmパッケージが <a href="https://yum.highgo.ca/ivorysql.html" target="_blank" rel="noopener">HighGo の yumリポジトリ</a>で公開されています。</p>
<p>ソースコードからビルドする手順例を以下に示します。</p>
<pre>$ git clone https://github.com/IvorySQL/IvorySQL.git
$ cd IvorySQL
$ ./configure --prefix=/usr/local/ivorysql --enable-debug
$ make
$ su -c 'make install'
$ cd contrib
$ make
$ su -c 'make install'
</pre>
<p>ビルド手順は PostgreSQL と同じです。configure で指定可能なオプションは PostgreSQL と違いありません。contrib が重要ですのでこれもビルド、インストールします。</p>
<p>環境変数としては以下を設定すれば良いでしょう。データが格納されるディレクトリを PostgreSQL と同様に PGDATA に設定します。</p>
<pre>export PATH=/usr/local/ivorysql/bin:$PATH
export PGDATA=/DATA/ivdata
export PGUSER=ivorysql
</pre>
<p>続いて、rpmパッケージから導入する手順を示します。本稿作成に当たっては RHEL 8.x を使用しましたが、RHEL 7.x や互換OS でも手順に違いはありません。</p>
<p><a href="https://yum.highgo.ca/ivorysql.html" target="_blank" rel="noopener">HighGo社の Webページ</a> から、リポジトリ定義のパッケージ<code>ivorysql-release-1.0-1.noarch.rpm</code>が入手できます。まずはこれをインストールします。</p>
<pre># yum install ivorysql-release-1.0-1.noarch.rpm
</pre>
<p>この rpmファイルには若干問題あるようで以下のエラーが出ます。</p>
<pre>エラー: トランザクション ロックを（/var/lib/rpm/.rpm.lock 上に）作成できません。(リソースが一時的に利用できません)
エラー: /etc/pki/rpm-gpg/HIGHGO-SOFTWARE-GPG-KEY: キー 1 のインポートに失敗しました。
警告: %post(ivorysql-release-1.0-1.noarch) スクリプトの実行に失敗しました。終了ステータス 1

Error in POSTIN scriptlet in rpm package ivorysql-release
</pre>
<p>そこで、失敗が報告されたキーのインポートを手動で実行しておきます。</p>
<pre># rpm --import /etc/pki/rpm-gpg/HIGHGO-SOFTWARE-GPG-KEY
</pre>
<p>次に IvorySQL のパッケージをインストールします。</p>
<pre># yum install ivorysql1-server ivorysql1 ivorysql1-libs ivorysql1-contrib
</pre>
<p>パッケージの構成は PostgreSQL の rpmパッケージを踏襲したものとなっています。IvorySQL を稼働させるための OSユーザ ivorysql が作られ、ivorysqlユーザのシェル設定に PGDATA環境変数が予め設定されています。また、追加のシェル設定を <code>.pgsql_profile</code> から読み込むようになっています。</p>
<p>PATH環境変数は設定されないので以下のように追加しておきます。</p>
<pre># su - ivorysql
$ export PATH=/usr/local/ivorysql/ivorysql-1/bin:$PATH
$ cat &gt; .pgsql_profile &lt;&lt;'EOF'
export PATH=/usr/local/ivorysql/ivorysql-1/bin:$PATH
EOF
</pre>
<h3>セットアップ</h3>
<p>initdb でデータベースインスタンスの初期データを作成します。Oracle互換機能を使うにはオプションに「<code>--compatible-mode oracle</code>」を指定します。デフォルトは互換モードではない「postgres」です。今のところ、すなわち IvorySQL 1.0 では、これは postgresql.conf の設定 <code>compatible_mode</code> の初期値を与えているだけです。それ以外のオプションは PostgreSQL の initdb と同じです。</p>
<pre>$ initdb -D $PGDATA -U ivorysql --locale=C --encoding=UTF8 --compatible-mode oracle
</pre>
<p>pg_ctl コマンドでサービスを起動します。pg_ctl コマンドの使い方も PostgreSQL と同じです。</p>
<pre>$ pg_ctl -D $PGDATA -l $PGDATA/start.log start
</pre>
<p>createdbコマンドでテスト用データベース ivdb1 を作って、psqlコマンドでデータベースに接続します。この辺りも PostgreSQL と変わるところがありません。</p>
<pre>$ createdb ivdb1
$ psql ivdb1
psql (14.0)
"help"でヘルプを表示します。

ivdb1=#
</pre>
<p>最初に行うべきことは orafce拡張の導入です。IvorySQL の Oracle互換機能のいくつかは orafce の中で提供されています。（→ v1.1 からは orafce は自動でロードされるようになっています）</p>
<pre>ivdb1=# CREATE EXTENSION orafce;
</pre>
<p>また、orafce で提供されるデータ型や関数、演算子などの互換オブジェクトをスキーマ修飾無しに使えるようにするため、<code>search_path</code>設定に oracleスキーマを加えます。pg_catalog よりも手前に置くことで、同名の組み込みオブジェクトよりも Oracle互換オブジェクトが優先して使われます。ここではデータベースに対して <code>search_path</code> のデフォルト値として設定することにします。また、ユーザと同名のivorysqlスキーマも作成しておきます。</p>
<pre>ivdb1=# SET search_path TO "$user", oracle, pg_catalog, public;
ivdb1=# ALTER DATABASE ivdb1 SET search_path TO "$user", oracle, pg_catalog, public;
ivdb1=# CREATE SCHEMA ivorysql;
</pre>
<p>なお、github での開発者のコメントによると IvorySQL 2.0 では、compatible_mode = oracle であれば search_path の変更をしなくとも互換オブジェクトが使われるようにするようです。</p>
<p>ここまでの手順で IvorySQL を使用する準備ができました。</p>
<h2>Oracle互換機能</h2>
<p>IvorySQL には様々な Oracle互換機能が含まれています。<a href="https://www.ivorysql.org/docs/intro/" target="_blank" rel="noopener">ドキュメント</a>に記載されている IvorySQL 独自の互換機能の他、<a href="https://github.com/orafce/orafce" target="_blank" rel="noopener">orafce</a> で提供されている機能もあります。</p>
<p>本節では IvorySQL で提供されている Oracle互換機能の中から主なものを見ていきます。</p>
<h3>パッケージ</h3>
<p>IvorySQL の最も有力な機能がパッケージのサポートです。パッケージとはストアドプロシージャや関数、データ型、変数、カーソルをまとめたものです。</p>
<p>パッケージは、名前空間を区切って機能の整理を良くするのみならず、特有の振る舞いを持ちます。このためネイティブの PostgreSQL の機能だけを使って移行しようとすると、同じ動作が再現できず、しばしば移行の難所となります。</p>
<p>同一セッション内の同一パッケージ内において、関数とプロシージャからカーソルと変数に共有してアクセスが可能で、これらに対する操作はコミット／ロールバックの影響を受けないという振る舞いをします。</p>
<p>いくつかサンプルを示します。コード中のコメントに説明を記載しています。</p>
<pre>$ cat pkg1.sql

CREATE OR REPLACE PACKAGE pkg1 AUTHID DEFINER IS -- パッケージ宣言
  PROCEDURE f_plus(p1 integer);    -- 変数に引数値を加えるプロシージャ
  FUNCTION f_incr RETURN integer;  -- 変数に 1 を足して、値を返す関数
END;

CREATE OR REPLACE PACKAGE BODY pkg1 AS           -- パッケージ本体定義
  v_num integer;    -- パッケージ変数／直接アクセスはできない
  PROCEDURE f_plus(p1 integer) IS
    BEGIN
      v_num := v_num + p1;
    END;
  FUNCTION f_incr RETURN integer IS
    BEGIN
      v_num := v_num + 1;  
      RETURN v_num;
    END;
  BEGIN             -- セッション中、最初の使用時に実行されるブロック
    v_num := 0;     -- パッケージ変数を初期化
  END;
</pre>
<p>上記のパッケージを読み込んで使用します。</p>
<pre>ivdb1=# \i pkg1.sql

ivdb1=# SELECT pkg1.f_incr;
 f_incr
--------
 1
(1 行)

ivdb1=# BEGIN;
ivdb1=*# SELECT pkg1.f_incr;
 f_incr
--------
 2
(1 行)

ivdb1=*# CALL pkg1.f_plus(10);
ivdb1=*# SELECT pkg1.f_incr;
 f_incr
--------
 13
(1 行)

ivdb1=*# ROLLBACK;

ivdb1=# SELECT pkg1.f_incr;
 f_incr
--------
 14
(1 行)
</pre>
<p>パッケージ変数が関数やプロシージャの間で共有されていることと、明示的トランザクション内でのパッケージ変数への操作が ROLLBACK されても取り消されていないことが、確認できます。</p>
<p>次はカーソルを使った例です。</p>
<pre>$ cat pkg2.sql

-- 操作する 10行データを持つテーブルを用意
DROP TABLE IF EXISTS tq;
CREATE TABLE tq (id int, v text);
INSERT INTO tq SELECT g, md5(g::text) FROM generate_series(1, 10) g;

CREATE OR REPLACE PACKAGE pkg2 AUTHID DEFINER IS -- パッケージ宣言
  CURSOR cur1 RETURN tq%ROWTYPE; -- (カーソル戻り値は %ROWTYPE 必須)
  FUNCTION f_next RETURN text; -- カーソルの次の行を返す関数
  FUNCTION f_prev RETURN text; -- カーソルの前の行を返す関数
END;

CREATE OR REPLACE PACKAGE BODY pkg2 IS -- パッケージ本体定義
  CURSOR cur1 RETURN tq%ROWTYPE IS SELECT id, v FROM tq ORDER BY id;
  FUNCTION f_next RETURN text IS
    rec tq%ROWTYPE;
    BEGIN
      FETCH cur1 INTO rec;
      RETURN rec.id || ': ' || rec.v;
    END;
  FUNCTION f_prev RETURN text IS
    rec tq%ROWTYPE;
    BEGIN
      FETCH PRIOR IN cur1 INTO rec;
      RETURN rec.id || ': ' || rec.v;
    END;
  BEGIN
    OPEN cur1;
  END;
</pre>
<p>上記を読み込みして実行してみます。</p>
<pre>ivdb1=# \i pkg2.sql

ivdb1=# SELECT ivorysql.pkg2.f_next;
       f_next
-------------------------------------
 1: c4ca4238a0b923820dcc509a6f75849b
(1 行)

ivdb1=# SELECT ivorysql.pkg2.f_next;
       f_next
-------------------------------------
 2: c81e728d9d4c2f636f067f89cc14862c
(1 行)

ivdb1=# SELECT ivorysql.pkg2.f_next;
       f_next
-------------------------------------
 3: eccbc87e4b5ce2fe28308fd9f2a7baf3
(1 行)

ivdb1=# SELECT ivorysql.pkg2.f_prev;
       f_prev
-------------------------------------
 2: c81e728d9d4c2f636f067f89cc14862c
(1 行)
</pre>
<p>カーソルを複数の関数から共有している動作が確認できます。上記例では行っていませんが、明示的なトランザクション内の関数実行をロールバックしても、カーソルの位置は変わりません。</p>
<p>また、上記例ではスキーマ名の修飾を加えて、パッケージ関数を呼び出しています。パッケージはスキーマに属するオブジェクトです。</p>
<p>パッケージ内の関数やプロシージャは plisql（PL/iSQL）という手続き言語で記述します。今のところこれはパッケージに対応している以外は PL/pgSQL とほぼ同じもので、PL/SQL で書かれたコードはそのままでは動作しません。</p>
<p>定義されているパッケージの一覧は以下の問い合わせで確認できます。今のところ psql の \d コマンドはパッケージに対応していません。</p>
<pre>ivdb1=# SELECT pkgname, pkgnamespace::regnamespace, pkgowner::regrole
          FROM pg_package;
 pkgname | pkgnamespace | pkgowner
---------+--------------+----------
 pkg2    | ivorysql     | ivorysql
 pkg1    | ivorysql     | ivorysql
(2 行)
</pre>
<p>また、1.0バージョンではパッケージの宣言と定義を psql のプロンプトで直接入力するとうまく動作しません。ファイルから読み込む形で使用する必要がありました。</p>
<h3>UPDATE、DELETE の互換構文サポート</h3>
<p>IvorySQL は UPDATE文、DELETE文における、Oracle 特有の構文をサポートしています。</p>
<p>UPDATE文で以下のように更新対象テーブルの別名が指定できます。</p>
<pre>ivdb1=# CREATE TABLE t1 (id int, v text);
ivdb1=# INSERT INTO t1 VALUES (1, 'AA'), (2, 'BB'), (3, 'CC');

ivdb1=# UPDATE t1 a SET a.v = 'XX' WHERE a.id = 1;
</pre>
<p>また、DELETE文で以下のように FROM を省略できます。</p>
<pre>ivdb1=# DELETE t1 WHERE id = 2;
</pre>
<p>これらは <code>compatible_mode</code>設定が oracle ではなく postgres であっても、動作します。</p>
<h3>シーケンスの互換構文サポート</h3>
<p>IvorySQL はシーケンス操作について、Oracle の構文をサポートしています。PostgreSQL の書き方も引き続き利用可能です。</p>
<pre>ivdb1=# CREATE SEQUENCE seq1;

ivdb1=# SELECT seq1.nextval;
 nextval
---------
 1
(1 row)

ivdb1=# SELECT seq1.currval;
 currval
---------
 1
(1 行)

ivdb1=# SELECT nextval('seq1');
 nextval
---------
 2
(1 行)
</pre>
<p>こちらも <code>compatible_mode</code>設定に関わりなく、常にこのように動作します。</p>
<h3>バイト単位文字数のサポート</h3>
<p>文字列型に関する Oracle と PostgreSQL の違いは、Oracle では NCHAR、NVARCHAR2、VARCHAR2 という PostgreSQL には無いデータ型が使われることと、データ型に指定する文字列長の単位がデフォルトではバイト単位（PostgreSQL では必ず文字単位）であるということです。</p>
<p>IvorySQL では、orafce拡張により VARCHAR2型と NVARCHAR2型が提供されます。また、設定<code>nls_length_semantics</code> に char または byte を設定することで、文字列長の単位を文字単位またはバイト単位に指定できます。</p>
<p>単体配布されている orafce でも VARCHAR2型は提供されますが、以下のようにバイト単位での上限を超えた文字を切り捨てる動作になっていました。エラーになるのが Oracle互換の動作です。</p>
<pre>orafce=# SELECT VARCHAR2(10) 'あいうえお';
 varchar2
----------
 あいう
(1 行)
</pre>
<p>（本動作は v1.1 で IvorySQL動作も orafce同様に文字を切り捨てる動作の方に変更されました）</p>
<p>IvorySQL で <code>nls_length_semantics = byte</code> を指定すると、バイト単位文字列長を超えたときには以下のようにエラーになります。また、NVARCHAR2型を使った場合、これは文字列長が必ず文字単位になるデータ型ですので、エラーになりません。</p>
<pre>ivdb1=# SET nls_length_semantics TO byte;
ivdb1=# SELECT VARCHAR2(10) 'あいうえお';
ERROR: value too long for type varchar2(10 byte)

ivdb1=# SELECT NVARCHAR2(10) 'あいうえお';
 nvarchar2
------------
 あいうえお
(1 行)
</pre>
<p><code>nls_length_semantics</code>設定は CHAR型、VARCHAR型の動作にも同様に影響します。</p>
<p>一方で <code>nls_length_semantics = byte</code> のときに、必ず文字単位であるはずの NCHAR型においてもバイト単位として扱われてしまいます。これは、IvorySQL や orafce による拡張が無くとも、PostgreSQL において NCHAR型が CHAR型の別名として受け入れられていることに起因すると考えられます。今後 IvorySQL が明示的に NCHAR型をサポートすることで、このような不整合が解消されることが望まれます。</p>
<h2>対応していないOracle互換動作</h2>
<p>本節では IvorySQL 1.0 ではサポートされていない、いくつかの機能について記載します。</p>
<h3>NUMBERデータ型</h3>
<p>IvorySQL のドキュメント上のサンプルではしばしば NUMBER型が登場しますが、今のところこれは IvorySQL 1.0 では提供されません。ネイティブの PostgreSQL に移植するときのように NUMERIC、integer、bigint などのいずれか数値データ型を選んで NUMBER型の代わりに使用する必要があります。</p>
<p>また、NUMERIC型を使う場合には注意が必要です。oracle互換のデータ型や演算子を透過的に使うために本例では <code>search_path</code>設定に oracleスキーマを含めていましたが、そうしていると数値リテラルを使った式で、使用する演算子を特定できずに以下のようなエラーを起こします。</p>
<pre>ivdb1=# CREATE TABLE t_num (numcol numeric, v text);
ivdb1=# INSERT INTO t_num VALUES (1, 'AAA'), (2, 'BBB'), (3, 'CCC');

ivdb1=# SELECT * FROM t_num WHERE numcol = 1;
ERROR: operator is not unique: numeric = integer

ivdb1=# SET search_path TO "$user", public; -- search_path から oracle を外せば動作
ivdb1=# SELECT * FROM t_num WHERE numcol = 1;
 numcol | v
--------+-----
      1 | AAA
(1 行)
</pre>
<p>これは <code>numcol = 1</code> ではなく <code>numcol = '1'</code> と書くか、数値部分をプリペアドステートメントのパラメータとして渡す限りは問題となりません。</p>
<p>NUMBER型は、開発中バージョンのドキュメントには対応型として明示的に記載があり、今後のバージョンで追加される予定のようです。（→ こちらは 1.1バージョンで追加されました。）</p>
<h3>NULL と 空文字列の同一視</h3>
<p>Oracle特有の振る舞いに、文字列データ型において NULL と空文字列（&#8221;）を同一視するということがあります。文字列連結では NULL が空文字列として扱われる一方、その他の文字列関数や演算子、NOT NULL制約では空文字列が NULL として扱われます。</p>
<p>この非互換性について IvorySQL固有のサポートは無く、ドキュメントにも言及がありません。しかしながら、同梱の orafce拡張に含まれる関数や演算子を通していくらかの互換動作が利用可能です。<br />
例えば、<code>orafce.varchar2_null_safe_concat</code>設定を on にすることで、NULL値を含む VARCHAR2型の連結を以下のように実行できます。<code>orafce.varchar2_null_safe_concat = off</code> なら、この SELECT結果は NULL です。</p>
<pre>ivdb1=# CREATE TABLE t_concat (c1 varchar2, c2 varchar2);
ivdb1=# INSERT INTO t_concat VALUES ('ABC', NULL);
ivdb1=# SET orafce.varchar2_null_safe_concat TO on;

ivdb1=# SELECT '{' || c1 || c2 || '}' as concat FROM t_concat;
 concat
--------
 {ABC}
(1 row)
</pre>
<p>一方で orafce で提供される oracle互換版の文字列処理関数であっても、「<code>SELECT length('') FROM dual</code>」が NULL になる、といった互換動作にはなりません（0になります）。また、NOT NULL制約に空文字列が投入不能、という動作にもなりません。</p>
<h2>まとめ</h2>
<p><a href="https://www.ivorysql.org/" target="_blank" rel="noopener">IvorySQL</a> にはここでは紹介できなかった互換機能も多数含まれています。見てきた通り、魅力的な互換機能を有する一方、まだまだ発展途上の部分も多くあるソフトウェアといえます。</p>
<p>オープンソースソフトウェアであることに加え、ネイティブ PostgreSQL に対する拡張モジュールではなく、データベース全体として提供される形態であるため、新たな互換機能の追加に仕組み上の制限が無いという点は魅力的です。今後の発展に期待したいところです。</p>
<h2>IvorySQL 1.1</h2>
<p>2022年 1月 25日に <a href="https://github.com/IvorySQL/IvorySQL/releases/tag/Ivory_REL_1_1">IvorySQL 1.1 がリリース</a>されました。以下の機能追加と修正が適用されており、本稿で問題として挙げた点のいくつかは既に改善されています。</p>
<ul>
<li>NUMBER型のサポート</li>
<li>orafceの自動ロード</li>
<li>Oracle互換モードではsearch_path設定をしなくとも自動的に互換オブジェクトを使用</li>
<li>バイト単位文字数のサポートについて挙動が変更されました</li>
<li>そのままでは動作しないドキュメント上のサンプルを修正</li>
</ul>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>pgAdmin 4 (PostgreSQL 用 GUI 管理ツール)</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/pgadmin4/</link>
		
		<dc:creator><![CDATA[佐藤 友章 (Tomoaki Sato)]]></dc:creator>
		<pubDate>Fri, 07 Jan 2022 04:09:32 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[pgAdmin]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=8030</guid>

					<description><![CDATA[pgAdmin 4 は PostgreSQL 用の GUI 管理ツールです。pgAdmin 4 を利用すれば、テーブル作成やデータ編集など、PostgreSQL の操作を GUI で行えます。とくにコマンド操作に不慣れな ...]]></description>
										<content:encoded><![CDATA[<p><a href="https://www.pgadmin.org/">pgAdmin 4</a> は PostgreSQL 用の GUI 管理ツールです。pgAdmin 4 を利用すれば、テーブル作成やデータ編集など、PostgreSQL の操作を GUI で行えます。とくにコマンド操作に不慣れなユーザには便利なツールです。</p>
<p>この記事では pgAdmin 4 の概要、インストールおよび起動・アクセス、DB サーバ登録の手順を説明します。</p>
<p><span id="more-8030"></span></p>
<h2>pgAdmin 4 とは</h2>
<p>pgAdmin 4 は pgAdmin Development Team によって開発され、PostgreSQL と同じく <a href="https://www.pgadmin.org/licence/">PostgreSQL ライセンス</a> で公開されています。Linux や Windows、macOS 上で動作し、PostgreSQL 10 以降に対応しています。<a href="https://powergres.sraoss.co.jp/">PowerGres</a> など、PostgreSQL ベースのデータベースでも利用できます。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-dashboard.png" alt="ダッシュボード画面" /></p>
<p>pgAdmin 4 のおもな機能は以下のとおりです。</p>
<ul>
<li>SQL 実行機能</li>
<li>データグリッド表示・編集機能</li>
<li>実行計画グラフィカル表示機能</li>
<li>アクセス権限対話型設定機能</li>
<li>手続き言語 PL/pgSQL デバッグ機能</li>
<li>スキーマ差分管理機能</li>
<li>自動 VACUUM 管理機能</li>
<li>監視ダッシュボード機能</li>
<li>メンテナンス実行機能</li>
<li>ジョブスケジュール実行機能</li>
<li>データベースオブジェクト管理機能</li>
</ul>
<p>pgAdmin 4 の名前についている 4 は pgAdmin の 4 世代目を表します。単にpgAdmin と書いてある場合は、1 世代前の pgAdmin III (3) を指している場合もあるので、注意してください。バージョン番号は 4 とは別についています。最新バージョンは 2022 年 1 月時点で 6.3 で、名前を含めて書くと pgAdmin 4 v6.3 になります。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-version.png" alt="世代とバージョン番号" /></p>
<p>pgAdmin 4 には 2 つのモードがあります。デスクトップモードはユーザが pgAdmin 4 を起動し、利用するモードです。それに対して、サーバモードは pgAdmin 4 を Web アプリケーションとして起動しておき、ユーザが Web ブラウザでログインし、利用するモードです。サーバモードは Web モードとも呼ばれます。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-desktop-mode.png" alt="デスクトップモード" /></p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-web-mode.png" alt="サーバモード" /></p>
<h2>pgAdmin 4 のインストール</h2>
<p>pgAdmin 4 のソースコードや各プラットフォーム向けのバイナリは <a href="https://www.pgadmin.org/download/">ダウンロードページ</a> で入手できます。ここでは Red Hat 系 Linux と Windows へのインストール手順を説明します。</p>
<h3>Red Hat 系 Linux へのインストール</h3>
<p>pgAdmin 4 は Red Hat 系 Linux として RHEL/CentOS 7 および 8、Rocky Linux 8、Fedora 33 および34 に対応しています。</p>
<p>Red Hat 系 Linux へのインストール手順は以下のとおりです。</p>
<ol>
<li>PostgreSQL のリポジトリをインストールしている場合は、インストール対象から pgAdmin を外します。リポジトリの設定ファイルは <code>/etc/yum.repos.d</code> ディレクトリに格納されていて、ファイル名は RHEL/CentOS/Rocky Linux の場合は <code>pgdg-redhat-all.repo</code>、Fedora の場合は <code>pgdg-fedora-all.repo</code> です。ファイルを編集し、<code>[pgdg-common]</code> セクションに <code>exclude=pgadmin*</code> の行を追加します。
<pre>[pgdg-common]
name=PostgreSQL common RPMs for RHEL/CentOS $releasever - $basearch
baseurl=https://download.postgresql.org/pub/repos/yum/common/redhat/rhel-$releasever-$basearch
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-PGDG
repo_gpgcheck = 1
<b>exclude=pgadmin*</b>
</pre>
<p>この設定を行っておかないと、リポジトリのインストール後に pgAdmin 4 をインストールしようとしても、なぜかリポジトリをインストールしようとし、リポジトリがインストール済みで、インストールが進まない状態になってしまいます。</p>
<pre>(以下、例示のため、実行しない)
$ <b>sudo yum -y install pgadmin4</b>
Last metadata expiration check: 0:00:27 ago on 2021年12月29日 04時02分58秒.
Package pgadmin4-redhat-repo-0.9-2.noarch is already installed.
Dependencies resolved.
Nothing to do.
Complete!
</pre>
</li>
<li>pgAdmin 4 のリポジトリをインストールします。リポジトリの URL は OS によって異なり、新しいバージョンがリリースされている場合もあるので、<a href="https://www.pgadmin.org/download/pgadmin-4-rpm/">RPM 向けダウンロードページ</a> を確認してください。以下は RHEL/CentOS/Rocky Linux の場合です。
<pre>$ <b>sudo yum -y install https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm</b>
(省略)
Installed:
  pgadmin4-redhat-repo-2-1.noarch

Complete!
</pre>
</li>
<li>pgAdmin 4 をインストールします。何をインストールするかによってパッケージが異なります。デスクトップモードのみの場合は <code>pgadmin4-desktop</code>、サーバモードのみの場合は <code>pgadmin4-web</code>、両方ともの場合は <code>pgadmin4</code> です。ここでは両方ともインストールします。
<pre>$ <b>sudo yum -y install pgadmin4</b>
(省略)
Installed:
  apr-1.6.3-12.el8.x86_64
  apr-util-1.6.1-6.el8.x86_64
  apr-util-bdb-1.6.1-6.el8.x86_64
  apr-util-openssl-1.6.1-6.el8.x86_64
  centos-logos-httpd-85.8-2.el8.noarch
  httpd-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64
  httpd-filesystem-2.4.37-43.module_el8.5.0+1022+b541f3b1.noarch
  httpd-tools-2.4.37-43.module_el8.5.0+1022+b541f3b1.x86_64
  libatomic-8.5.0-4.el8_5.x86_64
  mailcap-2.1.48-3.el8.noarch
  mod_http2-1.15.7-3.module_el8.4.0+778+c970deab.x86_64
  pgadmin4-6.3-1.el8.noarch
  pgadmin4-desktop-6.3-1.el8.x86_64
  pgadmin4-server-6.3-1.el8.x86_64
  pgadmin4-web-6.3-1.el8.noarch
  python3-mod_wsgi-4.6.4-4.el8.x86_64
  xdg-utils-1.1.2-5.el8.noarch

Complete!
</pre>
<p>これで Red Hat 系 Linux へのインストールは完了です。</li>
<li>サーバモードをインストールした場合は、セットアップスクリプトを実行します。スクリプトでは <code>semanage</code> コマンドで SELinux の設定を行うので、コマンドがない場合はインストールしておきます。パッケージは RHEL/CentOS 7 の場合は <code>policycoreutils-python</code>、それ以外の場合は <code>policycoreutils-python-utils</code> です。スクリプトの途中で pgAdmin 4 のログインに使用する Email アドレス、パスワードの入力が求められるので、入力します。また、Web サーバが起動していない場合は、サービスを有効にし、起動するかの確認が求められるので、そうする場合は <code>y</code>、そうしない場合は <code>n</code> を入力します。ここでは <code>y</code> を入力します。
<pre>$ <b>sudo /usr/pgadmin4/bin/setup-web.sh</b>
Setting up pgAdmin 4 in web mode on a Redhat based platform...
Creating configuration database...
NOTE: Configuring authentication for SERVER mode.

Enter the email address and password to use for the initial pgAdmin user account:

Email address: <b>(Email アドレスを入力)</b>
Password: <b>(パスワードを入力)</b>
Retype password: <b>(パスワードをもう一度入力)</b>
pgAdmin 4 - Application Initialisation
======================================

Creating storage and log directories...
Configuring SELinux...
The Apache web server is not running. We can enable and start the web server for you to finish pgAdmin 4 installation. Continue (y/n)? <b>y</b>
Created symlink from /etc/systemd/system/multi-user.target.wants/httpd.service → /usr/lib/systemd/system/httpd.service.
Apache successfully enabled.
Apache successfully started.
You can now start using pgAdmin 4 in web mode at http://127.0.0.1/pgadmin4
</pre>
</li>
</ol>
<h3>Windows へのインストール</h3>
<p>pgAdmin 4 は Windows として Windows 8 以降および Windows Server 2012 以降に対応しています。</p>
<p>pgAdmin 4 は、Windows では PostgreSQL に付属しているので、PostgreSQL のインストール時にチェックを外さなければ、インストールされています。バージョンは PostgreSQL リリース時の最新ですが、その後、アップデートされないので、少し古い場合があります。PostgreSQL 14.1 に付属の pgAdmin 4 はバージョン 6.1 で、6.3 の 2 つ前のバージョンです。</p>
<p>pgAdmin 4 は PostgreSQL とは別に単体でもインストールできます。Windows へのインストール手順は以下のとおりです。</p>
<ol>
<li>インストーラをダウンロードします。<a href="https://www.pgadmin.org/download/pgadmin-4-windows/">Windows 向けダウンロードページ</a> にアクセスし、インストールするバージョンのリンクをたどります。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-download.png" alt="ダウンロード画面" /></p>
<p>リンク先でインストーラのファイルをダウンロードします。ここでは v6.3 をインストールするので、pgAdmin 4 v6.3 のリンクをクリックし、<code>pgadmin4-6.3-x64.exe</code> のファイルをダウンロードします。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-download2.png" alt="ダウンロード画面" /></p>
</li>
<li>ダウンロードしたファイルをダブルクリックし、インストーラを起動します。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-installer.png" alt="インストーラ" /></p>
</li>
<li>インストールモード選択のダイアログが表示されるので、個人向けにインストールする場合は Install for me only、全ユーザ向けにインストールする場合は Install for all users をクリックします。ここでは個人向けにインストールします。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-setup-install-mode.png" alt="インストールモード選択画面" /></p>
</li>
<li>インストーラが起動されるので、Next をクリックします。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-setup-welcome.png" alt="インストール開始画面" /></p>
</li>
<li>ライセンスの内容が表示されるので、確認し、同意する場合は I accept the agreement を選択し、Next をクリックします。ライセンスに同意しなければ、インストールできません。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-setup-license.png" alt="ライセンス確認画面" /></p>
</li>
<li>インストール先のフォルダのパスを入力するか、Browse&#8230; をクリックし、フォルダを選択し、Next をクリックします。個人向けにインストールする場合のデフォルトは <code>C:\Users\ユーザ名\AppData\Local\Programs\pgAdmin 4\v6</code> で、ここではデフォルトのままにします。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-setup-destination.png" alt="インストール先選択画面" /></p>
</li>
<li>スタートメニューのフォルダ名を入力するか、Browse&#8230; をクリックし、フォルダを選択し、Next をクリックします。フォルダを作成したくない場合は Don&#8217;t create a Start Menu folder にチェックを入れます。デフォルトは pgAdmin 4 で、ここではデフォルトのままにします。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-setup-start-menu.png" alt="スタートメニュー選択画面" /></p>
</li>
<li>これでインストールの準備は完了です。インストールの設定が表示されるので、確認し、問題なければ、Install をクリックします。問題があれば、Back をクリックし、前に戻って設定を変更します。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-setup-confirm.png" alt="インストール確認画面" /></p>
</li>
<li>インストールが開始されるので、完了するまでしばらく待ちます。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-setup-progress.png" alt="インストール進行中画面" /></p>
</li>
<li>これで Windows へのインストールは完了です。Finish をクリックすると、インストーラが閉じられます。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-setup-finish.png" alt="インストール完了画面" /></p>
</li>
</ol>
<h2>pgAdmin 4 の起動・アクセス</h2>
<p>pgAdmin 4 の利用にあたり、デスクトップモードでの起動方法と、サーバモードでのアクセス方法を説明します。</p>
<h3>デスクトップモードでの起動</h3>
<p>デスクトップモードで pgAdmin 4 を起動するには、Red Hat 系 Linux では RHEL/CentOS/Rocky Linux 8 の場合には画面左上のアクティビティをクリックし、画面左のダッシュ内、一番下のグリッド (9 つの丸からなるアイコン) をクリックすると、画面中央にアプリケーションの一覧が表示されるので、その中から pgAdmin 4 をクリックします。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-launch-linux.png" alt="Linux での起動" /></p>
<p>または以下のコマンドを実行します。</p>
<pre>$ <b>/usr/pgadmin4/bin/pgadmin4</b>
</pre>
<p>Windows では pgAdmin 4 を個別にインストールした場合にはスタートメニューのアプリケーションの一覧から pgAdmin 4 フォルダ内の pgAdmin 4 をクリックします。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-launch-windows.png" alt="Windows での起動" /></p>
<p>デスクトップモードで起動すると、最初にマスタパスワードの設定を行います。マスタパスワードは DB サーバのパスワードを暗号化するのに利用します。ここで指定したマスタパスワードは次回から起動時に入力が求められます。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-master-password.png" alt="マスタパスワードの設定" /></p>
<p>表示言語はデフォルトで英語になっています。日本語に変更するには、File メニューの Preferences をクリックし、Miscellaneous 内の User language を開き、表示言語として Japanese を選択し、Save をクリックします。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-language.png" alt="表示言語の設定" /></p>
<p>表示言語の設定を反映するには、pgAdmin 4 を再起動する必要があります。</p>
<h3>サーバモードでのアクセス</h3>
<p>サーバモードで pgAdmin 4 にアクセスするには、Red Hat 系 Linux では Web ブラウザで <code>http://(ホスト名または/ IP アドレス)/pgadmin4</code> の URL にアクセスします。ログイン画面が表示されるので、インストール時に指定した Email アドレス、パスワードを入力し、表示言語として Japanese を選択し、Login をクリックします。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-login.png" alt="ログイン画面" /></p>
<p>Windows でもサーバモードで起動できますが、Web サーバがフレームワークに付属のテスト用なので、別途 Apache HTTP Server をインストールし、利用したほうがよいでしょう。その手順について、ここでは説明を省略しますが、興味があれば <a href="https://www.pgadmin.org/docs/pgadmin4/latest/server_deployment.html#apache-httpd-configuration-windows">マニュアル</a> を参照してください。</p>
<h2>DB サーバの登録</h2>
<p>Windows では自動的にローカルホストの DB サーバを検出し、登録してくれますが、検出されなかった場合や Red Hat 系 Linux の場合には、手動で DB サーバを登録します。</p>
<p>DB サーバの登録手順は以下のとおりです。</p>
<ol>
<li>ダッシュボードのクイックリンク内の新しいサーバを追加をクリックします。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-server-add.png" alt="DB サーバ追加" /></p>
</li>
<li>登録するサーバの設定を入力し、保存をクリックします。最低限入力が必要な設定は General タブの名前、接続タブのホスト名/アドレスです。名前は識別子として使用する DB サーバの名前、ホスト名/アドレスは接続先の DB サーバのホスト名/アドレスです。ここでは、あらかじめ作成済みの PostgreSQL 14 の DB サーバを登録するため、まず、General タブで名前として PostgreSQL 14 を入力します。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-server-add2.png" alt="DB サーバ追加の一般設定" /></p>
<p>次に、接続タブでホスト名/アドレスとして localhost、パスワードを入力し、パスワードを保存を有効にします。</p>
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-server-add3.png" alt="DB サーバ追加の接続設定" /></p>
<p>ポート番号やユーザ名がデフォルトと異なる場合には、それらに合わせて設定を入力してください。</li>
<li>DB サーバの登録が完了すると、画面左のブラウザ内の指定したサーバグループ内に指定した名前で DB サーバが表示されます。ここではサーバグループ Servers 内に DB サーバ PostgreSQL 14 が表示されます。
<p style="text-align: center;"><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2022/01/pgadmin4-dashboard.png" alt="ダッシュボード画面" /></p>
<h2>おわりに</h2>
<p>この記事では PostgreSQL 用の GUI 管理ツール pgAdmin4 の概要と、インストールおよび起動・アクセス、DB サーバ登録の手順を説明しました。pgAdmin 4 は多くの機能を備えていて、個々の機能は説明できていませんが、GUI なので直感的に操作できると思います。</p>
<p>pgAdmin 4 は基本的にコマンド操作に不慣れなユーザ向けですが、それだけでなく、実行計画グラフィカル表示機能や手続き言語 PL/pgSQL デバッグ機能など、コマンドでは実現できない機能も提供しているので、GUI だと毛嫌いせずに一度利用してみてもよいでしょう。</li>
</ol>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>TimescaleDB によるデータ分析</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/timescaledb-analysis/</link>
		
		<dc:creator><![CDATA[佐藤 友章 (Tomoaki Sato)]]></dc:creator>
		<pubDate>Thu, 24 Jun 2021 01:10:49 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[TimescaleDB]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=7054</guid>

					<description><![CDATA[TimescaleDB は、IoT 機器のセンサーやスマートメーターが生成するデータのように、時系列に沿って発生するデータを扱うのに適した時系列データベースの機能を、リレーショナルデータベース PostgreSQL に追 ...]]></description>
										<content:encoded><![CDATA[<p>TimescaleDB は、IoT 機器のセンサーやスマートメーターが生成するデータのように、時系列に沿って発生するデータを扱うのに適した時系列データベースの機能を、リレーショナルデータベース PostgreSQL に追加する拡張モジュールです。</p>
<p>この記事では、TimescaleDB の備える時系列データ向けの分析機能について紹介します。TimescaleDB の概要やインストール、基本的な使い方については前回の <a href="https://www.sraoss.co.jp/tech-blog/pgsql/timescaledb-intro/">TimescaleDB の紹介</a> を参照してください。</p>
<p><span id="more-7054"></span></p>
<h2>時系列データの分析関数</h2>
<p>TimescaleDB には時系列データの分析に便利な関数がいくつかあります。ここでは、前回生成したデータを使って各関数の概要と使い方について紹介していきます。それには、まず、<code>test</code> データベースに接続しておきます。</p>
<pre>$ psql test
psql (13.2)
"help"でヘルプを表示します。

=#
</pre>
<h2><code>time_bucket</code> 関数</h2>
<p><code>time_bucket</code> 関数は時間を任意の間隔で丸める関数です。同じような関数は PostgreSQL にもあって <code>date_trunc</code> 関数と言います。</p>
<p><code>date_trunc</code> 関数を使うと、例えば、1 分間隔で収集した温度のデータに対し、1 時間ごとの平均を求められます。</p>
<pre>=# SELECT date_trunc('hour', time) AS time_1h,
     location,
     avg(temperature)
   FROM conditions
   GROUP BY time_1h, location
   ORDER BY time_1h, location;
       time_1h       | location |        avg
---------------------+----------+--------------------
 2020-01-30 00:00:00 | nagoya   | 19.883333333333333
 2020-01-30 00:00:00 | osaka    |  20.68333333333333
 2020-01-30 00:00:00 | tokyo    |              20.73
 2020-01-30 01:00:00 | nagoya   |  19.65666666666666
 2020-01-30 01:00:00 | osaka    |              20.82
 2020-01-30 01:00:00 | tokyo    |  20.04000000000001
 2020-01-30 02:00:00 | nagoya   | 18.854999999999993
 2020-01-30 02:00:00 | osaka    |  19.89833333333333
 2020-01-30 02:00:00 | tokyo    | 20.345000000000006
 2020-01-30 03:00:00 | nagoya   | 20.338333333333335
 2020-01-30 03:00:00 | osaka    |             20.395
 2020-01-30 03:00:00 | tokyo    | 20.781666666666673
(省略)
</pre>
<p>同じことは <code>time_bucket</code> 関数でもできます。</p>
<pre>=# SELECT time_bucket('1 hour', time) AS time_1h,
     location,
     avg(temperature)
   FROM conditions
   GROUP BY time_1h, location
   ORDER BY time_1h, location;
       time_1h       | location |        avg
---------------------+----------+--------------------
 2020-01-30 00:00:00 | nagoya   | 19.883333333333333
 2020-01-30 00:00:00 | osaka    |  20.68333333333333
 2020-01-30 00:00:00 | tokyo    |              20.73
 2020-01-30 01:00:00 | nagoya   |  19.65666666666666
 2020-01-30 01:00:00 | osaka    |              20.82
 2020-01-30 01:00:00 | tokyo    |  20.04000000000001
 2020-01-30 02:00:00 | nagoya   | 18.854999999999993
 2020-01-30 02:00:00 | osaka    |  19.89833333333333
 2020-01-30 02:00:00 | tokyo    | 20.345000000000006
 2020-01-30 03:00:00 | nagoya   | 20.338333333333335
 2020-01-30 03:00:00 | osaka    |             20.395
 2020-01-30 03:00:00 | tokyo    | 20.781666666666673
(省略)
</pre>
<p><code>date_trunc</code> 関数と <code>time_bucket</code> 関数の違いは、<code>date_trunc</code> 関数が 1 時間や 1 日など、固定の時間でしか丸められないのに対し、<code>time_bucket</code> 関数は 3 時間や 5 日など、任意の時間で丸められることです。<code>time_bucket</code> 関数を使うと、例えば、3 時間ごとの平均を求められます。</p>
<pre>=# SELECT time_bucket('3 hours', time) AS time_3h,
     location,
     avg(temperature)
   FROM conditions
   GROUP BY time_3h, location
   ORDER BY time_3h, location;
       time_3h       | location |        avg
---------------------+----------+--------------------
 2020-01-30 00:00:00 | nagoya   |             19.465
 2020-01-30 00:00:00 | osaka    |  20.46722222222223
 2020-01-30 00:00:00 | tokyo    | 20.371666666666673
 2020-01-30 03:00:00 | nagoya   | 20.202777777777776
 2020-01-30 03:00:00 | osaka    |  19.89277777777778
 2020-01-30 03:00:00 | tokyo    | 21.122222222222224
 2020-01-30 06:00:00 | nagoya   |  19.99055555555557
 2020-01-30 06:00:00 | osaka    | 19.288333333333338
 2020-01-30 06:00:00 | tokyo    | 19.353333333333335
 2020-01-30 09:00:00 | nagoya   | 20.950000000000003
 2020-01-30 09:00:00 | osaka    | 20.285000000000007
 2020-01-30 09:00:00 | tokyo    | 20.203333333333326
(省略)
</pre>
<h3><code>time_bucket_gapfill</code> 関数</h3>
<p><code>time_bucket_gapfill</code> 関数は <code>time_bucket</code> 関数と同じく時間を任意の間隔で丸める関数ですが、欠落したデータを補ってくれることが異なります。</p>
<p>例えば、2020 年 2 月 1 日、東京のデータを削除し、人為的にデータを欠落させ、<code>time_bucket</code> 関数を使って 3 時間ごとの平均を求めると、6 時、9 時のデータが欠落しているのが分かります。</p>
<pre>=# DELETE FROM conditions
   WHERE time &gt;= '2020-02-01 06:00:00'
     AND time &lt; '2020-02-01 12:00:00' AND location = 'tokyo';
DELETE 360
=# SELECT time_bucket('3 hours', time) AS time_3h, location, avg(temperature) FROM conditions WHERE time &gt;= '2020-02-01'
     AND time &lt; '2020-02-02'
   GROUP BY time_3h, location
   ORDER BY time_3h, location;
       time_3h       | location |        avg
---------------------+----------+--------------------
 2020-02-01 00:00:00 | nagoya   |  19.75833333333333
 2020-02-01 00:00:00 | osaka    |  20.46555555555556
 2020-02-01 00:00:00 | tokyo    | 19.315555555555555
 2020-02-01 03:00:00 | nagoya   | 19.328888888888887
 2020-02-01 03:00:00 | osaka    | 20.167777777777783
 2020-02-01 03:00:00 | tokyo    | 19.592777777777773
 2020-02-01 06:00:00 | nagoya   |  19.87055555555555
 2020-02-01 06:00:00 | osaka    |  20.07277777777777
 2020-02-01 09:00:00 | nagoya   | 19.240555555555563
 2020-02-01 09:00:00 | osaka    |             19.915
 2020-02-01 12:00:00 | nagoya   | 20.018888888888895
 2020-02-01 12:00:00 | osaka    |  19.66388888888888
 2020-02-01 12:00:00 | tokyo    | 19.437777777777782
 2020-02-01 15:00:00 | nagoya   | 19.904444444444444
 2020-02-01 15:00:00 | osaka    |  20.11388888888888
 2020-02-01 15:00:00 | tokyo    | 19.707222222222217
 2020-02-01 18:00:00 | nagoya   | 20.251111111111115
 2020-02-01 18:00:00 | osaka    | 20.023888888888884
 2020-02-01 18:00:00 | tokyo    | 20.571666666666676
 2020-02-01 21:00:00 | nagoya   |  19.91722222222222
 2020-02-01 21:00:00 | osaka    | 20.952777777777772
 2020-02-01 21:00:00 | tokyo    | 19.904444444444447
(22 行)
</pre>
<p><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2021/06/timescaledb-gap.png" alt="データ欠落" /></p>
<p>それに対し、<code>time_bucket_gapfill</code> 関数を使うと、欠落したデータに <code>NULL</code> が補われるようになります。</p>
<pre>=# SELECT time_bucket_gapfill('3 hours', time) AS time_3h,
     location,
     avg(temperature)
   FROM conditions
   WHERE time &gt;= '2020-02-01'
     AND time &lt; '2020-02-02'
   GROUP BY time_3h, location
   ORDER BY time_3h, location;
       time_3h       | location |        avg
---------------------+----------+--------------------
 2020-02-01 00:00:00 | nagoya   |  19.75833333333333
 2020-02-01 00:00:00 | osaka    |  20.46555555555556
 2020-02-01 00:00:00 | tokyo    | 19.315555555555555
 2020-02-01 03:00:00 | nagoya   | 19.328888888888887
 2020-02-01 03:00:00 | osaka    | 20.167777777777783
 2020-02-01 03:00:00 | tokyo    | 19.592777777777773
 2020-02-01 06:00:00 | nagoya   |  19.87055555555555
 2020-02-01 06:00:00 | osaka    |  20.07277777777777
 2020-02-01 06:00:00 | tokyo    |
 2020-02-01 09:00:00 | nagoya   | 19.240555555555563
 2020-02-01 09:00:00 | osaka    |             19.915
 2020-02-01 09:00:00 | tokyo    |
 2020-02-01 12:00:00 | nagoya   | 20.018888888888895
 2020-02-01 12:00:00 | osaka    |  19.66388888888888
 2020-02-01 12:00:00 | tokyo    | 19.437777777777782
 2020-02-01 15:00:00 | nagoya   | 19.904444444444444
 2020-02-01 15:00:00 | osaka    |  20.11388888888888
 2020-02-01 15:00:00 | tokyo    | 19.707222222222217
 2020-02-01 18:00:00 | nagoya   | 20.251111111111115
 2020-02-01 18:00:00 | osaka    | 20.023888888888884
 2020-02-01 18:00:00 | tokyo    | 20.571666666666676
 2020-02-01 21:00:00 | nagoya   |  19.91722222222222
 2020-02-01 21:00:00 | osaka    | 20.952777777777772
 2020-02-01 21:00:00 | tokyo    | 19.904444444444447
(24 行)
</pre>
<p><code>time_bucket_gapfill</code> 関数を使う場合には、どこからどこまでデータを補うかを指定するため、時間の範囲を <code>WHERE</code> 句か <code>start</code>、<code>finish</code> 引数で指定する必要があります。指定しないとエラーになります。</p>
<pre>(以下、例示のため、実行不要)
=# SELECT time_bucket_gapfill('3 hours', time) AS time_3h,
     location,
     avg(temperature)
   FROM conditions
   GROUP BY time_3h, location
   ORDER BY time_3h, location;
ERROR:  missing time_bucket_gapfill argument: could not infer start from WHERE clause
HINT:  Specify start and finish as arguments or in the WHERE clause.
</pre>
<p><code>NULL</code> の代わりに値を補うこともできます。それには、<code>locf</code> または <code>interpolate</code> 関数を使います。</p>
<p><code>locf</code> (last observation carried forward; 最後に観測された値を繰り越す) 関数を使うと、直前の値がそのまま補われます。ここでは、6 時、9 時には直前の 3 時と同じ 19.592度 (以下、小数点第 4 位以下切り捨て) が補われます。</p>
<pre>=# SELECT time_bucket_gapfill('3 hours', time) AS time_3h,
     location,
     locf(avg(temperature))
   FROM conditions
   WHERE time &gt;= '2020-02-01'
     AND time &lt; '2020-02-02'
   GROUP BY time_3h, location
   ORDER BY time_3h, location;
       time_3h       | location |        locf
---------------------+----------+--------------------
 2020-02-01 00:00:00 | nagoya   |  19.75833333333333
 2020-02-01 00:00:00 | osaka    |  20.46555555555556
 2020-02-01 00:00:00 | tokyo    | 19.315555555555555
 2020-02-01 03:00:00 | nagoya   | 19.328888888888887
 2020-02-01 03:00:00 | osaka    | 20.167777777777783
 2020-02-01 03:00:00 | tokyo    | 19.592777777777773
 2020-02-01 06:00:00 | nagoya   |  19.87055555555555
 2020-02-01 06:00:00 | osaka    |  20.07277777777777
 2020-02-01 06:00:00 | tokyo    | 19.592777777777773
 2020-02-01 09:00:00 | nagoya   | 19.240555555555563
 2020-02-01 09:00:00 | osaka    |             19.915
 2020-02-01 09:00:00 | tokyo    | 19.592777777777773
 2020-02-01 12:00:00 | nagoya   | 20.018888888888895
 2020-02-01 12:00:00 | osaka    |  19.66388888888888
 2020-02-01 12:00:00 | tokyo    | 19.437777777777782
 2020-02-01 15:00:00 | nagoya   | 19.904444444444444
 2020-02-01 15:00:00 | osaka    |  20.11388888888888
 2020-02-01 15:00:00 | tokyo    | 19.707222222222217
 2020-02-01 18:00:00 | nagoya   | 20.251111111111115
 2020-02-01 18:00:00 | osaka    | 20.023888888888884
 2020-02-01 18:00:00 | tokyo    | 20.571666666666676
 2020-02-01 21:00:00 | nagoya   |  19.91722222222222
 2020-02-01 21:00:00 | osaka    | 20.952777777777772
 2020-02-01 21:00:00 | tokyo    | 19.904444444444447
(24 行)
</pre>
<p><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2021/06/timescaledb-gapfill-locf.png" alt="locf 関数によるデータ補間" /></p>
<p><code>interpolate</code> 関数を使うと、直前と直後の値をもとに線形補間によって値が補われます。ここでは、直前の 3 時は 19.592 度、直後の 12 時は19.437 度で、6 時は線形補間によって19.592 + (19.437 &#8211; 19.592) / 3 = 19.541 度、9 時も同じように 19.592 + (19.437 &#8211; 19.592) × 2 / 3 = 19.489 度が補われます。</p>
<pre>=# SELECT time_bucket_gapfill('3 hours', time) AS time_3h,
     location,
     interpolate(avg(temperature))
   FROM conditions
   WHERE time &gt;= '2020-02-01'
     AND time &lt; '2020-02-02'
   GROUP BY time_3h, location
   ORDER BY time_3h, location;
       time_3h       | location |    interpolate
---------------------+----------+--------------------
 2020-02-01 00:00:00 | nagoya   |  19.75833333333333
 2020-02-01 00:00:00 | osaka    |  20.46555555555556
 2020-02-01 00:00:00 | tokyo    | 19.315555555555555
 2020-02-01 03:00:00 | nagoya   | 19.328888888888887
 2020-02-01 03:00:00 | osaka    | 20.167777777777783
 2020-02-01 03:00:00 | tokyo    | 19.592777777777773
 2020-02-01 06:00:00 | nagoya   |  19.87055555555555
 2020-02-01 06:00:00 | osaka    |  20.07277777777777
 2020-02-01 06:00:00 | tokyo    |  19.54111111111111
 2020-02-01 09:00:00 | nagoya   | 19.240555555555563
 2020-02-01 09:00:00 | osaka    |             19.915
 2020-02-01 09:00:00 | tokyo    | 19.489444444444445
 2020-02-01 12:00:00 | nagoya   | 20.018888888888895
 2020-02-01 12:00:00 | osaka    |  19.66388888888888
 2020-02-01 12:00:00 | tokyo    | 19.437777777777782
 2020-02-01 15:00:00 | nagoya   | 19.904444444444444
 2020-02-01 15:00:00 | osaka    |  20.11388888888888
 2020-02-01 15:00:00 | tokyo    | 19.707222222222217
 2020-02-01 18:00:00 | nagoya   | 20.251111111111115
 2020-02-01 18:00:00 | osaka    | 20.023888888888884
 2020-02-01 18:00:00 | tokyo    | 20.571666666666676
 2020-02-01 21:00:00 | nagoya   |  19.91722222222222
 2020-02-01 21:00:00 | osaka    | 20.952777777777772
 2020-02-01 21:00:00 | tokyo    | 19.904444444444447
(24 行)
</pre>
<p><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2021/06/timescaledb-gapfill-interpolate.png" alt="interpolate 関数によるデータ補間" /></p>
<h3><code>first</code>、<code>last</code> 関数</h3>
<p><code>first</code>、<code>last</code> 関数は別の列をキーに並び替え、最初、最後の値を取得する集約関数です。時間ごとに平均などでなく、素の値を抜き出すのに使います。並び替えにインデックスは使えないので、実行に時間がかかる場合があります。</p>
<p>例えば、2020 年 3 月 1 日、東京の温度のデータを 3 時間ごとに最初の値を取得してみます。</p>
<pre>=# SELECT time_bucket('3 hours', time) AS time_3h,
     first(temperature, time)
   FROM conditions
   WHERE time &gt;= '2020-03-01'
     AND time &lt; '2020-03-02'
     AND location = 'tokyo'
   GROUP BY time_3h
   ORDER BY time_3h;
       time_3h       | first
---------------------+-------
 2020-03-01 00:00:00 |  24.4
 2020-03-01 03:00:00 |  18.9
 2020-03-01 06:00:00 |  25.1
 2020-03-01 09:00:00 |  22.8
 2020-03-01 12:00:00 |  25.8
 2020-03-01 15:00:00 |  23.1
 2020-03-01 18:00:00 |  11.6
 2020-03-01 21:00:00 |  21.3
(8 行)
</pre>
<h3><code>histogram</code> 関数</h3>
<p><code>histogram</code> 関数はヒストグラムを取得する集約関数です。データの度数分布を確認するのに使います。</p>
<p>例えば、2020 年 3 月 1 日、東京の温度のデータを 3 時間ごとに 12.5 度から 27.5 度まで 2.5 度刻みで 6 つに区切ったヒストグラムを取得してみます。</p>
<pre>=# SELECT time_bucket('3 hours', time) AS time_3h,
     histogram(temperature, 12.5, 27.5, 5)
   FROM conditions
   WHERE time &gt;= '2020-03-01'
     AND time &lt; '2020-03-02'
     AND location = 'tokyo'
   GROUP BY time_3h
   ORDER BY time_3h;
       time_3h       |         histogram
---------------------+---------------------------
 2020-03-01 00:00:00 | {23,16,19,21,29,19,27,26}
 2020-03-01 03:00:00 | {21,17,21,25,25,21,29,21}
 2020-03-01 06:00:00 | {20,29,14,25,22,24,27,19}
 2020-03-01 09:00:00 | {17,24,19,27,18,28,28,19}
 2020-03-01 12:00:00 | {20,17,25,22,31,22,21,22}
 2020-03-01 15:00:00 | {28,23,17,29,25,18,18,22}
 2020-03-01 18:00:00 | {23,14,27,18,30,22,25,21}
 2020-03-01 21:00:00 | {20,20,20,32,19,27,25,17}
(8 行)
</pre>
<p>ヒストグラムは波カッコ <code>{}</code> で囲まれ、カンマ <code>,</code> で区切られた配列として出力されます。配列の各要素が区間内の度数を表します。ここでは、最初の要素が 12.5 度未満、次が 12.5 度以上、15 度未満、その次が 15 度以上、17.5 度未満と続いていき、最後が 27.5 超過のデータの度数になります。</p>
<h3><code>approximate_row_count</code> 関数</h3>
<p><code>approximate_row_count</code> 関数は統計情報を使ってテーブルのおおよその行数を取得する関数です。結果が正確でない代わりに、<code>count</code> 関数に比べて高速に動作します。</p>
<p><code>\timing</code> コマンドで実行時間を比べると、<code>approximate_row_count</code> 関数が高速なのが分かります。</p>
<pre>=# \timing
タイミングは on です。
=# SELECT count(*) FROM conditions;
  count
---------
 1455480
(1 行)

時間: 246.751 ミリ秒
=# SELECT approximate_row_count('conditions');
 approximate_row_count
-----------------------
               1455840
(1 行)

時間: 4.144 ミリ秒
</pre>
<h2>おわりに</h2>
<p>今回は、TimescaleDB の分析関数として、時間を任意の間隔で丸める <code>time_bucket</code> 関数や、別の列をキーに並び替え、最初、最後の値を取得する <code>first</code>、<code>last</code> 関数を取り上げ、関数の概要や使い方について紹介しました。関数については <a href="https://docs.timescale.com/timescaledb/latest/">TimescaleDB Documentation</a> の <a href="https://docs.timescale.com/api/latest/">API Reference</a> もあわせて参照してください。</p>
<p>次回は、時系列データの自動集計や削除、圧縮の機能について紹介する予定です。</p>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>TimescaleDB の紹介</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/timescaledb-intro/</link>
		
		<dc:creator><![CDATA[佐藤 友章 (Tomoaki Sato)]]></dc:creator>
		<pubDate>Mon, 24 May 2021 04:10:16 +0000</pubDate>
				<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[TimescaleDB]]></category>
		<category><![CDATA[サードパーティーソフトウェア]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=6973</guid>

					<description><![CDATA[TimescaleDB は、IoT 機器のセンサーやスマートメーターが生成するデータのように、時系列に沿って発生するデータを扱うのに適した時系列データベースの機能を、リレーショナルデータベース PostgreSQL に追 ...]]></description>
										<content:encoded><![CDATA[<p>TimescaleDB は、IoT 機器のセンサーやスマートメーターが生成するデータのように、時系列に沿って発生するデータを扱うのに適した時系列データベースの機能を、リレーショナルデータベース PostgreSQL に追加する拡張モジュールです。</p>
<p>この記事では、TimescaleDB の概要とインストール、基本的な使い方について紹介します。</p>
<p><span id="more-6973"></span></p>
<h2>TimescaleDB とは</h2>
<p>TimescaleDB は米国 <a href="https://www.timescale.com/">Timescale 社</a> が開発する時系列データベースで、ソースコードは <a href="https://github.com/timescale/timescaledb">GitHub リポジトリ</a> で公開されています。ライセンスは一部が <a href="https://github.com/timescale/timescaledb/blob/master/tsl/LICENSE-TIMESCALE">Timescale ライセンス (TSL)</a> で、それ以外は <a href="https://github.com/timescale/timescaledb/blob/master/LICENSE-APACHE">Apache ライセンス 2.0</a> です。TSL 部分は簡単に言えばそれ自体をサービスとして提供しない限り自由に使えます。ライセンスについて正確には自分で確認してください。</p>
<p>TimescaleDB は PostgreSQL の拡張モジュールとして実装されていて、リレーショナルデータベースと NoSQL の特長をあわせもちます。</p>
<ul>
<li>使いやすさ &#8211; SQL に完全対応をしていて、PostgreSQL 用のアプリケーションもそのまま接続でき、時系列データ向けの分析や管理機能も備えています。</li>
<li>スケーラビリティの高さ &#8211; データは適切なサイズに分割して格納され、データ量が増えても安定して高速に書き込みでき、並列処理にも対応しています。</li>
<li>信頼性の高さ &#8211; 長い歴史をもつ PostgreSQL 上で動作し、レプリケーションやバックアップなど、PostgreSQL の機能はもちろん、PostgreSQL 用のツールも使えます。</li>
</ul>
<p>TimescaleDB では、データは内部的に時間と空間で分割されたチャンクと呼ばれるテーブルに格納され、ハイパーテーブルを介して透過的にアクセスされます。空間は時間以外にチャンクを分割する条件です。分割方式として <a href="https://www.postgresql.jp/document/current/html/ddl-partitioning.html#DDL-PARTITIONING-IMPLEMENTATION-INHERITANCE">継承によるパーティショニング</a> を採用していて、複数の条件で分割しても階層が多段にならない実装になっています。</p>
<p><img decoding="async" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2021/05/timescaledb-architecture.png" alt="TimescaleDB のアークテクチャ" /></p>
<h2>TimescaleDB のインストール</h2>
<p>TimescaleDB は各プラットフォーム向けのバイナリに加え、Docker イメージや AMI、マネージドサービス Timescale Cloud も提供されています。すべてのインストール方法については <a href="https://docs.timescale.com/latest/main">TimescaleDB Documentation</a> の <a href="https://docs.timescale.com/latest/getting-started/installation">Installation</a> を参照してください。</p>
<p>TimescaleDB は 2021 年 6 月時点で以下のバージョンがメンテナンスされていて、それぞれ対応する PostgreSQL のバージョンが異なります。</p>
<table>
<thead>
<tr>
<th>TimescaleDB バージョン</th>
<th>対応 PostgreSQL バージョン</th>
</tr>
</thead>
<tbody>
<tr>
<td>2.4以降</td>
<td>12、13</td>
</tr>
<tr>
<td>2.1～2.3</td>
<td>12、13</td>
</tr>
<tr>
<td>2.0</td>
<td>11、12</td>
</tr>
<tr>
<td>1.7</td>
<td>9.6、10、11、12</td>
</tr>
</tbody>
</table>
<p>ここでは、CentOS 8 上の PostgreSQL 13 に Yum を使って TimescaleDB をインストールしていきます。</p>
<ol>
<li>TimescaleDB の Yum リポジトリを登録します。
<pre>$ sudo tee /etc/yum.repos.d/timescale_timescaledb.repo &lt;&lt;EOF
[timescale_timescaledb]
name=timescale_timescaledb
baseurl=https://packagecloud.io/timescale/timescaledb/el/\$releasever/\$basearch
repo_gpgcheck=1
gpgcheck=0
enabled=1
gpgkey=https://packagecloud.io/timescale/timescaledb/gpgkey
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
metadata_expire=300
EOF
</pre>
</li>
<li>TimescaleDB の RPM パッケージをインストールします。
<pre>$ sudo yum -y install timescaledb-2-postgresql-13
(省略)
Installed:
  timescaledb-2-postgresql-13.x86_64 0:2.2.0-0.el8

Dependency Installed:
  timescaledb-2-loader-postgresql-13.x86_64 0:2.2.0-0.el8
  timescaledb-tools.x86_64 0:0.11.0-0.el8

Complete!
</pre>
</li>
<li>TimescaleDB 向けに PostgreSQL の設定を行います。
<p>設定ファイル <code>postgresql.conf</code> を直接編集してもよいですが、ここでは、メモリ量や CPU 数をもとに対話的に設定を行う <code>timescaledb-tune</code> コマンドを使います。</p>
<pre>$ sudo timescaledb-tune --pg-config=/usr/pgsql-13/bin/pg_config
Using postgresql.conf at this path:
/var/lib/pgsql/13/data/postgresql.conf

Is this correct? [(y)es/(n)o]: y
Writing backup to:
/tmp/timescaledb_tune.backup202105010056

shared_preload_libraries needs to be updated
Current:
#shared_preload_libraries = ''
Recommended:
shared_preload_libraries = 'timescaledb'
Is this okay? [(y)es/(n)o]: y
success: shared_preload_libraries will be updated

Tune memory/parallelism/WAL and other settings? [(y)es/(n)o]: y
Recommendations based on 465.21 MB of available memory and 1 CPUs for PostgreSQL 13

Memory settings recommendations
Current:
shared_buffers = 128MB
#effective_cache_size = 4GB
#maintenance_work_mem = 64MB
#work_mem = 4MB
Recommended:
shared_buffers = 119094kB
effective_cache_size = 357282kB
maintenance_work_mem = 59547kB
work_mem = 2977kB
Is this okay? [(y)es/(s)kip/(q)uit]: y
success: memory settings will be updated

WAL settings recommendations
Current:
#wal_buffers = -1
min_wal_size = 80MB
Recommended:
wal_buffers = 3572kB
min_wal_size = 512MB
Is this okay? [(y)es/(s)kip/(q)uit]: y
success: WAL settings will be updated

Miscellaneous settings recommendations
Current:
#default_statistics_target = 100
#random_page_cost = 4.0
#checkpoint_completion_target = 0.5
max_connections = 100
#max_locks_per_transaction = 64
#autovacuum_max_workers = 3
#autovacuum_naptime = 1min
#effective_io_concurrency = 1
Recommended:
default_statistics_target = 500
random_page_cost = 1.1
checkpoint_completion_target = 0.9
max_connections = 20
max_locks_per_transaction = 64
autovacuum_max_workers = 10
autovacuum_naptime = 10
effective_io_concurrency = 200
Is this okay? [(y)es/(s)kip/(q)uit]: y
success: miscellaneous settings will be updated
Saving changes to: /var/lib/pgsql/13/data/postgresql.conf
</pre>
<p><code>--pg-config</code> オプションには <code>pg_config</code> コマンドのパスを指定します。そのほかのオプションは <code>--help</code> オプションで確認できます。実行を開始すると、次々に聞かれる質問に答えていきます。基本的には <code>y</code> を入力していけばよいです。最後に設定がファイルに書き込まれます。</li>
<li>設定を反映するため、PostgreSQL のサービスを再起動します。
<pre>$ sudo systemctl restart postgresql-13
</pre>
</li>
<li>TimescaleDB の拡張モジュールをデータベースにインストールします。
<p>ここでは、PostgreSQL のスーパーユーザ <code>postgres</code> に切り替わり、テスト用に <code>test</code> データベースを作成し、そこにインストールします。</p>
<pre>$ sudo su - postgres
$ createdb test
$ psql -c "CREATE EXTENSION timescaledb" test
WARNING:
WELCOME TO
 _____ _                               _     ____________
|_   _(_)                             | |    |  _  \ ___ \
  | |  _ _ __ ___   ___  ___  ___ __ _| | ___| | | | |_/ /
  | | | |  _ ` _ \ / _ \/ __|/ __/ _` | |/ _ \ | | | ___ \
  | | | | | | | | |  __/\__ \ (_| (_| | |  __/ |/ /| |_/ /
  |_| |_|_| |_| |_|\___||___/\___\__,_|_|\___|___/ \____/
               Running version 2.2.0
For more information on TimescaleDB, please visit the following links:

 1. Getting started: https://docs.timescale.com/getting-started
 2. API reference documentation: https://docs.timescale.com/api
 3. How TimescaleDB is designed: https://docs.timescale.com/introduction/architecture

Note: TimescaleDB collects anonymous reports to better understand and assist our users.
For more information and how to disable, please see our docs https://docs.timescaledb.com/using-timescaledb/telemetry.

CREATE EXTENSION
</pre>
</li>
</ol>
<h2>TimescaleDB の基本的な使い方</h2>
<p>ここからは、<code>test</code> データベースに接続し、TimescaleDB の基本的な使い方を確認していきます。</p>
<pre>$ psql test
psql (13.2)
"help"でヘルプを表示します。

=#
</pre>
<h3>ハイパーテーブルの作成</h3>
<p>ハイパーテーブルを作成するには通常のテーブルを作成し、<code>create_hypertable</code> 関数を実行します。ここでは、時間と場所ごとに温度と湿度を格納する <code>conditions</code> テーブルを作成し、ハイパーテーブルに変換します。</p>
<pre>=# CREATE TABLE conditions (
     time timestamp NOT NULL,
     location text NOT NULL,
     temperature double precision,
     humidity double precision
   );
CREATE TABLE
=# SELECT create_hypertable('conditions', 'time');
    create_hypertable
-------------------------
 (1,public,conditions,t)
(1 行)
</pre>
<p><code>create_hypertable</code> 関数の引数にはハイパーテーブルに変換するテーブル名、分割の条件にする時間の列名を指定します。戻り値はそれぞれハイパーテーブルの ID、スキーマ名、テーブル名、実際に作成されたかが返されます。</p>
<p>チャンクはデフォルトで 7 日ごとに分割されます。時間列のデータ型はタイムスタンプや日付以外に整数にも対応しています。整数の場合には値が表す時間の大きさが分からないため、チャンクを分割する間隔を <code>chunk_time_interval</code> 引数で指定する必要があります。例えば、時間列が秒を表し、7 日ごとに分割する場合には、7 日 × 24 時間 × 60 分 × 60 秒 = 604,800 を指定します。</p>
<pre>(以下、例示のため、実行不要)
=# SELECT create_hypertable('conditions', 'time');
ERROR:  integer dimensions require an explicit interval
=# SELECT create_hypertable('conditions', 'time', chunk_time_interval := 604800);
    create_hypertable
-------------------------
 (1,public,conditions,t)
(1 行)
</pre>
<p>変換元のテーブルは空でないとエラーになります。<code>migrate_data</code> 引数に <code>true</code> を指定すれば、データを移行することもできます。データ量が多いと時間がかかり、外部キー制約があるとデッドロックが発生する可能性があります。通常はハイパーテーブルの作成後にデータを投入したほうがよいでしょう。</p>
<pre>(以下、例示のため、実行不要)
=# SELECT create_hypertable('conditions', 'time');
ERROR:  table "conditions" is not empty
HINT:  You can migrate data by specifying 'migrate_data =&gt; true' when calling this function.
=# SELECT create_hypertable('conditions', 'time', migrate_data := true);
NOTICE:  migrating data to chunks
DETAIL:  Migration might take a while depending on the amount of data.
    create_hypertable
-------------------------
 (1,public,conditions,t)
(1 行)
</pre>
<p><code>create_hypertable</code> 関数について詳しくは TimescaleDB Documentation の <a href="https://docs.timescale.com/latest/api#create_hypertable">API Reference</a> を参照してください。</p>
<h3>時系列データの操作</h3>
<p>時系列データの操作はハイパーテーブルを介して通常のデータと同じようにできます。ここでは、まず、データの挿入、検索、更新、削除をしてみます。</p>
<pre>=# INSERT INTO conditions
     VALUES ('2021-05-01 09:00:00', 'tokyo', 19.4, 62.0);
INSERT 0 1
=# SELECT * FROM conditions;
        time         | location | temperature | humidity
---------------------+----------+-------------+----------
 2021-05-01 09:00:00 | tokyo    |        19.4 |       62
(1 行)

=# UPDATE conditions
   SET temperature = temperature + 0.1
   WHERE time = '2021-05-01 09:00:00'
     AND location = 'tokyo';
UPDATE 1
=# DELETE FROM conditions
   WHERE time = '2021-05-01 09:00:00'
     AND location = 'tokyo';
DELETE 1
</pre>
<p>次に、あとで分析に使うデータを生成します。データは 2020 年 1 月 1 日から 1 分間隔で 1 年間、場所は東京、名古屋、大阪の 3 か所、温度は 10 〜 30 度、湿度は 60 〜 90% の範囲でランダムです。これで 55 個のチャンクからなる 1,581,120 行、135MB のデータになります。データの投入後は <code>ANALYZE</code> コマンドで統計情報を更新しておきましょう。</p>
<pre>=# INSERT INTO conditions
   SELECT time,
     location,
     round(10 + random()::numeric * (30 - 10), 1) AS temperature,
     round(60 + random()::numeric * (90 - 60), 0) AS humidity
   FROM generate_series('2020-01-01 00:00:00'::timestamp, '2020-12-31 23:59:00', '1 minute') AS time,
     unnest(ARRAY['tokyo', 'nagoya', 'osaka']) AS location;
INSERT 0 1581120
=# ANALYZE;
ANALYZE
</pre>
<h3>チャンクの削除</h3>
<p>データの削除は <code>DELETE</code> コマンドでもできますが、それだと不要領域が発生し、回収して再利用可能にするにはバキュームが必要です。それに対して、チャンクごと削除すれば、不要領域が発生せずに済みます。</p>
<p>チャンクを削除するには <code>drop_chunks</code>関数を実行します。ここでは、2021 年 2 月 1 より前のチャンクを削除します。</p>
<pre>=# SELECT drop_chunks('conditions', '2020-02-01 00:00:00'::timestamp);
              drop_chunks
----------------------------------------
 _timescaledb_internal._hyper_1_2_chunk
 _timescaledb_internal._hyper_1_3_chunk
 _timescaledb_internal._hyper_1_4_chunk
 _timescaledb_internal._hyper_1_5_chunk
 _timescaledb_internal._hyper_1_6_chunk
(5 行)
</pre>
<p><code>drop_chunks</code> 関数の引数にはハイパーテーブルの名前、いつより前のデータしか含まないチャンクを削除するかのタイムスタンプを指定します。戻り値は削除されたチャンクの名前が返されます。</p>
<p>いつより前を削除するかはタイムスタンプや日付、整数以外に間隔にも対応しています。間隔の場合には現在を基準にその間隔より前が削除されます。例えば、現在が 2021 年 5 月 1 日で、間隔が 15 か月の場合には、2020 年 2 月 1 日より前のチャンクが削除されます。</p>
<pre>(以下、例示のため、実行不要)
=# SELECT drop_chunks('conditions', '15 months'::interval);
              drop_chunks
----------------------------------------
 _timescaledb_internal._hyper_1_2_chunk
 _timescaledb_internal._hyper_1_3_chunk
 _timescaledb_internal._hyper_1_4_chunk
 _timescaledb_internal._hyper_1_5_chunk
 _timescaledb_internal._hyper_1_6_chunk
(5 行)
</pre>
<p><code>drop_chunks</code> 関数について詳しくは TimescaleDB Documentation の <a href="https://docs.timescale.com/latest/api#drop_chunks">API Reference</a> を参照してください。</p>
<h2>おわりに</h2>
<p>今回は、リレーショナルデータベースと NoSQL の特長をあわせもった時系列データベース TimescaleDB の概要、CentOS 8 上の PostgreSQL 13 への Yum を使ったインストール、ハイパーテーブルの作成や時系列データの操作など、基本的な使い方について紹介しました。</p>
<p>次回は、TimescaleDB の備える時系列データ向けの分析機能について紹介します。</p>
<div>
<a href="https://www.sraoss.co.jp/prod_serv/support/pgsql-mainte/?utm_source=sraoss-techblog&utm_medium=referral&utm_campaign=under-article-pgsql" rel="noopener" style="color:initial;" target="_blank" class="no-ext"></p>
<div style="background-color:#DAEFF6; padding:20px; border-radius:8px; text-align:center;">
SRA OSSでは、PostgreSQL/Pgpool-II のサポートを提供しております。<br />
詳しくは<strong style="color:#2581c4;">弊社ホームページ</strong>をご覧ください。
</div>
<p></a>
</div>

]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
