忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2024/04/20 08:51 |
EC-CUBE SC_DB_DBFactory_MYSQL::getWhereConverter()を使用する
EC-CUBEは、MySQLの場合Viewを使用せず毎回テーブルをJOINしています。
View非対応の古いバージョンのMySQLのためにそのような作りになっているようですが、そのせいで恐ろしい弊害が発生します。


EC-CubeをMySQLで実装すると、遅すぎて使い物にならなくなります。
どのくらい酷いかって中野区立図書館を遙かに超えるレベル。

商品数わずか1000件程度でストレスを感じるほどの遅延が出るようになり、10000件登録しようものならTimeoutをいじらないといけないというどうしようもない出来映えです。

どうしてこんなことになってるのか/class/db/dbfactory/SC_DB_DBFactory_MYSQL.phpを見てみましょう。
SC_DB_DBFactory_MYSQL::viewToSubQuery()とかな。
なんだよこれ。

読んでみると、サブクエリで全件取得して、その後でWHERE句で絞り込みを行うというとんでもなく非効率な書き方のSQLとなっています。
なんかもうちょっとどうにかならんのか、と見ているとサブクエリ中に'&&noncls_where&&'という謎の文字列を発見しました。

具体的なSQLは長大すぎて引用できませんが、構造的には次のような作りです。
'SELECT * FROM ( SELECT * FROM dtb_products &&noncls_where&& ) AS T1 WHERE (検索)'

ビューを参照するSQLを実行すると、

SC_Query::select()
SC_DB_DBFactory::getInstance()
SC_DB_DBFactory_MYSQL::sfChangeMySQL()
SC_DB_DBFactory_MYSQL::sfChangeView()
SC_DB_DBFactory_MYSQL::viewToSubQuery()
SC_DB_DBFactory_MYSQL::getWhereConverter()

という順番でSQLが変換されます。
SC_DB_DBFactory_MYSQL::viewToSubQuery()でビューに相当するSQLに書き換えられ、SC_DB_DBFactory_MYSQL::getWhereConverter()'&&noncls_where&&'のような文字列を置換して返します。

つまり、'&&noncls_where&&'に抽出範囲を狭める条件文をねじ込めば、SQLの実行速度が速くなりそうです。
サブクエリの返り値が10000件と1件では圧倒的な差が生まれることは間違いありません。

で、具体的なSC_DB_DBFactory_MYSQL::getWhereConverter()はどうなってるかというと、
 
    function getWhereConverter() {
        return array(
            "&&crscls_where&&" => "",
            "&&crsprdcls_where&&" =>"",
            "&&noncls_where&&" => "",
            "&&allcls_where&&" => "",
            "&&allclsdtl_where&&" => "",
            "&&prdcls_where&&" => "",
            "&&catcnt_where&&" => ""
        );
    }

実装されていません。

さらに、コントローラからSC_DB_DBFactoryを直接触る術がないため、外部から値を注入することもできません。
selectするたびに毎回SC_DB_DBFactoryインスタンスを作成してるんだよねこれ。

どうにかするためにはSC_Queryを直接書き換える必要があります。

/eccube/data/class/db/dbfactory/SC_DB_DBFactory_MYSQL.php
    function getWhereConverter() {
        return array(
            "&&crscls_where&&"    => $this->where['crscls_where'],
            "&&crsprdcls_where&&" => $this->where['crsprdcls_where'],
            "&&noncls_where&&"    => $this->where['noncls_where'],
            "&&allcls_where&&"    => $this->where['allcls_where'],
            "&&allclsdtl_where&&" => $this->where['allclsdtl_where'],
            "&&prdcls_where&&"    => $this->where['prdcls_where'],
            "&&catcnt_where&&"    => $this->where['catcnt_where']
        );
    }

/eccube/data/class/SC_Query.php
    function select($col, $table, $where = "", $arrval = array()){
        $sqlse = $this->getsql($col, $table, $where);
        $dbFactory = SC_DB_DBFactory_Ex::getInstance();
        $dbFactory->where = $this->where;
        $sqlse = $dbFactory->sfChangeMySQL($sqlse);
        $ret = $this->conn->getAll($sqlse, $arrval);
        return $ret;
    }

以上で形ができました。
あとはSELECTを実行する際に、
    $objQuery->where['noncls_where'] = ' WHERE product_id = '.mysql_real_escape_string($product_id);
    $arrRet = $objQuery->select("*", "vw_products_nonclass AS alldtl", "product_id = ?", array($product_id));

などとすれば動作します。

上記はあくまで最低限の形なので、NOTICEが出たりコントローラに直接mysql_real_escape_string()があったりする残念な作りです。
SC_Query::escape()Pear_DB::escapeSimple()あたりを拾ってくるようにしたりした方がよいでしょう。
あと'&&noncls_where&&'以外の置換文字列はそもそも存在すらしていないので、追加するなり削除するなりしてしまいましょう。


ちなみにPostgresの場合は相当すっきりしたSQLになっていますが、これは単にCREATE VIEWに面倒ごとを突っ込んでいるだけです。
つうかMySQLにviewが実装されたのは2005年なんだからview非対応への対応とかもういいよ。
PR


2011/03/21 19:37 | Comments(0) | TrackBack() | PHP

トラックバック

トラックバックURL:

コメント

コメントを投稿する






Vodafone絵文字 i-mode絵文字 Ezweb絵文字 (絵文字)



<<今週の実績 2011/03/27 | HOME | 買ったものリスト 2011/03/20>>
忍者ブログ[PR]