PHP を使用して共有メモリーにデータセットを直接保存する

PHP でストレージとして共有メモリーを使用する方法についてのステップ・バイ・ステップのガイド

共有メモリーについての基本操作と、Web アプリケーションのデータ・ストレージとして共有メモリーを使用する方法を学びましょう。共有メモリーを使用すると、アプリケーションの実行速度や信頼性が高まり、他のアプリケーションとのデータ交換もできるようになります。この記事では、Web アプリケーションの開発で起こりがちな一般的な問題を解決するために共有メモリーを使用する方法について、例を示しながら説明します。

Klaus Silveira, Software Engineer and Instructor, 4Linux

Klaus SilveiraKlaus Silveira は PHP を専門とするソフトウェア技術者であり、インストラクターでもあります。彼は Web のためのインテリジェントなソリューション開発や、SimpleString や SimpleSHM など、オープンソースの世界での貢献に強い情熱を持っています。彼はフリー・ソフトウェア・ソリューションに特化したブラジル企業、4Linux に勤務しています。彼は Zend PHP 認定試験を受験する開発者のためのエンタープライズ Web アプリケーションや PHP コースの作成を担当しています。



2012年 2月 16日

概要

共有メモリーを使用すると、同じマシン内のアプリケーション間で効率的にデータを交換することができます。あるプロセスが共有メモリーとしてメモリー・セグメントを作成すると、そのセグメントに適切なパーミッションが設定されている限り、他のプロセスがそのセグメントにアクセスすることができます。すべてのセグメントには物理メモリーの 1 つの領域を指す shmid という一意の ID があり、この領域を他のプロセスが操作することができます。いったんセグメントが作成され、適切なパーミッションが設定されると、同じマシン内の他のプロセスはそのセグメントに対し、読み取り、書き込み、削除の操作をすることができます。

これはつまり、C で作成されたアプリケーションが、Java や PHPといった他の言語で作成されたアプリケーションと情報を共有できるということです。それらのアプリケーションは、共有メモリーに格納されている情報にアクセスしてその内容を理解できる限り、その情報を共有することができます。共有メモリーは広く使用されており、ほとんどの言語で実装できるため、共有メモリーへのアクセスについては問題ないはずです。情報を理解するという点については、XML や JSON などの標準的なフォーマットを使用することができます。

共有メモリーを使用すると、プロセス間でデータを高速に交換することができます。これは主に、いったん共有メモリーにセグメントが作成されると、その後のデータの受け渡しにカーネルがまったく関与しないことによるものです。このような手法はよく、IPC (Interprocess Communication: プロセス間通信) と呼ばれます。IPC には、他にもパイプ、メッセージ・キュー、RPC、ソケットなどを使用する方法があります。アプリケーション同士の通信が必要なエコシステムを扱う場合、アプリケーション間で信頼性の高いデータ交換を高速に行える IPC のような手法は貴重です。通常、アプリケーション間の情報交換にはデータベースが使用されますが、データベースを使用すると、エコシステムの規模によってはクエリーの実行速度が遅くなる場合や、I/O 処理をブロックしてしまう場合もあります。共有メモリーを使用すれば、開発者が I/O 処理で苦労することがなくなります。

この記事で紹介する内容は単純です。ここでは PHP で共有メモリーのセグメントを作成する方法と操作する方法、さらには共有メモリーを使用してデータセットを保存し、そのデータセットを他のアプリケーションで使用できるようにする方法を説明します。たとえ共有メモリーを使用してデータを交換する予定がない場合でも、共有メモリーを使用するとアプリケーションで I/O 処理の問題を気にする必要がなくなるため、共有メモリーを使用するとメリットがあります。データセットを直接メモリーに保存すると、Web サービスのデータをキャッシュしたりセッションを共有したりできるなど、多くのメリットがあります。共有メモリーは非常に有用な概念であり、すべての PHP 開発者は共有メモリーについて理解しておく必要があります。

共有メモリーと PHP

PHP には多種多様な拡張モジュールがあり、共有メモリーに関しても同様です。しかし、共有メモリーの場合、いくつかの共有メモリー関数を利用すれば、拡張モジュールをインストールしなくても、セグメントを容易に操作することができます。


セグメントを作成する

共有メモリー関数はファイル操作関数と似ています。ただし共有メモリー関数はストリームを扱う代わりに共有メモリーのアクセス ID を扱います。そうした関数の最初の例が shmop_open 関数です。この関数を使用すると、既存のセグメントを開くことや、新しいセグメントを作成することができます。この shmop_open 関数は昔ながらの fopen 関数と非常によく似ています。fopen 関数はストリームを開いてファイルを操作し、リソースを返します。他の関数はそのリソースを使用して、その開かれたストリームに対して読み書きすることができます。では、リスト 1 の shmop_open を見てみましょう。

