<?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>IVM &#8211; SRA OSS Tech Blog</title>
	<atom:link href="https://www.sraoss.co.jp/tech-blog/tag/ivm/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>Mon, 09 Nov 2026 03:38:53 +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>IVM &#8211; SRA OSS Tech Blog</title>
	<link>https://www.sraoss.co.jp/tech-blog/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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>PostgreSQL におけるマテリアライズドビューの高速更新技術(Incremental View Maintenance)の提案</title>
		<link>https://www.sraoss.co.jp/tech-blog/pgsql/ivm_poc_with_oids/</link>
		
		<dc:creator><![CDATA[長田 悠吾 (Yugo Nagata)]]></dc:creator>
		<pubDate>Fri, 11 Jan 2019 04:00:43 +0000</pubDate>
				<category><![CDATA[OSS Development]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[IVM]]></category>
		<category><![CDATA[PoC]]></category>
		<guid isPermaLink="false">https://www.sraoss.co.jp/tech-blog/?p=1247</guid>

					<description><![CDATA[SRA OSS は PostgreSQL 関連サービスを提供しているだけではなく、PostgreSQL の開発にも参加しています。過去には再帰SQLの実装や、ラージオブジェクトの64bit化などを行ってきました。 そして ...]]></description>
										<content:encoded><![CDATA[<p>SRA OSS は PostgreSQL 関連サービスを提供しているだけではなく、PostgreSQL の開発にも参加しています。過去には再帰SQLの実装や、ラージオブジェクトの64bit化などを行ってきました。</p>
<p>そして、最近は <strong>Incremental View Maintenance</strong> という機能の実装を提案することを検討しています（2018年12月末にPostgreSQL開発MLにて議論開始）。これはマテリアライズドビューの更新を高速に行うための技術です。PostgreSQLが大規模システムの採用が増加する中、マテリアライズドビューの利用は増えていると考えており、またその高速リフレッシュは喫緊の課題であると考えています。</p>
<p><a href="https://www.postgresql.eu/events/pgconfeu2018/schedule/session/2195-implementing-incremental-view-maintenance-on-postgresql/">これに関する発表</a>を昨年の10月にポルトガルのリスボンで開催された <a href="https://2018.pgconf.eu/">PostgreSQL Conference Europe (PGConf.EU) 2018</a> で発表してきました（レポートは<a href="https://www.sraoss.co.jp/event_seminar/2018/pgcon_europe_2018.php">こちら</a>）。本記事では、その内容について解説します。</p>
<p><span id="more-1247"></span></p>
<h2>ビュー、マテリアライズドビュー、そして IVM</h2>
<p>通常のビューは、それを定義する SELECT クエリのみを持っていて、ビューに対する問い合わせが発生したときにはこのクエリが実行され、その結果がクライアントに返ります。<a href="https://www.postgresql.jp/document/10/html/rules-materializedviews.html">マテリアライズドビュー</a>では通常のビューと異なり、ビューを定義するときに問い合わせの結果がデータとして保存されます。これにより、マテリアライズドビューへの問い合わせが発生した時には、保存されている結果を使用することで高速な応答が可能です。PostgreSQL では 9.3 からマテリアライズドビューをサポートしています。</p>
<p>ただし、ビューの定義に含まれる実テーブルが更新された場合には、マテリアライズドビューに保存されている内容は古くなってしまいます。そのため、これを最新の状態に更新する必要が出てきます。これはビューのメンテナンスと呼ばれています。ビューをメンテンスする最も単純な方法は、マテリアライズドビューの内容を全て「再計算」することです。PostgreSQL にはこれを行う <a href="https://www.postgresql.jp/document/10/html/sql-refreshmaterializedview.html">REFRESH MATERIALIZED VIEW</a> という SQL コマンドが用意されており、これを実行するとビューを定義する SELECT クエリが再度実行され、その新しい結果でマテリアライズドビューの中にある古いデータが置き換えられます。</p>
<p>しかし大抵の場合、実テーブルの中で更新される行は、テーブル全体のごく一部だけのことがほとんどです。そのわずかな変更のために、マテリアライズドビューを一から作り直すのは出来れば避けたい所です。そこで、実テーブルに発生した更新差分を元に、マテリアライズドビューに発生する差分のみを計算して、これを適用することでマテリアライズドビューをメンテナンスすることを考えます。これが、Incremental View Maintenance（以下、IVM と呼びます）という考え方です。</p>
<figure id="attachment_1251" aria-describedby="caption-attachment-1251" style="width: 867px" class="wp-caption alignright"><img fetchpriority="high" decoding="async" class="wp-image-1251 size-full" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/bc5852c6ed9d2f287e5edbe8953f6df9.png" alt="" width="867" height="391" srcset="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/bc5852c6ed9d2f287e5edbe8953f6df9.png 867w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/bc5852c6ed9d2f287e5edbe8953f6df9-300x135.png 300w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/bc5852c6ed9d2f287e5edbe8953f6df9-768x346.png 768w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/bc5852c6ed9d2f287e5edbe8953f6df9-304x137.png 304w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/bc5852c6ed9d2f287e5edbe8953f6df9-282x127.png 282w" sizes="(max-width: 867px) 100vw, 867px" /><figcaption id="caption-attachment-1251" class="wp-caption-text">Incremental View Maintenance（IVM） の概念図：通常のリフレッシュではビュー定義と更新後の実テーブルからマテリアライズドビューを作り直す（緑のルート）が、IVM では実テーブルに発生した差分からマテリアライズドビューの差分を計算し、これを適用する（赤のルート）。</figcaption></figure>
<h2>OID を使用した IVM の実装</h2>
<p>IVM はマテリアライズドビューを使用しているユーザにとって非常に有用な機能ですが、今の PostgreSQL ではサポートされていません。そこで、我々はこの機能の実装にチャレンジすることにしました。まずは、一緒に共同研究をしているお茶の水女子大学名誉教授の増永良文先生のアイデアを元に、テーブルの行 OID を使用した PoC（Proof of Concept, 概念実証: アイデアの実現可能性を評価するための試作）の実装を行いました。</p>
<h3>基本的なアイデア</h3>
<figure id="attachment_1248" aria-describedby="caption-attachment-1248" style="width: 862px" class="wp-caption alignnone"><img decoding="async" class="wp-image-1248 size-full" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/a0e654b800ea1714f30260761ed7c9df.png" alt="" width="862" height="428" srcset="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/a0e654b800ea1714f30260761ed7c9df.png 862w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/a0e654b800ea1714f30260761ed7c9df-300x149.png 300w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/a0e654b800ea1714f30260761ed7c9df-768x381.png 768w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/a0e654b800ea1714f30260761ed7c9df-304x151.png 304w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/a0e654b800ea1714f30260761ed7c9df-282x140.png 282w" sizes="(max-width: 862px) 100vw, 862px" /><figcaption id="caption-attachment-1248" class="wp-caption-text">OIDを利用したIVM実装の基本的アイデア：ビューとテーブルの行OIDの対応付け（OID map）を持っていれば、実テーブルの行の更新がビューのどの行に影響を与えるかを知ることができる。</figcaption></figure>
<p>基本的なアイデアは以下の通りです。</p>
<p>この図は実テーブル devices と parts の JOIN 結果のマテリアライズドビュー V を表しています。それぞれの実テーブルとビューの行には、OID が付与されており、実テーブルとビューの OID の対応関係は OID map に保存されています。例えば、ビューの中で OID が 301 の行は、OID が 101 および 201 の行から生成されています。ここで、parts テーブルの OID が 201 の行が変更されたとします。この場合、 OID map を参照することで ビューの OID が 301 の行のみが影響を受けるのがわかります。つまり、再計算が必要なのはこの行だけで、他の行はそのまま残しておくことができるというわけです。</p>
<h3>全体像</h3>
<p>次の図は今回行った PoC 実装で行われる処理の全体像です。処理されるタイミングによって、大きく３つの部分に分けられています。</p>
<figure id="attachment_1250" aria-describedby="caption-attachment-1250" style="width: 854px" class="wp-caption alignright"><img decoding="async" class="wp-image-1250 size-full" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/d9775456c4b910a23e9ffeb5f40c1f09.png" alt="" width="854" height="496" srcset="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/d9775456c4b910a23e9ffeb5f40c1f09.png 854w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/d9775456c4b910a23e9ffeb5f40c1f09-300x174.png 300w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/d9775456c4b910a23e9ffeb5f40c1f09-768x446.png 768w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/d9775456c4b910a23e9ffeb5f40c1f09-304x177.png 304w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/d9775456c4b910a23e9ffeb5f40c1f09-282x164.png 282w" sizes="(max-width: 854px) 100vw, 854px" /><figcaption id="caption-attachment-1250" class="wp-caption-text">今回のPoC実装の全体像：ビュー定義時、テーブル更新時、ビューメンテナンス時の３つの処理に分けることができる。</figcaption></figure>
<p>１つは「ビューを定義」するときに行われる処理で、図ではオレンジの矢印で表されています。このタイミングでは、ビューを定義するクエリが実行されたマテリアライズドビューに格納されるデータが計算されるだけではなく、ビュー定義に含まれる実テーブルとビューの間の行 OID の関連付け（OID map の作成）や、実テーブル毎に「差分テーブル」と AFTER トリガの生成、および、マテリアライズドビューを更新するときに実行すべき「クエリスクリプト」の生成が行われます。</p>
<p>２つ目は「実テーブルが更新」された時の処理で、図では緑の矢印で表されています。この時、実テーブルに発生した差分が、それぞれの「差分テーブル」に格納されます。</p>
<p>最後は実際に「ビューのメンテナンス」、すなわち、マテリアライズドビューの更新を行うときの処理で、青の矢印で表されています。この時点で、ビュー定義時に生成しておいた「クエリスクリプト」が実行され、マテリアライズドビューの内容が最新に更新されます。</p>
<h3>前提条件</h3>
<p>この実装方法は 行 OID を使用するので、マテリアライズドビューの定義に含まれる実テーブルは、<a href="https://www.postgresql.jp/document/10/html/sql-createtable.html">CREATE TABLE</a> で定義されるときに WITH OIDS を使用するか、<a href="https://www.postgresql.jp/document/10/html/sql-altertable.html">ALTER TABLE</a> で WITH OIDS オプションを追加する必要があります。同様にマテリアライズドビューにも行 OID が保持しておく必要があるのですが、PostgreSQL の <a href="https://www.postgresql.jp/document/10/html/sql-creatematerializedview.html">CREATE MATERIALIZED VIEW</a> では WITH OIDS の使用はサポートされていません。そこで、今回の PoC 実装ではこの構文を改造して、WITH OIDS 付きのマテリアライズドビューを作成できるようにしています。</p>
<h2>PoC 実装の詳細</h2>
<h3>ビューの定義時</h3>
<p>マテリアライズドビューを定義する時に、ビューと実テーブルの行 OID の対応表が作成されます。ビューを定義する SELECT 文を実行している最中に、ビューのある１行を生成するために使用された実テーブルの行の OID が収集されます。そして、結果の行がマテリアライズドビューに挿入される時に、ビューの行 OID も取得します。その関連は pg_ivm_oidmap という名前のテーブルに保存されます。次の図はその例です。例えば、マテリアリズドビューの OID が 301 の行が、devices テーブルの OID が 101 の行と parts テーブルの OID が 201 の行から生成されたことなどが記録されています。</p>
<figure id="attachment_1252" aria-describedby="caption-attachment-1252" style="width: 885px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-1252 size-full" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/66eb52690992c3e4e2a2ef3e62d8bb4e.png" alt="" width="885" height="497" srcset="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/66eb52690992c3e4e2a2ef3e62d8bb4e.png 885w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/66eb52690992c3e4e2a2ef3e62d8bb4e-300x168.png 300w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/66eb52690992c3e4e2a2ef3e62d8bb4e-768x431.png 768w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/66eb52690992c3e4e2a2ef3e62d8bb4e-304x171.png 304w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/66eb52690992c3e4e2a2ef3e62d8bb4e-282x158.png 282w" sizes="auto, (max-width: 885px) 100vw, 885px" /><figcaption id="caption-attachment-1252" class="wp-caption-text">OIDマップ生成の例：devicesテーブルとpartsテーブルのJOINを行うビューの場合</figcaption></figure>
<p>また、この時にはビューを定義する SELECT クエリが解析され、そこに含まれる実テーブルが抽出されます。各実テーブルに対して、その差分を格納するための「差分テーブル」が作成されます。実テーブル１つごとに old と new の２つの差分テーブル作成され、その名前は pg_ivm_&lt;実テーブルのOID&gt;_old（または _new）となります。例えば、OID が 1111 の実テーブルの OLD 差分を格納するテーブルの名前は pg_ivm_1111_old です。さらに、各実テーブル上には AFTER トリガが作成されます。</p>
<p>その他、マテリアライズドビューを更新するときに実行すべき「クエリスクリプト」が生成され、これは pg_ivm_script というテーブルに保存されます。</p>
<h3>実テーブルの更新時</h3>
<figure id="attachment_1253" aria-describedby="caption-attachment-1253" style="width: 774px" class="wp-caption alignright"><img loading="lazy" decoding="async" class="wp-image-1253 size-full" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/97708ef6e9cc67bf1a9cb3ae7940b50a.png" alt="" width="774" height="237" srcset="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/97708ef6e9cc67bf1a9cb3ae7940b50a.png 774w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/97708ef6e9cc67bf1a9cb3ae7940b50a-300x92.png 300w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/97708ef6e9cc67bf1a9cb3ae7940b50a-768x235.png 768w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/97708ef6e9cc67bf1a9cb3ae7940b50a-304x93.png 304w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/97708ef6e9cc67bf1a9cb3ae7940b50a-282x86.png 282w" sizes="auto, (max-width: 774px) 100vw, 774px" /><figcaption id="caption-attachment-1253" class="wp-caption-text">差分テーブルの例：partsテーブルのpriceが10から15に変更された場合</figcaption></figure>
<p>実テーブルが更新された時には、そのテーブルに定義された AFTER トリガが発動します。このトリガの中では、OLD 差分と NEW 差分が取得され、それぞれ対応する pg_ivm_&#8230;_old テーブルと、pg_ivm_&#8230;_new テーブルに格納されます。OLD 差分には、テーブルから削除された行、および、更新によって置き換えられた行の古い内容が含まれます。また、NEW 差分には、テーブルに新しく挿入された行、および、更新によって置き換えられた行の新しい内容が含まれます。例えば、parts テーブル（OID は 2222）の price というカラムの値が 10 から 15 に更新された場合、price の値が 10 である古い行の内容は pg_ivm_2222_old に、price の値が 15 である新しい行の内容は pg_ivm_2222_new に格納されます。このとき、対応する行の OID も一緒に格納されます。なお、この機能は AFTER トリガの「遷移テーブル」の機能を使って実装しました。</p>
<p>同じ行が複数回更新された場合には、NEW 差分の内容を新しく更新することで対応しています。例えば、上記の例に続いて、parts テーブルの price カラムの値が 15 から 25 に更新された場合には、pg_ivm_111_new から price が 15 の行を削除し、代わりに price が 25 の行を挿入することで、古い更新差分の情報を上書きしています。</p>
<h3>ビューのメンテナンス時</h3>
<p>今回の実装では、ビューのメンテナンス、すなわちマテリアライズドビューの更新は、以下の SQL コマンドが実行された時に行うようにしました。</p>
<pre>REFRESH MATERIALIZED VIEW <strong>INCREMENTALLY</strong> [view name];
</pre>
<p>この INCREMENTALLY というキーワードは今回の PoC 実装で行った拡張です。これが指定されているときには IVM すなわち、マテリアライズドビューの差分更新を行い、これが指定されなかった場合には通常のリフレッシュ、つまりマテリアライズドビューの作り直しを行います。</p>
<p>IVM が実行された場合には、pg_ivm_script テーブル内に保存されている「クエリスクリプト」が実行されます。具体的には、まずマテリアライズドビューから「古くなった行」を削除します。例えば、pg_ivm_2222_old テーブルに OID が 201 の行が含まれている場合、pg_ivm_oidmap に保存されている OID マップを参照することで、OID 301 の行をマテリアライズドビューから削除すべきことがわかります。</p>
<p>次に、マテリアライズドビューに新しい行を挿入します。これは、JOIN を行うビューの場合、実テーブルと実テーブルの NEW 差分テーブルの JOIN を実行することで求めることができます。例えば、devices テーブルと parts テーブルを JOIN するビューの場合、devices テーブルそのものと、parts テーブルの NEW 差分テーブルである pg_ivm_2222_new の JOIN によって生成された行がマテリアライズドビューに新しく挿入すべき行となります。</p>
<figure id="attachment_1256" aria-describedby="caption-attachment-1256" style="width: 884px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-1256 size-full" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/f91e9e030bc90b18f59895767b839138.png" alt="" width="884" height="355" srcset="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/f91e9e030bc90b18f59895767b839138.png 884w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/f91e9e030bc90b18f59895767b839138-300x120.png 300w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/f91e9e030bc90b18f59895767b839138-768x308.png 768w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/f91e9e030bc90b18f59895767b839138-304x122.png 304w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/f91e9e030bc90b18f59895767b839138-282x113.png 282w" sizes="auto, (max-width: 884px) 100vw, 884px" /><figcaption id="caption-attachment-1256" class="wp-caption-text">マテリアライズドビューに新しく挿入すべき行の計算の例。devicesテーブルとpartsテーブルのNEW差分のJOINを行う。</figcaption></figure>
<h2>動作例</h2>
<p>今回の PoC 実装の動作確認には <a href="https://www.postgresql.jp/document/10/html/pgbench.html">pgbench</a> で使用される２つのテーブル pgbench_acounts と pgbench_tellers を JOIN するマテリアリズドビューを用いました。</p>
<p>IVM を使用するマテリアライズドビュー mv_ivm は以下のように、WITH OIDS を使用して作成しました。２つのテーブルは bid カラムで結合し、WHERE 句には tid カラムの値に条件を指定しています。pgbench_acounts と pgbench_tellers には、予め ALTER TABLE で WITH OIDS オプションを追加してあります。</p>
<pre>CREATE MATERIALIZED VIEW mv_ivm <strong>WITH OIDS</strong> AS
  SELECT a.aid, a.abalance, t.tbalance
  FROM pgbench_accounts a
  JOIN pgbench_tellers t ON a.bid = t.bid
  WHERE t.tid in (1,2,3) ;
</pre>
<p>また、比較のため、IVM を用いない通常のマテリアライズドビュー mv_normal を、以下のように WITH OIDS を使用せずに作成しておきます。</p>
<pre>CREATE MATERIALIZED VIEW mv_normal AS
  SELECT a.aid, a.abalance, t.tbalance
  FROM pgbench_accounts a
  JOIN pgbench_tellers t ON a.bid = t.bid
  WHERE t.tid in (1,2,3) ;
</pre>
<p>なお、pgbench でテーブルを生成するときのスケールファクターは 500 を使用しました。この場合、pgbench_accounts の行数は 50,000,000、pgbench_tellers の行数は 5,000、結果となるマテリアライズドビューの行数は 300,000 となります。</p>
<h3>性能評価</h3>
<p>まず、最初に pgbench_accounts テーブルの１行のみを更新した場合を見てみます。</p>
<pre>ivm_demo=# UPDATE pgbench_accounts SET abalance = abalance + 1 WHERE aid = 1;
UPDATE 1
Time: 9.749 ms

ivm_demo=# REFRESH MATERIALIZED VIEW mv_normal;
REFRESH MATERIALIZED VIEW
Time: <strong>39979.546 ms</strong> (00:39.980)

ivm_demo=# REFRESH MATERIALIZED VIEW<strong> INCREMANTALLY</strong> mv_ivm;
REFRESH MATERIALIZED VIEW
Time: <strong>537.591 ms</strong>

ivm_demo=# SELECT count(1) FROM (
  (SELECT * FROM mv_normal EXCEPT SELECT * FROM mv_ivm)
     UNION ALL
  (SELECT * FROM mv_ivm EXCEPT SELECT * FROM mv_normal)) q;
count
-------
0
(1 row)
</pre>
<p>この場合、通常のマテリアライズドビュー mv_normal で REFRESH MATERIALIZED VIEW コマンドを使用した場合、およそ 40 秒ほど時間がかかりました。一方で、mv_ivm に対して REFRESH MATERIALIZED VIEW INCREMENTAL コマンドを使用して IVM を行った所、500 ミリ秒ほどしかかかりませんでした。通常のリフレッシュに比べると非常に速いことが確認できました。ここで最後に実行しているクエリは EXCEPT と UNION を使って２つのマテリアライズドビューの内容に差がないことを確認しています。つまり、IVM を利用した場合でも、正しくリフレッシュが行えていることを意味しています。</p>
<p>以下のグラフは、pgbench_accounts で更新する行数を 500 から 500,000 まで増やしていった場合に、IVM に要する時間の変化を表したものです。赤い線は、通常のリフレッシュを行った場合の所要時間を表しています。更新する行数が増えると IVM に要する時間も増える傾向にはありますが、この状況では IVM は通常のリフレッシュに比べて非常に速いことが見て取れます。</p>
<figure id="attachment_1254" aria-describedby="caption-attachment-1254" style="width: 604px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-1254 size-full" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/fd00d7bcf2f30e494efa0be5b66ea1da.png" alt="" width="604" height="336" srcset="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/fd00d7bcf2f30e494efa0be5b66ea1da.png 604w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/fd00d7bcf2f30e494efa0be5b66ea1da-300x167.png 300w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/fd00d7bcf2f30e494efa0be5b66ea1da-304x169.png 304w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/fd00d7bcf2f30e494efa0be5b66ea1da-282x157.png 282w" sizes="auto, (max-width: 604px) 100vw, 604px" /><figcaption id="caption-attachment-1254" class="wp-caption-text">pgbench_accounts の更新行数に対する IVM 所要時間の変化</figcaption></figure>
<p>次に、pgbench_tellers テーブルの１行を更新した場合を見てみます。最初に tid = 5 の行を更新していますが、ビューの定義より、この行はマテリアライズドビューの内容に関わりのないことに注意してください。すなわち、ビュー定義の WHERE 句では tid IN (1, 2, 3) となっており、tid = 5 の行は含まれていません。この場合には、IVM は行うべき処理がほとんどないため、500 ms ほどですぐに完了しています。</p>
<p>一方、tid = 1 の行を更新した場合はどうでしょう。この場合には、19 秒ほど時間がかかってしまいました。</p>
<pre>ivm_demo=# UPDATE pgbench_tellerss SET tbalance = tbalance + 1 WHERE <strong>tid = 5</strong>;
UPDATE 1
Time: 10.007 ms

ivm_demo=# REFRESH MATERIALIZED VIEW INCREMENTALLY mv_ivm;
REFRESH MATERIALIZED VIEW
Time: <strong>512.998 ms</strong>

ivm_demo=# UPDATE pgbench_tellers SET tbalance = tbalance + 1 WHERE <strong>tid = 1</strong>;
UPDATE 1
Time: 9.201 ms

ivm_demo=# REFRESH MATERIALIZED VIEW INCREMENTALLY mv_ivm;
REFRESH MATERIALIZED VIEW
Time: <strong>19555.446 ms</strong> (00:19.555)
</pre>
<p>pgbench_tellers の行を更新した時に IVM に時間がかかる理由は、このビューでは pgbench_tellers の１行が pgbench_accounts の全ての行と JOIN されているためです。以下のグラフは、pgbench_tellers で更新する行数を 1, 2, 3 と増やしていった場合に、IVM に要する時間の変化を表したものです。赤い線は、通常のリフレッシュの所要時間です。行数を増やす毎に IVM の所要時間は大幅に増えていき、tid の値が 1, 2, 3 の３行全てを更新した場合には通常のリフレッシュの所要時間を上回ってしまいました。これは、OID マップのメンテナンスなど、今回の実装における IVM のオーバヘッドが現れたものと思われます。</p>
<figure id="attachment_1255" aria-describedby="caption-attachment-1255" style="width: 412px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-1255 size-full" src="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/0e8e51c25542c2ec4c0349b281290c27.png" alt="" width="412" height="321" srcset="https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/0e8e51c25542c2ec4c0349b281290c27.png 412w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/0e8e51c25542c2ec4c0349b281290c27-300x234.png 300w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/0e8e51c25542c2ec4c0349b281290c27-275x214.png 275w, https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/12/0e8e51c25542c2ec4c0349b281290c27-282x220.png 282w" sizes="auto, (max-width: 412px) 100vw, 412px" /><figcaption id="caption-attachment-1255" class="wp-caption-text">pgbench_tellers の更新行数に対する IVM 所要時間の変化</figcaption></figure>
<p>このように、今回の PoC 実装では、ある場合にはIVM の効果は大きいが、逆にかえって時間がかかる場合もあることがわかりました。巨大のテーブルのごく一部が更新された場合には有効に働くように思われますが、それもビューの定義次第です。オーバーヘッドの少ない実装の工夫ももちろう必要ですが、もし通常のリフレッシュより IVM の方が時間がかかることが事前に推測できるのであれば IVM を使用せず通常のリフレッシュを行う、といった方法も考える必要があるでしょう。</p>
<h2>おわりに</h2>
<p>実意は今回の実装はあくまで PoC ということで非常に簡約に作られているため、多くの制約があります。例えば、ビュー定義としては２つのテーブルの単純な JOIN ビューしかサポートしておらず、テーブルの数が３つ以上になったビューや、集約やサブクエリを含むようなビューには対応していません。また、マテリアライズビューの作成や更新の際のクエリ実行で利用可能なプランの種類も限られており、例えばネステッドループやマージジョインは使えますがハッシュジョインには対応していません。</p>
<p>実装に OID を使っていること自体にも問題があります。実は PostgreSQL では次期バージョン以降で行 OID の廃止が決まっており、WITH OIDS を使ったテーブル定義ができなくなる予定です。そのため、今回の PoC 実装の方法は少なくともそのままでは使用できません。それ以外にも、OID を用いた実装には、OID マップのメンテンナンスのコストが大きいことや、実行プラン毎の対応が必要になるといった実装上の困難があります。そのため、OID に依存しない実装方法の調査研究も進めているところです。</p>
<p>このように、まだ IVM の実装は途上のプロジェクトで、本記事で紹介した内容とは異なる方法で進めていく可能性も高いのですが、今後は、PoC 実装で得た知見をベースにして、PostgreSQL 上での IVM の実装にむけ、コミュニティと議論しながら開発を進めて行く予定です。何かご意見がありましたら、どうぞよろしくお願いいたします。</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>
