MySQL de ビュー

 DBMSのビュー機能って言うのは、簡単にいえばSELECT文でテーブルの複製のようなものを構築する機能。

例えば、
orderテーブル

id
item_id
unit

itemテーブル

id
name
price

みたいなテーブルがあって、ビューを

CREATE VIEW order_view AS SELECT order.id, name, unit*price as total_price FROM order, item WHERE order.item_id = item.id

の様に定義しておくと、id, name, total_priceを持つビューができる。
 ビューはテーブル同様、 SELECT * FROM order_view の様に処理できる。

 CREATE TABLE ~ SELECT ~形式でテーブルを作ると似た結果になるが、CREATE TABLE ~ SELECT ~では、CREATE TABLEしたタイミングの情報がテーブルに保持されるのに対して、ビューはビューに対してクエリが投げられる度に、AS~の文を実行してデータを取得する。
 よく使う取得形式について、SELECT *で済ませる様に定義したり、制限ユーザにテーブル全項目ではなくカラムを限定してビューとして提供したり、内部構造の変更を吸収するレイヤーとして使うことができる。

 便利に思えるビューではあるが、色々と制限もある。
 例えば更新系クエリ(INSERTUPDATE)は、レコードが一意に元テーブルのレコード・カラムを特定できる事が最低条件である。
 先に示した定義の場合、total_priceunitpriceの演算により求められるカラムであり、total_priceが元テーブルにどのような値を与えるか不定であるから更新できない。
手の込んだ構造のビューは基本的に更新できない、見る(view)ためだけの物であると考えたほうが良い。
 また、ビューにはインデックスを設定することもできない(クエリを投げた段階でSELECTしてデータを取得するので、SELECT後にインデックスを構築して検索したとしても、コストが増えるだけ)ため、パフォーマンス的に弱いし、集計操作などにも癖がある。
 パフォーマンス制限について、一回の接続で繰り返し同じビューを利用するなら、ビューではなく、一時テーブル(CREATE TEMPORARY TABLE ~)を接続するたびに構築した方が高速な事もある(接続中はマスタの変更は反映されないし、更新系は結局、元テーブルに投げなければならないが)

(95)

カテゴリー: LAMP[Linux, Apache, MySQL, PHP], MySQL | コメントをどうぞ

PHP チューニングの基本

MySQLの時にも書いたけど、チューニング情報収集が大事(どこでどれだけ時間がかかっているか知る)
Webシステムでは、どのページがどれくらい時間がかかっているかというのを知った上で、そのページの中のどこが時間がかかっているかを調べる。

で、各ページの処理時間を簡単に表示するのに、
php.ini

auto_prepend_file
auto_apend_file

この設定を使うと便利。

これは、PHPのページアクセス時に、スクリプトに先立って自動実行されるファイルと、終了時に自動実行されるファイルを指定するものだ(exit命令で終了すると自動実行されない)
この設定は.htaccessでも設定できるので、開発ディレクトリに
.htaccess

php_value auto_prepend_file autoprepend.php
php_value auto_append_file autoappend.php

のように設定して、
autoprepend.php

<?php $time_start = microtime(true); ?>

autoappend.php

<?php echo(‘runtime:’.(microtime(true)-$time_start).’sec’); ?>

みたいなファイルを用意しておくと、ページの最後にruntime:4.6920776367188372secとか表示されるので、これが大きいページから処理していくと効率が良い。

なお、この自動実行ファイルはrequiredなので、ファイルがないとFatalErrorで落ちるので、include_pathに置いておくのが良い。

(203)

カテゴリー: LAMP[Linux, Apache, MySQL, PHP], PHP | コメントをどうぞ

PHP de オブジェクト指向 Part.2

PHP de オブジェクト指向part.2

この間のクラスに機能を追加してみる。

class SimpleDataObject
追記・変更

protected $_mask;

public function __construct($id){
$_mask[] = ‘id’;
foreach(get_object_vars($this) as $key => $val){
if(strpos($key, ‘_’)===0) $this->_mask[] = $key;
}
$this->id = $id;
}

public function getColumns(){
$ret = array();
foreach(get_object_vars($this) as $key => $val){
if(in_array($key, $this->_mask)) continue;
$ret[] = $key;
}
return $ret;
}

public function setByArray($array){
foreach($array as $key => $val){
try{
$this->set($key, $val);
}catch(Exception $e){
throw new Exception(‘[‘.__FUNCTION__.’]エラー:’.$e->getMessage());
}
}
return true;
}

