開発室ブログ

mongoDB PHP

PHPでMongoDB(実践編)

 今年も残すところ一ヶ月を切りました。128年ぶりの遅い初雪で執筆時点でようやく雪が積もり、スキー場関係者は安堵した事でしょう。以前は仕事終わりに週3でスキー場へ行ってましたが、ここ十数年トンとご無沙汰でした。

 昨シーズン、ひょんなことから久しぶりにスキー場へ行ってみたところ、2本滑ってギブでした。しかも、ブーツにビンディングを付け、立ち上げろうとしても脚力が無く、腹が邪魔となりなかなか立ち上げれません…。情けないったらありゃしないので、この冬は毎週末スキー場通いと考えている今日この頃です。(多分、考えるだけで終わると思います。キッパリ)

オーンズ(onze)はフランス語で「11」って言う意味らしいよ

はじめに

 Aさんにブログねた取られた先に越されたので、仕方なく他のネタを探りながら日々の業務をこなしていたら、最近立ち上がった社内プロダクトにて処理件数の増加に伴い、メモリーエラーが発生しました。

PHP Fatal error:  Allowed memory size of 1073741824 bytes exhausted (tried to allocate 132112384 bytes) in /hoge/fuga/ahoaho.php on line xxx

 だまし騙しini_set('memory_limit', 拡張メモリ値);でメモリを増加させてましたが、この延命策も時間の問題であり破綻が目に見えるので抜本的な対策が必要になりました。

 ってなことで内部処理でデータが大容量となってきているファイルI/Oの部分をDBに移行とすることにします。データの形式はJSONなので、そのまんまブチ込めるMongoDBを採用します。

 早速、情報をGoogle先生に聞いてみたところ、ナント開発室チョーのブログが第4位にランクインされているではないですか!!

 流石です。でも参考にさせて頂いたのは2位と3位の記事でした。

MongoDBインストール ~ PHPで使うまで いいからやる。CentOS7 + PHP7 + mongoDB

 ちなみに、室チョーのブログについて弊社で利用している社内ツールで検索順位をモニターしてましたら安定的にほぼ4位の位置につけています。大人の事情で社内ツールの画面は掲載できませんがモニター結果をダウンロードしてエクセルグラフにしました。

 このように気になるサイトの検索順位を地域やモバイル毎に毎日モニターできるツールについて来年以降皆様にもご利用いただけるよう鋭意、開発中でございます。今後に乞うご期待ください。

SEOツールとSEO・Webマーケティング情報 | アクセスSEO対策

環境構築

 まずは諸々インストールしますが、大概は一筋縄にはいかないのが世の常です。案の定このようなエラーが出ました。

$ pecl install mongodb