リスト 1. shmop_open 関数
<?php

$systemid = 864; // System ID for the shared memory segment
$mode = "c"; // Access mode
$permissions = 0755; // Permissions for the shared memory segment
$size = 1024; // Size, in bytes, of the segment

$shmid = shmop_open($systemid, $mode, $permissions, $size);

?>

最初にあるのがシステム ID パラメーターです。このパラメーターはシステム内の共有メモリー・セグメントを指定する番号です。2 番目のパラメーターはアクセス・モードです。このアクセス・モードは fopen 関数のアクセス・モードと非常によく似ています。セグメントにアクセスする場合のモードには、以下の 4 つがあります。

  • モード "a" (セグメントに読み取り専用でアクセスすることができます)
  • モード "w" (セグメントにアクセスして読み書きすることができます)
  • モード "c" (新しいセグメントを作成します。そのセグメントが既に存在する場合には、セグメントを開いて読み書きしようとします)
  • モード "n" (新しいセグメントを作成します。そのセグメントが既に存在する場合には、操作に失敗したとして、セグメントを開くことなく終了します)

3 番目のパラメーターはセグメントのパーミッションです。このパラメーターには 8 進数の値を指定する必要があります。

4 番目のパラメーターはセグメントのサイズをバイト数で指定します。セグメントに書き込む場合、そのセグメントには適切なバイト数を割り当てておく必要があります。

この shmop_open 関数は ID の番号を返すため、他の関数がこの ID を使用して共有メモリー・セグメントを操作できることに注意してください。この ID は共有メモリーにアクセスするための ID であり、引数として関数に渡されるシステム ID とは別です。この 2 つを混同しないように注意してください。shmop_open は失敗すると FALSE を返します。


セグメントに書き込む

shmop_write 関数を使用すると、共有メモリー・ブロックに書き込むことができます。この関数の使い方は単純であり、この関数が取る引数は 3 つしかありません。この関数をリスト 2 に示します。

リスト 2. shmop_write 関数を使用して共有メモリー・ブロックに書き込む
<?php

$shmid = shmop_open(864, 'c', 0755, 1024);
shmop_write($shmid, "Hello World!", 0);

?>

shmop_write 関数は fwrite 関数と似ています (fwrite 関数には引数が 2 つあり、1 つは fopen によって返される開かれたストリーム・リソースを指定し、もう 1 つは書き込み対象のデータを指定します)。shmop_write 関数は、まさに書き込み操作を行います。

1 番目の引数は shmop_open によって返される ID であり、操作対象の共有メモリー・ブロックを指定します。2 番目の引数は保存対象のデータです。そして最後 (3 番目) の引数は書き込みを開始する場所を指定します。3 番目の引数にはデフォルトで必ず 0 を指定することで、セグメントの先頭から書き込みが開始されるようにします。この関数は書き込みに失敗すると FALSE を返し、成功すると書き込まれたバイト数を返すことに注意してください。


セグメントから読み取る

共有メモリー・セグメントからの読み取りは単純です。必要なものは、開かれたセグメントと shmop_read 関数のみです。この関数は引数をいくつか取って、fread と同じように動作します。リスト 3 はファイルの内容を PHP で読み取る方法を示しています。

リスト 3. shmop_read 関数を使用してファイルの内容を読み取る
<?php

$stream = fopen('file.txt', 'r+');
fwrite($stream, "Hello World!");
echo fread($stream, 11);

?>

共有メモリー・セグメントの内容を読み取る方法も似ています (リスト 4)。

リスト 4. 共有メモリー・セグメントの内容を読み取る
<?php

$shmid = shmop_open(864, 'c', 0755, 1024);
shmop_write($shmid, "Hello World!", 0);
echo shmop_read($shmid, 0, 11);

?>

ここに示した引数に注目してください。shmop_read 関数は shmop_open によって返される ID を 1 番目の引数に取ります。この ID については既に説明しましたが、shmop_read 関数は他にも 2 つの引数を取ります。2 番目の引数はセグメントから読み出しを開始する位置を指定し、3 番目の引数は読み取り対象のバイト数を指定します。2 番目の引数は常に 0 (データの先頭) になるかもしれませんが、何バイト読めばよいのかわからない場合があるため、3 番目の引数は問題になる可能性があります。