で、前回作ったEstimateDataObjectを使って

$obj = new EstimateDataObject(1);
$cols = $obj->getColumns();
$sql = ‘SELECT ‘.implode(‘,’, $cols).’ FROM Estimates WHERE id=’.$obj->get(‘id’).’;’;
$res = mysql_query($sql);
$row = mysql_fetch_assoc($res);
$obj->setByArray($row);

こんな事をすると、EstimateDataObjectで定義した変数を列挙したSQL文を作って$objに読み込むことが出来る。
で、これを更新保存するには、

$obj->set(‘name’, $_GET[‘name’]);
$sql = ‘UPDATE Estimates SET’;
foreach($cols as $col){
$sql .= ‘ '.$col.'=’.$obj->get($col).’,’;
}
$sql = rtrim($sql, ‘,’);
$sql .= ‘ WHERE id=’.$obj->get(‘id’);
mysql_query($sql);

みたいなことで出来る。
EstimateDataObjectだけでなく、SimpleDataObjectを継承してプロパティを増やしたクラスもこのコードで対応できたりする。
便利便利

(92)

カテゴリー: LAMP[Linux, Apache, MySQL, PHP], PHP | コメントをどうぞ

PHP de オブジェクト指向機能

PHPのオブジェクト指向機能でシンプルな機能実装をする。

class SimpleDataObject{
protected $id;

public function __construct($id){
$this->id = $id;
}

public function set($name, $val){
if(property_exists($this, $name)) $this->{$name} = $val;
else throw new Exception(‘[‘.get_class($this).’]の定義されていないプロパティ[‘.$name.’]にアクセスしました’);
return true;
}

public function get($name){
if(property_exists($this, $name)) return $this->{$name};
else throw new Exception(‘[‘.get_class($this).’]の定義されていないプロパティ[‘.$name.’]にアクセスしました’);
return false;
}

public function __set($key, $val){
throw new Exception(‘[‘.get_class($this).’]の定義されていないプロパティ[‘.$name.’]にアクセスしました’);
}

public function __get($key){
throw new Exception(‘[‘.get_class($this).’]の定義されていないプロパティ[‘.$name.’]にアクセスしました’);
}

}

みたいなクラスを作る。
何のことはない、プロパティを保存してset/getで入出力するだけ。 その上、プロパティはidだけ。
これじゃ役に立たないが、継承して

class EstimateDataObject extends SimpleDataObject{
protected $name, $staff_code, $date;
}

のようにすると、parentのset/get機能で、こちらで定義したname/staff_code/dateを操作できる様になる。

親クラスに機能を追加してみる。

public function format($format){
$str = $format;
foreach(get_object_vars($this) as $key => $val){
$str = preg_replace(‘/%’.strtoupper($key).’%/’, $val, $str);
}
return $str;
}

これで、format(“あなたのIDは%ID%です”)とか流すと、「あなたのIDは1です」みたいな挙動をしてくれる。
get_object_varsは、対象オブジェクトのプロパティを列挙するので、EstimateDataObjectでは、%NAME%, %STAFF_CODE%, %DATE%も置換できる様になっている。
同じように、get_object_varsをうまく使うことで、基本クラスの単純な機能で、継承先の機能を回すことが出来る。

(89)

カテゴリー: LAMP[Linux, Apache, MySQL, PHP], PHP | コメントをどうぞ

mysql チューニング – 上物 explain mysqldumpslow

インフラ的なチューニング項目もあるけど、今回は上物のチューニング。

上物のチューニングといえば、「Wordpressでページ送りが表示されない」の記事で出てきたEXPLAIN文。
こいつは、SQL文の前にEXPLAINとつけて実行すると、そのSQL文が実行されるときの実行計画が表示される。

また、チューニングするには情報収集が大事で、クエリのログを取得するのが第一歩。
長時間を要するログを取得するのに、MySQLアップデート - epel/remiリポジトリでやったスロークエリログを取得するほかに、

my.cnf

log = /var/log/mysql-query.log

の様に記述することで、クエリ等のログを取得できるが、スロークエリの方が解析しやすいと思う。
で、取得したスローログは、mysqldumpslowコマンドで処理できる。

mysqldumpslow -s t all-sql.log >sorted.txt

の様に実行することで、総消費時間(実行時間x実行回数)順でソートしたSQL文一覧が出力されるので、これの上からチューニングを施していく。