WARNING: channel "pecl.php.net" has updated its protocols, use "pecl channel-update pecl.php.net" to update
downloading mongodb-1.5.3.tgz ...
Starting to download mongodb-1.5.3.tgz (1,059,580 bytes)
..........................................................................................done: 1,059,580 bytes
426 source files, building
running: phpize
Can't find PHP headers in /opt/remi/php72/root/usr/include/php
The php-devel package is required for use of this command.
ERROR: `phpize' failed

 以下で解決

PHPのAPCをインストールするのにつまずいた
$ yum install --enablerepo=remi,remi-php72 php
:
完了しました!

$ pecl install mongodb
:
Build process completed successfully
Installing '/usr/lib64/php/modules/mongodb.so'
install ok: channel://pecl.php.net/mongodb-1.5.3
configuration option "php_ini" is not set to php.ini location
You should add "extension=mongodb.so" to php.ini

php.iniにダイナミックライブラリを書け

 と言われたので追加します。

$vi php.ini

extension=/usr/lib64/php/modules/mongodb.so

 でWEBサーバー再起動します。

$ systemctl restart httpd

 phpinfoで確認します。

 本来ならMongoDBをデーモンとしてサービス登録してゴニョゴニョしますが、実はこの環境には既にMongoDBがデーモンとして実行していました。(どうりでデフォルトポート(=27017)で接続出来なかった訳だ)

 DBクライアントにはこの環境で使用していたMongoDB Compassをそのまんま使い、新たにDBとコレクション(MongoDBではテーブルではなくコレクション(Collection)と言います)を追加します。

MongoDB Compass

 これで一通り環境が構築できました(注)

実装

<?php
/* DB接続 */
$mongo = new MongoDB\Driver\Manager("mongodb://localhost:接続ポート");
print_r($mongo);

 まずは接続を確認してみます。

MongoDB\Driver\Manager Object
(
    [uri] => mongodb://localhost:接続ポート
    [cluster] => Array
        (
        )

)

 オブジェクトが表示されましたので接続されたようです。あとはコレに習ってゴリゴリ書きます。

MongoDB driver

 MongoDBにデータを登録し、そのデータを取得します。SQLでいうところのINSERT INTO テーブル VALUES (データ1,...)SELECT * FROM テーブルですね。

$mongo = new MongoDB\Driver\Manager("mongodb://localhost:接続ポート");

// Insert
$bulk = new MongoDB\Driver\BulkWrite;
$bulk->insert(['column1' => 'hogehoge1', 'column2' => 'fugafuga1', 'column3' => 'ahoaho1']); // 1レコード目
$bulk->insert(['column1' => 'hogehoge2', 'column2' => 'fugafuga2', 'column3' => 'ahoaho2']); // 2レコード目
$mongo->executeBulkWrite('DB名.コレクション名', $bulk);

// Select
$query  = new MongoDB\Driver\Query( [] );
$articles = $mongo->executeQuery( 'DB名.コレクション名', $query);
print_r($articles);

 ↑の結果↓

MongoDB\Driver\Cursor Object
(
    [database] => DB名
    [collection] => コレクション名
    [query] => MongoDB\Driver\Query Object
        (
            [filter] => stdClass Object
                (
                )

            [options] => stdClass Object
                (
                )

            [readConcern] => 
        )

    [command] => 
    [readPreference] => 
    [session] => 
    [isDead] => 
    [currentIndex] => 0
    [currentDocument] => 
    [server] => MongoDB\Driver\Server Object
        (
            [host] => localhost
            [port] => 接続ポート
            [type] => 1
            [is_primary] => 
            [is_secondary] => 
            [is_arbiter] => 
            [is_hidden] => 
            [is_passive] => 
            [last_is_master] => Array
                (
                    [ismaster] => 1
                    [maxBsonObjectSize] => 16777216
                    [maxMessageSizeBytes] => 48000000
                    [maxWriteBatchSize] => 100000
                    [localTime] => MongoDB\BSON\UTCDateTime Object
                        (
                            [milliseconds] => 1542856673388
                        )

                    [logicalSessionTimeoutMinutes] => 30
                    [minWireVersion] => 0
                    [maxWireVersion] => 6
                    [readOnly] => 
                    [ok] => 1
                )

            [round_trip_time] => 0
        )

)

 ムッムッム??取得結果はデータの配列じゃないのか?と思いGoogle先生に尋ねたところ、

phpのMongoクラスでfind()してもレコードが返ってこないときの対処法

foreachで一つずつ取ってこないと中身が取れない

 なるほど…ってなことでforeachでぶん回します。

$mongo = new MongoDB\Driver\Manager("mongodb://localhost:接続ポート");

// Insert
$bulk = new MongoDB\Driver\BulkWrite;
$bulk->insert(['column1' => 'hogehoge1', 'column2' => 'fugafuga1', 'column3' => 'ahoaho1']); // 1レコード目
$bulk->insert(['column1' => 'hogehoge2', 'column2' => 'fugafuga2', 'column3' => 'ahoaho2']); // 2レコード目
$mongo->executeBulkWrite('DB名.コレクション名', $bulk);

// Select
$query  = new MongoDB\Driver\Query( [] );
foreach( $mongo->executeQuery( 'DB名.コレクション名', $query) as $article ){
    print_r( $article );
}

 と修正し、実行してみます。

stdClass Object
(
    [_id] => MongoDB\BSON\ObjectId Object
        (
            [oid] => 5bf4b73fce6efd5c88b74ed0
        )

    [column1] => hogehoge1
    [column2] => fugafuga1
    [column3] => ahoaho1
)
stdClass Object
(
    [_id] => MongoDB\BSON\ObjectId Object
        (
            [oid] => 5bf4b771ce6efd5c88b74efe
        )

    [column1] => hogehoge2
    [column2] => fugafuga2
    [column3] => ahoaho2
)

 はい、無事に取り出すことに出来ました。メデタシ、メデタシ。

 ちなみに_idというのはMongoDB内のユニークキー情報でコレをqueryOptionsパラメータの指定で出力させないことも可能です。詳細はQueryクラスのqueryOptionsパラメータを参照願います。

おわりに

 このようにSQLなDBを使うまでもなけどファイルI/Oではリソース面で懸念がある場合、お手軽(?)なデータ管理としてMongoDBが有効と思います。今後、ソリューションの選択肢としてもいいのではないでしょうか。

おまけ

 MongoDBのカラム属性にSQLでは掟破りな配列カラムを定義することが出来ます。NoSQLなMongoDBなら「まッ、いっか」って納得しますが、SQLでカラムに配列を使おうと(Postgreが配列カラムを扱うことが出来るようです。)思った時点で負けです。トットとテーブルの正規化を見直してください。

注:実はMongoDBのPHP向けライブラリがあるのですが、今回は直接ドライバをコールします。

RecentPost