この関数の振る舞いは fread 関数の振る舞いに非常によく似ています。fread 関数は 2 つの引数を取り、1 番目の引数には fopen によって返される開かれたストリーム・リソースを指定し、2 番目の引数には、そのストリームから読み取るバイト数を指定します。ファイル内のバイト数を返す filesize 関数を使用すると、ファイル内の全データを読み取ることができます。

幸いなことに、共有メモリー・セグメントを扱う際、shmop_size 関数は filesize 関数と同じようにセグメントのサイズをバイト数として返します。リスト 5 を見てください。

リスト 5. shmop_size 関数はセグメントのサイズをバイト数で返す
<?php

$shmid = shmop_open(864, 'c', 0755, 1024);
shmop_write($shmid, "Hello World!", 0);

$size = shmop_size($shmid);
echo shmop_read($shmid, 0, $size);

?>

セグメントを削除する

ここまで、共有メモリー・セグメントを開く方法と、共有メモリー・セグメントに対して書き込みおよび読み取りを行う方法を説明しました。共有メモリー・セグメントに対する CRUD 操作の説明をひと通り終えるには、セグメントを削除する方法を説明する必要があります。shmop_delete 関数を使用すると、容易にセグメントを削除することができます。この関数が取る引数は、削除対象の共有メモリーの ID という 1 つのみです。

リスト 6. shmop_delete 関数は削除対象のセグメントをマーキングする
<?php

$shmid = shmop_open(864, 'c', 0755, 1024);
shmop_write($shmid, "Hello World!", 0);
shmop_delete($shmid);

?>

このコードによって実際にセグメントが削除されるわけではなく、削除対象のセグメントがマーキングされるにすぎません。なぜなら、他のプロセスが共有メモリー・セグメントを使用している間、そのセグメントを削除することはできないからです。shmop_delete 関数は削除対象のセグメントをマーキングし、他のプロセスがそのセグメントを開くのを防ぎます。そのセグメントを削除するためには、そのセグメントを閉じる必要があります。


セグメントを閉じる

共有メモリー・セグメントを開くと、そのセグメントに「接続」されます。セグメントに接続できると、そのセグメントに対して読み書きすることができますが、操作を終えた後は、共有メモリー・セグメントとの接続を切断する必要があります。そのためにはリスト 7 の shmop_close 関数を使用します。

shmop_close 関数は、ファイルを扱う場合の fclose 関数と非常によく似ています。ファイルでストリームを開き、そのストリームの読み書きをした後は、そのストリームを閉じる必要があります。閉じないとロックが発生してしまいます。

リスト 7. shmop_close 関数を使用してセグメントとの接続を切断する
<?php

$shmid = shmop_open(864, 'c', 0755, 1024);
shmop_write($shmid, "Hello World!", 0);
shmop_delete($shmid);
shmop_close($shmid);

?>

共有メモリーをストレージの選択肢として使用する

ここまでで共有メモリーの基本と、共有メモリー・セグメントに対する基本的な CRUD 操作について説明したので、今度はそれを実際に応用してみましょう。共有メモリーは独特なストレージ手段として使用することができます。共有メモリーをストレージとして使用すると、読み書き操作が高速になる、プロセスの相互運用が可能になる、といったメリットがあります。Web アプリケーションの場合、以下のような用途に共有メモリーを使用することができます。

  • キャッシュ・ストレージ (データベース・クエリー、Web サービス・データ、外部データなどのキャッシュ)
  • セッション・ストレージ
  • アプリケーション間のデータ交換

先に進む前に、SimpleSHM という簡単なライブラリーを紹介しましょう。SimpleSHM は PHP を使用して共有メモリーを操作するための簡単な抽象化レイヤーであり、オブジェクト指向の方法で容易にセグメントを操作することができます。このライブラリーを使用すると、共有メモリーをストレージとして使用する簡単なアプリケーションを作成する際、非常に簡潔なコードを作成することができます。SimpleSHM を使用するためには、GitHub のページから tarball を入手します。

扱うメソッドは read、write、delete の 3 つです。単純にクラスからオブジェクトをインスタンス化すると、共有メモリー・セグメントを開くことができます。リスト 8 は基本的な使い方を示しています。

リスト 8. SimpleSHM の基本的な使い方
<?php

$memory = new SimpleSHM;
$memory->write('Sample');
echo $memory->read();

?>

クラスに ID が渡されていないことに注意してください。ID を渡さない場合、SimpleSHM はランダムに数値を選択し、その数値を使用して新しいセグメントを開きます。コンストラクターの引数として数値を渡すと、既存のセグメントを開くことや、特定の ID を持つセグメントを作成することができます (リスト 9)。

リスト 9. 特定のセグメントを開
<?php

