不要なデータベースクエリからKohana ORMを配信する

ORMは間違いなく強力で便利なものですが、リクエストは常に最適であるだけでなく、不要なものも生成されます。 モデルのオブジェクトを作成するとき、ORMは対応するデータベーステーブルのすべてのフィールドに関する情報を知っている必要があります。 これにより、不要なデータベースクエリが発生します。



問題



ORMを使用してモデルオブジェクトを作成する場合、 「tablename」からSHOW FULL COLUMNSクエリが実行され、 保護された$ _table_columnsオブジェクトフィールドにフィールドデータの配列が入力されます。

protected _table_columns => array(8) ( "id" => array(4) ( "type" => string(3) "int" "is_nullable" => bool FALSE ) "email" => array(4) ( "type" => string(6) "string" "is_nullable" => bool FALSE ) ...
      
      







スクリーンショットは、データベースへの最後のクエリを示しています(クリック可能)

画像



さらに、ORM :: factory()は毎回オブジェクトの新しいインスタンスを作成するため、構造を使用して連続していくつかのメソッドを呼び出します

 ORM::factory('model_name')->method_1() ORM::factory('model_name')->method_2()
      
      





2つの同一のSHOW FULL COLUMNS FROMクエリを生成します(特定のケースで$ _table_columnsがまったく必要ない場合でも)。 また、関連モデルをロードすると、各モデルのクエリが生成されます-ORMへの呼び出し:: factory( 'user')-> with( 'profile')-> with( 'photo') 。 (ORMをアクティブに使用して)プロジェクトのデータベースに対する2番目のクエリがすべて出力されます-SHOW FULL COLUMNS FROM



一つの解決策



問題の解決策は非常に簡単ですが、何らかの理由で他のどこにも説明されていません-各モデルにこの配列を手動で入力します(当然、プロジェクト開発の最後に)。 数十の大きなテーブルを手動で埋めようとするのは、自分を足で撃つようなものです。 したがって、数時間で普遍的なソリューションが見つかりました-モデルフォルダーを再帰的にトラバースするOptimizeクラスを記述し、 拡張ORMレコードと保護された$ _table_columnsレコードを選択し、 ORM :: factory( 'model')を使用してモデルのこの配列を生成します- > list_columns()およびわずかにやり直した「ネイティブ」メソッドDebug :: vars();

クラス自体のコードはネタバレです



クラスコードを最適化する
 class Optimize{ private static $files = array(); /** * Returns database tables columns list * * @uses find_models() * @uses _dump_simple() */ public static function list_columns() { $dir = APPPATH . "classes/model"; self::find_models($dir); foreach (self::$files as $model) { $file_text = file_get_contents($model); if(preg_match('/extends +ORM/i', $file_text) && !preg_match('/_table_columns/i', $file_text)){ preg_match("/(class\sModel_)(\w+)?(\sextends)/", $file_text, $match); $model_name = preg_replace("/(class\sModel_)(.*?)(\sextends)/", "$2", $match[0]); echo '<h3>Model_'.ucfirst($model_name).'</h3>'; $columns[] = ORM::factory(strtolower($model_name))->list_columns(); $output = array(); foreach ($columns as $var) { $output[] = self::_dump_simple($var, 1024); } echo '<pre>protected $_table_columns = ' . substr(implode("\n", $output), 0, -1) . ';</pre>'; echo '========================================================'; } } } public static function find_models($in_dir) { if (preg_match("/_vti[.]*/i", $in_dir)) { return; } if ($dir_handle = @opendir($in_dir)) { while ($file = readdir($dir_handle)) { $path = $in_dir . "/" . $file; if ($file != ".." && $file != "." && is_dir($path) && $file != '.svn') { self::find_models($path); } if (is_file($path) && $file != ".." && $file != "." && strtolower(substr(strrchr($path, '.'), 1))=='php') { self::$files[] = $path; } } } } protected static function _dump_simple(& $var, $length = 128, $limit = 10, $level = 0) { if ($var === NULL) { return 'NULL,'; } elseif (is_bool($var)) { return ($var ? 'TRUE' : 'FALSE') . ','; } elseif (is_float($var)) { return $var . ','; } elseif (is_string($var)) { return "'" . $var . "',"; } elseif (is_array($var)) { $output = array(); $space = str_repeat($s = ' ', $level); static $marker; if ($marker === NULL) { $marker = uniqid("\x00"); } if ($level < $limit) { $output[] = "array("; $var[$marker] = TRUE; foreach ($var as $key => & $val) { if ($level == 1 && !in_array($key, array('type', 'is_nullable'))) continue; if ($key === $marker) continue; if (!is_int($key)) { $key = "'" . htmlspecialchars($key, ENT_NOQUOTES, Kohana::$charset) . "'"; } $output[] = "$space$s$key => " . self::_dump_simple($val, $length, $limit, $level + 1); } unset($var[$marker]); $output[] = "$space),"; } return implode("\n", $output); } else { return htmlspecialchars(print_r($var, TRUE), ENT_NOQUOTES, Kohana::$charset) . ','; } } } // End Optimize
      
      









モデルクラスコードでは、フィールドを持つフィールドの配列を自動的に行わないことに決定しました。コードのフォーマットは推測できません。 したがって、すべてがフォームの画面に表示されます。

 Model_Option protected $_table_columns = array( 'id' => array( 'type' => 'int', 'is_nullable' => FALSE, ), 'name' => array( 'type' => 'string', 'is_nullable' => FALSE, ),
      
      







クラス自体(/application/classes/optimize.phpにホストされています)。 どこからでもメソッドを呼び出す:

  echo Optimize::list_columns();
      
      





メソッドの証拠は、モデルの印刷オブジェクトにlast_queryがないことです。

画像



他のソリューションが見つかりました-blogocms.ru/2011/05/kohana-uskoryaem-orm-テーブル構造をキャッシュします。 より単純なソリューションですが、速度も最適ではありません。



プロファイリングとテスト



速度を測定しようとしました(精度を測定するふりをしません)。 小さな模擬テストを書きましょう

 $token = Profiler::start('Model', 'User'); ORM::factory('user')->with('profile')->with('profile:file'); Profiler::stop($token); echo View::factory('profiler/stats');
      
      





そして、10回実行します。 $ _table_columns配列に値を入力しない 、平均して、フレームワーク全体の作業に0.15秒かかりますが、そのうち0.005秒です。 リクエストの全列を表示します。

塗りつぶされた$ _table_columnsで -平均0.145秒。 成長3.3%



いくつかのレコードを選択し、関連するモデルを使用して、より実際のテストを作成します。

 $token = Profiler::start('Model', 'User'); for ($index = 0; $index < 10; $index++) { ORM::factory('user')->with('profile')->with('profile:file')->get_user(array(rand(1,100), rand(1,100))); } Profiler::stop($token); echo View::factory('profiler/stats');
      
      





$ _table_columns配列を埋めない場合、フレームワーク操作全体で平均0.18秒が費やされ、そのうち0.015秒がかかります。 データベースへのクエリを実行して、配列にテーブルフィールドを設定します。 したがって、成長は小さくなります-2.8%



もちろん、実際のプロジェクトでは、数値はコード自体に大きく依存し、ORMで動作します。 データベースクエリの数の予想される減少は、ORMを使用するプロジェクトでは1.5〜3倍であり、MySQLサーバーに大きな負荷がかかります。 ただし、リクエストは同じように繰り返され、MySQLはキャッシュされます。したがって、速度の特定の増加は2〜3%程度になります。



ソリューションの明らかなマイナス点が1つあります-ライブサーバーで動作し、並行して活発に開発されているプロジェクトでは、新しいフィールドをそれぞれ$ _table_columns配列に手動で追加し、新しいテーブルの配列全体を生成する必要があります。



PS記事の共著者-貪欲ではないunix44-は招待を与えることができます。



All Articles