HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

カーソルの使用方法

もうZend Framework 1(ZF1)の話はいいやって感じだけど一応メモ。

ZF1を使ってDBへのアクセスを実現するには、通常はZend_Db_Table_Abstractクラスを継承したモデルクラスを作って実現すると思うじゃないですか。

<?php

require_once 'Zend/Db/Table/Abstract.php';

class Model_Hoge extends Zend_Db_Table_Abstract {
    protected $_name = 'hoge';
}

で、例えばデータの取得は以下のような感じ。

$query = $hoge->select();
$rows = $hoge->fetchAll($query);
$res= array();
while ($rows->valid()) {
    $row = $rows->current();
    $res[$row->id] = $row->name;
    $rows->next();
}

でも、これだと、Zend_Db_Table_Rowset_Abstractが返された瞬間に全レコードのデータがメモリに載るので、件数が多かったりするとメモリアロケーションエラーが発生する。

そんなわけで「Zend Framework カーソル」なんて検索すると以下のページがヒットしたりするわけなんだが。

Statement?DB Adapter? なんか嫌な予感がした。

先のモデルクラスをベースにすると、以下のように書かないと、大量データの処理の際には使い物にならないという。

$query = $hoge->select();
$stmt = $hoge->getAdapter()->query($query->__toString());
$res = array();
while (($row = $stmt->fetch()) !== false) {
    $res[$row["id"]] = $row["name"];
}

もう、Zend_Db_Table_Rowset_AbstractやZend_Db_Table_Row_Abstractの恩恵一切なし・・・

ついでだったので、INSERTがクソ遅いという問題についても調べてみた。

元のコードはこんな感じ。

$start = time();
for ($i = 10000; $i < 11000; ++$i) {
    $data = array(
        "id" => $i,
        "name" => "hhelibex-{$i}",
    );
    $hoge->insert($data);
}
echo('insert time:' . (time() - $start));
$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $data = array(
            "name" => "HHeLiBeX-{$i}",
        );
        $where = array(
            "id = ?" => $i,
        );
        $hoge->update($data, $where);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('update time:' . (time() - $start));
$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $where = array(
            "id = ?" => $i,
        );
        $hoge->delete($where);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('delete time:' . (time() - $start));

これを実行すると、環境にもよるが(だいぶ前のLet's Note上のCentOS 7なので)、

insert time:40
update time:41
delete time:37

で、こんなふうに書き換えてみた。

<?php

require_once 'Zend/Db/Table/Abstract.php';

class Model_Hoge extends Zend_Db_Table_Abstract {
        protected $_name = 'hoge';
        public function getTableName() {
                return $this->_name;
        }
}

$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $data = array(
            "id" => $i,
            "name" => "hhelibex-{$i}",
        );
        $hoge->getAdapter()->insert($hoge->getTableName(), $data);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('insert time:' . (time() - $start));
$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $data = array(
            "name" => "HHeLiBeX-{$i}",
        );
        $where = array(
            "id = ?" => $i,
        );
        $hoge->getAdapter()->update($hoge->getTableName(), $data, $where);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('update time:' . (time() - $start));
$start = time();
try {
    for ($i = 10000; $i < 11000; ++$i) {
        $where = array(
            "id = ?" => $i,
        );
        $hoge->getAdapter()->delete($hoge->getTableName(), $where);
    }
} catch (Exception $e) {
    echo '<pre>' . $e->__toString() . '</pre>';
}
echo('delete time:' . (time() - $start));

結果は。

insert time:16
update time:16
delete time:14

半分以下にはなった。