$new = new SimpleSHM(897);
$new->write('Sample');
echo $new->read();

?>

マジック・メソッド __destructor は、セグメントに対して shmop_close を呼び出してオブジェクトを設定解除し、セグメントとの接続を切断します。これが SimpleSHM の基本です。今度は SimpleSHM の高度な使い方として、共有メモリーをストレージとして使用する方法を説明しましょう。配列つまりオブジェクトをそのままメモリーに保存することはできないため、データセットを保存するためにはデータセットのシリアライズを行う必要があります。ここでは JSON を使用してシリアライズを行いますが、XML や、PHP に組み込みのシリアライズ機能など、他の方法を使用するのでも十分です。リスト 10 は 1 つの例を示しています。

リスト 10 共有メモリーをストレージとして使用する
<?php

require('SimpleSHM.class.php');

$results = array(
	'user' => 'John',
	'password' => '123456',
	'posts' => array('My name is John', 'My name is not John')
);

$data = json_encode($results);

$memory = new SimpleSHM;
$memory->write($data);
$storedarray = json_decode($memory->read());

print_r($storedarray);

?>

これで、配列を JSON ストリングにシリアライズして共有メモリー・ブロックに保存し、そこから読み出して JSON ストリングをデシリアライズし、共有メモリー・ブロックに保存されていた配列を表示することができました。これは基本的なことに思えますが、このスニペットの可能性を考えてみてください。この方法を使用することで、Web サービス・リクエストやデータベース・クエリーの実行結果、さらにはテンプレート・エンジンのキャッシュを保存することができます。メモリーに対して読み書きすることにより、ディスクに対して読み書きするよりも、はるかに高いパフォーマンスを実現することができます。

このストレージ手法はキャッシュに有効なだけではなく、アプリケーション間でのデータの交換にも有効です (ただしデータを交換する双方で読み取り可能なフォーマットでデータを保存する必要があります)。Web アプリケーションでの共有メモリーの強力さを過小評価してはなりません。多種多様な方法により、この種のストレージを賢明な方法で実装することができます。開発者の創造性とスキル次第で可能性は無限です。


まとめ

この記事では、PHP で共有メモリー・セグメントを操作するために使用できる方法の大部分を説明し、共有メモリーがどのように動作するかも説明しました。さらに、Web アプリケーションの能力を高める方法を提案し、Web アプリケーションの開発で起こりがちな一般的な問題に対するソリューションを作成する上で考慮すべき要素についても概説しました。この記事で示した共有メモリーの概念と、その実装方法についてのガイドラインは、共有メモリーをストレージとして使用するための出発点として役立つはずです。この記事で作成した初期モデルは、より複雑な機能やソリューションを検討する上で役に立つはずです。

この先は

この記事では、アプリケーション間でのキャッシング、セッション共有、一般的なデータ交換など、まさに共有メモリーを使用する場合によくある問題をいくつか概説しました。これらの問題については、この記事で紹介した手法を基にもっと洗練されたソリューションを探求することができます。現在の SimpleSHM の実装を皆さんのニーズに合わせて自由に拡張し、皆さんが加えた変更を SimpleSHM プロジェクトへの貢献としてフィードバックしてください。

参考文献

学ぶために

  • Michael Kerrisk 氏による著書『The Linux Programming Interface』にはプロセス間通信について説明した素晴らしい章があり、また 45 章から 48 章は System V の IPC に特化して説明しています。
  • Dave Marshall 氏による記事「IPC:Shared Memory」は、C 言語の共有メモリー関数に関して興味深く実用的な手法を紹介しています。
  • Richard Stevens 氏による著書『UNIXネットワークプログラミング』は技術的に優れた内容を解説しており、また C による実装をいくつか紹介しています。共有メモリーに関するサンプル・チャプターを読んでみてください。
  • IBM developerWorks の PHP project resources を利用して PHP のスキルを磨いてください。
  • developerWorks には他にも PHP に関する記事やチュートリアルが豊富に用意されています。
  • IBM オープンソース開発者にとって関心のある、今後開催されるカンファレンスや展示会、ウェブキャスト、その他のイベントについて調べてみてください。
  • developerWorks の Open source ゾーンを訪れてください。オープンソース技術を使用した開発や、IBM 製品でオープンソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。
  • Twitter で developerWorks をフォローしてください。

製品や技術を入手するために

  • 皆さんの次のオープンソース開発プロジェクトを IBM ソフトウェアの試用版を使って革新してください。ダウンロード、あるいは DVD で入手することができます。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=792470
ArticleTitle=PHP を使用して共有メモリーにデータセットを直接保存する
publish-date=02162012