SQLのチューニングと言えばインデックスを張ることで、ユニークなキー、クエリの要素を出来るだけ多く含んだキーを設定することで速度的に大きな恩恵が得られる事が多い。
そして、インデックスがどのように働くかというのを知る方法が、冒頭に述べたEXPLAIN文である。
sorted.txt

Count: 11108  Time=0.04s (451s)  Lock=0.00s (0s)  Rows=0.0 (425), est_bk[est]@localhost
SELECT price FROM est WHERE r_id = ‘S’ AND s_id =N LIMIT N

の様に出てきた場合、平均実行時間0.04秒のクエリが11108回実行されていると言うことになる。このSQL文を

EXPLAIN SELECT price FROM est WHERE r_id = ‘S’ AND s_id =0 LIMIT 10

等の様に実行する(S/Nは文字列・数値の代替文字なので、適宜数値などを設定する)

実行結果は

id select_type table type possible_keys
1 SIMPLE est ALL NULL
key key_len ref rows Extra
NULL NULL NULL 5935 Using where

のように出力される。
この場合、SIMPLE(UNION等を含まない)セレクトがestテーブルに実行されて、有効キーはなく5935行をALL(全域スキャン)を行う計画になっている。 全域スキャンはコストが高いので、インデックスを設定することで高速化できそう。
確認のため、インデックスを張る前に、EXPLAINを外して普通に実行すると、クエリの実行時間 0.0580 秒であった。

ALTER TABLE est ADD INDEX INDEX_R_S_PRICE ( r_id , s_id )

のようにしてインデックスを張って、再度、EXPLAIN文を実行すると

id select_type table type possible_keys
1 SIMPLE est ref INDEX_R_S_PRICE
key key_len ref rows Extra
INDEX_R_S_PRICE 44 const,const 1 Using where

のように、INDEX_R_S_PRICEが有効化された。
この状態で、再度、EXPLAINを外した文を実行すると、クエリの実行時間 0.0009 秒となった。
これで、インデックスを張る前よりも1.55%まで処理時間を短縮できた事になる。

同様の手法で、ログを上から片付けていけばDB動作の高速化が望める(但し、インデックスを張りすぎると、INSERT等の更新系処理で、インデックスを構築する負荷が増加するのに注意が必要)

(348)

カテゴリー: LAMP[Linux, Apache, MySQL, PHP], MySQL | コメントをどうぞ

基本コマンド3

jobs

バックグラウンドジョブを表示する。
バックグラウンドジョブは

コマンド &

の様に、最後に&を付けて実行することで、コマンドが完了する前に操作がコマンドラインに戻ってきて、実行されたコマンドはバックグラウンドジョブになる。 圧縮処理とか、時間がかかりそうなコマンドをバックグラウンド実行して、コマンドライン操作を続けたりする。

コマンドの実行中にctrl+zを入力すると、そのコマンドがバックグラウンドの待機状態になって、コマンドラインに戻る。

バックグラウンドジョブは

fg ジョブ番号
bg ジョブ番号
stop ジョブ番号

の3種類のコマンドで操作できて、fgならフォアグランド実行(通常の実行状態)に戻し、bgならバックグラウンド実行、stopなら待機状態にする。

ジョブは

kill %ジョブ番号

で停止することができる。

killコマンドは、送るシグナルの種類を指定できる(kill -SIGHUP, kill -SIGALRM等) 、サーバプログラム等では、このシグナルにより、リロードしたりする。

ジョブではなく、プロセスを表示するには

ps

コマンドを利用する。 よく使われるオプションはax(a:全ユーザ、x:端末制御外)で、httpd等のプロセスが表示されるので、基本コマンドの組み合わせで ps ax|grep httpd|wc等とすると、現在動作中のhttpdの数を知ることができる。

プロセスはジョブと同様、killコマンドで操作できる(kill プロセス番号)

(416)

カテゴリー: LAMP[Linux, Apache, MySQL, PHP] | コメントをどうぞ

基本コマンド2

grep
grep 検索語 検索ファイル
検索ファイル中の検索語を含む行を表示する。

grep hoge.com /var/log/httpd/*
cat /var/log/access*|grep hoge.com
のように、他から流し込むこともできる。

df
ディスクの空き状況を表示する。

free
メモリの空き状況を表示する。

wc
行数を知る。
grep error error_log|wc
みたいに繋ぐと、該当語がある行数を簡単に調べたり出来る

(85)

カテゴリー: LAMP[Linux, Apache, MySQL, PHP] | コメントをどうぞ