年末に初すべりを済ませてきたので、そのときに初めて乗った011 ArtisticのGULLWINGの感想です。
まず最初に思ったのはめっちゃ柔らかい!
オーリーがめっちゃしやすくて、高さも結構出ます。
バター系トリックも思ったとおり結構やりやすくていい感じです。
スピン系のトリックはまだ初すべりだったんでなんともいえない感じですが、
まぁそんなに問題がある感じではなかったです。
全体的にかなり調子いい感じなんですが1点だけ気になったのは、
ターンのときにエッジが刺さりすぎてるってとこです。
センター部分のエッジが雪面に引っかかりすぎて、思ったよりスピードが出ず、
バランスを崩してこけたりすることが何回かありました。
やっぱキャンバーとロッカーで構造が違うのでしょうがないですね。
その辺は慣れだと思いますんでこれから徐々に慣れて行きたいと思います。
とはいえかなり面白い!
相当楽しめる板であることには間違いないので、
今後はスピントリックを色々試してみて乗り味をレビューできたらと思います。
page_adsence
2010年12月30日木曜日
2010年12月27日月曜日
MySQLのmy.cnfの設定に関して
今日会社でたまたま話題に出たので調べてみた。
前の会社ではmy.cnfの設定なんて全く触ってなかったですが、
どういう設定がいいのか調べてみたら、結構いろいろ出てきた。
一応設定例を以下に示す。
ここにあったのをまるっと頂いてきました。
OLTPでメモリ8GB/InnoDBがメインの場合
OLTPでメモリ2GB/InnoDBがメインの場合
Webのバックエンドでメモリ2GB/MyISAMのみの場合
Linuxの場合は、sysctl.confにおいて以下の設定をすること。
といった感じ。
どういった基準でこうなったのかは全く書いてないので、
とりあえず今回話題に上がったinnodb_buffer_pool_sizeに関して調べてみた。
グローバルバッファ
InnoDBのデータやインデックスをキャッシュするためのメモリ上の領域
OS自身がキャッシュしない。
実装メモリの70-80%を目安に設定
innodb_buffer_pool_size=XXX
テーブルやインデックスデータをメモリに展開する際の上限
これを適正な値に引き上げることにより
Disk I/O が減らせ全体的なパフォーマンスアップにつながる。
ってことらしい。
http://www.sapa.ne.jp/manual-mysql51/storage-engines.html
このページ見ると「専用のデータベース サーバ上で、これをマシンの物理的メモリ サイズの最大80% に設定すると良いでしょう。」って書いてあるけど、
「物理的メモリの競合が OS 内でページングを引き起こす可能性があるので、あまり大きく設定しないでください。」とも書いてあって、結局どの位が適正なのかはわからない。
前の会社ではmy.cnfの設定なんて全く触ってなかったですが、
どういう設定がいいのか調べてみたら、結構いろいろ出てきた。
一応設定例を以下に示す。
ここにあったのをまるっと頂いてきました。
OLTPでメモリ8GB/InnoDBがメインの場合
[mysqld]
# basic settings
port = 3306
socket = /tmp/mysql.sock
user = mysql
character-set-server = utf8
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /var/tmp/mysql
core-file
# buffers
key_buffer = 128M
max_allowed_packet = 16M
table_cache = 1024
sort_buffer_size = 1M
read_buffer_size = 1M
read_rnd_buffer_size = 2M
myisam_sort_buffer_size = 64M
max_connections = 512
thread_cache_size = 128
query_cache_size = 0
# binlog and replication
log-bin = mysql-bin
server-id = 1
max_binlog_size = 256M
# if the system is the master, comment out the following line.
# sync_binlog=1
# InnoDB configurations
innodb_buffer_pool_size=6G
innodb_additional_mem_pool_size=20M
innodb_flush_method=O_DIRECT
innodb_log_file_size=256M
innodb_log_files_in_group=2
innodb_log_buffer_size=64M
innodb_flush_log_at_trx_commit=1
OLTPでメモリ2GB/InnoDBがメインの場合
[mysqld]
# basic settings
port = 3306
socket = /tmp/mysql.sock
user = mysql
character-set-server = utf8
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /var/tmp/mysql
core-file
# buffers
key_buffer = 64M
max_allowed_packet = 10M
table_cache = 512
sort_buffer_size = 1M
read_buffer_size = 256K
read_rnd_buffer_size = 1M
myisam_sort_buffer_size = 64M
max_connections = 300
thread_cache_size = 100
query_cache_size = 0
# binlog and replication
log-bin = mysql-bin
server-id = 1
max_binlog_size = 256M
# if the system is the master, comment out the following line.
# sync_binlog=1
# InnoDB configurations
innodb_buffer_pool_size=1400M
innodb_additional_mem_pool_size=20M
innodb_flush_method=O_DIRECT
innodb_log_file_size=256M
innodb_log_files_in_group=2
innodb_log_buffer_size=64M
innodb_flush_log_at_trx_commit=1
Webのバックエンドでメモリ2GB/MyISAMのみの場合
[mysqld]
# basic settings
port = 3306
socket = /tmp/mysql.sock
user = mysql
character-set-server = utf8
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /var/tmp/mysql
core-file
# buffers
key_buffer = 512M
max_allowed_packet = 10M
table_cache = 4096
sort_buffer_size = 1M
read_buffer_size = 256K
read_rnd_buffer_size = 1M
myisam_sort_buffer_size = 64M
max_connections = 512
thread_cache_size = 128
query_cache_size = 64
query_cache_type = ON
# binlog and replication
log-bin = mysql-bin
server-id = 1
max_binlog_size = 256M
# if the system is the master, comment out the following line.
# sync_binlog=1
skip-innodb
Linuxの場合は、sysctl.confにおいて以下の設定をすること。
といった感じ。
どういった基準でこうなったのかは全く書いてないので、
とりあえず今回話題に上がったinnodb_buffer_pool_sizeに関して調べてみた。
グローバルバッファ
InnoDBのデータやインデックスをキャッシュするためのメモリ上の領域
OS自身がキャッシュしない。
実装メモリの70-80%を目安に設定
innodb_buffer_pool_size=XXX
テーブルやインデックスデータをメモリに展開する際の上限
これを適正な値に引き上げることにより
Disk I/O が減らせ全体的なパフォーマンスアップにつながる。
ってことらしい。
http://www.sapa.ne.jp/manual-mysql51/storage-engines.html
このページ見ると「専用のデータベース サーバ上で、これをマシンの物理的メモリ サイズの最大80% に設定すると良いでしょう。」って書いてあるけど、
「物理的メモリの競合が OS 内でページングを引き起こす可能性があるので、あまり大きく設定しないでください。」とも書いてあって、結局どの位が適正なのかはわからない。
CentOS5.5にSubversionを導入
社内でバージョン管理されていないプロジェクトが以外と多いことを知ったので、
とりあえず自分だけでもバージョン管理をしようと思い、
VMWare上のCentOSにSubversionをインストールすることにした。
今回の要件はこんな感じ。
・subversionのインストール
・WebDAV経由でリポジトリにアクセスできるようにする
・ディレクトリごとに細かくアクセス制限をかける
リポジトリ内のディレクトリ構成は以下のような感じを想定して作ってみる。
今回自分の環境ではもともとsubversionがインストール済みであったが、
念のためインストールからの手順を示す。(rootユーザー)
といってもyumでインストールするだけなので、特に問題ないかと。
インストールが完了したら、リポジトリ用のディレクトリを切ってからリポジトリの作成
apache経由でアクセスするのでapacheから書き込めるようにオーナーをapacheにしておく。
そっからWebDAVでアクセスできるようにする。
ここでちょっとはまった。
いろんなサイトを見て回ったけど、モジュールの読み込み部分について触れてないブログが結構多くて、
gracefulで文法チェックするとエラーになってしまっていた。
WebDAVを使用する場合は以下の「mod_dav_svn.so」のモジュールを読み込んであげないとエラーになってしまう。
また、「mod_authz_svn.so」はディレクトリごとに細かくアクセス制限をかけることができるようになるモジュール。
ユーザーを作成し、ディレクトリごとに読み書きを制御できるようになる。
個人で使用する分にはまず必要ないが、勉強のためこれも組み込む。
ディレクトリごとにアクセス制限をかけるための設定ファイル(authzsvn.conf)をsubversion.confで読み込むので、
まずはそのファイルの作成をする。
この設定ファイルに関してのドキュメントが見つからなかったので、とりあえずコピペしたのをおく。
問題なさそうならapacheの設定ファイルをリロード。
これで一応WebDAV経由でアクセスできるようになったはず。
参考文献
http://park1.wakwak.com/~ima/centos4_subversion0001.html
とりあえず自分だけでもバージョン管理をしようと思い、
VMWare上のCentOSにSubversionをインストールすることにした。
今回の要件はこんな感じ。
・subversionのインストール
・WebDAV経由でリポジトリにアクセスできるようにする
・ディレクトリごとに細かくアクセス制限をかける
リポジトリ内のディレクトリ構成は以下のような感じを想定して作ってみる。
/path/to/dir
└ projects
├ project_name
│ ├ trunk
│ ├ branches
│ └ tags
│
└ project_name
:
:
今回自分の環境ではもともとsubversionがインストール済みであったが、
念のためインストールからの手順を示す。(rootユーザー)
といってもyumでインストールするだけなので、特に問題ないかと。
# yum install subversion ← Subversion本体
# yum install mod_dav_svn ← WebDAV
インストールが完了したら、リポジトリ用のディレクトリを切ってからリポジトリの作成
apache経由でアクセスするのでapacheから書き込めるようにオーナーをapacheにしておく。
# mkdir /path/to/dir/projects
chown apache:apache /path/to/dir/projects
# svnadmin create /path/to/dir/projects
そっからWebDAVでアクセスできるようにする。
ここでちょっとはまった。
いろんなサイトを見て回ったけど、モジュールの読み込み部分について触れてないブログが結構多くて、
gracefulで文法チェックするとエラーになってしまっていた。
WebDAVを使用する場合は以下の「mod_dav_svn.so」のモジュールを読み込んであげないとエラーになってしまう。
また、「mod_authz_svn.so」はディレクトリごとに細かくアクセス制限をかけることができるようになるモジュール。
ユーザーを作成し、ディレクトリごとに読み書きを制御できるようになる。
個人で使用する分にはまず必要ないが、勉強のためこれも組み込む。
ディレクトリごとにアクセス制限をかけるための設定ファイル(authzsvn.conf)をsubversion.confで読み込むので、
まずはそのファイルの作成をする。
# vi /path/to/dir/project/authzsvn.conf
[groups] ← グループの管理をしている部分の記述開始
#開発者
developers = kusagaya ← グループ名 = ユーザー名
test = userA, userB, userC
#管理者
manager = kusagaya
[/] ← ディレクトリごとのアクセス制限
#すべての人が読み込み可能
* = r
[projects:/trunk]
#開発者は読み書き可能
@developers = rw
[projects:/branches]
#管理者は書き込みも可能
@manager = rw
[projects:/tags]
#管理者は書き込みも可能
@manager = rw
この設定ファイルに関してのドキュメントが見つからなかったので、とりあえずコピペしたのをおく。
# vi /etc/httpd/conf.d/subversion.conf
LoadModule dav_svn_module modules/mod_dav_svn.so
LoadModule authz_svn_module modules/mod_authz_svn.so
<VirtualHost *>
ServerAdmin your_mail_address
DocumentRoot /path/to/dir/projects
ServerName your_server_name
ErrorLog /path/to/dir/logs/error_log
CustomLog /path/to/dir/logs/access_log common
<Location /svn>
DAV svn
SVNPath /path/to/dir/projects←svnadminコマンドで作成したリポジトリのディレクトリ
# our access control policy
AuthzSVNAccessFile /path/to/dir/projects/authzsvn.conf←mod_authz_svnによるアクセス制限の設定ファイルのパス
以下はただのBasic認証
# only authenticated users may access the repository
Require valid-user
# how to authenticate a user
AuthType Basic
AuthName "Subversion repository(sample)"
AuthUserFile /path/to/dir/projects/.htpasswd
</Location>
</VirtualHost>
問題なさそうならapacheの設定ファイルをリロード。
# /etc/rc.d/init.d/httpd reload
これで一応WebDAV経由でアクセスできるようになったはず。
参考文献
http://park1.wakwak.com/~ima/centos4_subversion0001.html
2010年12月22日水曜日
PHP5のabstract class(抽象クラス)に関して
PHP5から追加された機能に抽象クラスを作成できるようになったが、
今まで全く使っていなかったので、ちょっと勉強してみた。
抽象クラスはインスタンス化できないクラスで、必ず継承して使用するようにしているクラスのこと。
抽象メソッドは、抽象クラス内に定義しておくことで、継承クラス内で必ずその関数を作らないといけないようになり、抽象クラスを拡張した形で継承クラス内に処理を記述することができる。
参考文献
http://f3.aaa.livedoor.jp/~matukazu/php5/php5_abstract.php
今まで全く使っていなかったので、ちょっと勉強してみた。
抽象クラスはインスタンス化できないクラスで、必ず継承して使用するようにしているクラスのこと。
抽象メソッドは、抽象クラス内に定義しておくことで、継承クラス内で必ずその関数を作らないといけないようになり、抽象クラスを拡張した形で継承クラス内に処理を記述することができる。
参考文献
http://f3.aaa.livedoor.jp/~matukazu/php5/php5_abstract.php
oracleのインストール(Linux版)
初oracleインストールしたので、その時のメモ。
基本的には以下の通り。
http://blog.goo.ne.jp/h-mori/e/132867c006097f60a35d7ec937dbaed5
最初に設定されているユーザーがrootじゃなくてsystemユーザーってことにビックリ。
ユーザー名がわからなくて、ログインできないかと思った…。
基本的には以下の通り。
http://blog.goo.ne.jp/h-mori/e/132867c006097f60a35d7ec937dbaed5
最初に設定されているユーザーがrootじゃなくてsystemユーザーってことにビックリ。
ユーザー名がわからなくて、ログインできないかと思った…。
2010年12月21日火曜日
PHP5のfgetcsvに関して
PHP5でShift-JISでエンコードされているcsvファイルをfgetcsvを使うと文字化けしてしまうらしい。
文字コードがSJISのデータを処理させた時に、「"機能"」などというデータがあった場合、「能」の2バイト目が「5C(\)」であるため「"」の部分が「\"」と扱われてエスケープされてしまい、データが正しく取得できないのだ。エスケープ文字として処理された結果「5C」の部分は消えてしまい、結果、データが文字化けしてしまう。
こういった現象を回避するためにyossyさんという方が、fgetcsv_regというバグを取った版の関数を作ってくれている。
が、壊れたcsvや画像ファイルを無理やり読み込ませると無限ループに陥るという欠点がある。
それを修正した版は以下の通り。
使い方は以下の通り。
文字コードがSJISのデータを処理させた時に、「"機能"」などというデータがあった場合、「能」の2バイト目が「5C(\)」であるため「"」の部分が「\"」と扱われてエスケープされてしまい、データが正しく取得できないのだ。エスケープ文字として処理された結果「5C」の部分は消えてしまい、結果、データが文字化けしてしまう。
こういった現象を回避するためにyossyさんという方が、fgetcsv_regというバグを取った版の関数を作ってくれている。
が、壊れたcsvや画像ファイルを無理やり読み込ませると無限ループに陥るという欠点がある。
それを修正した版は以下の通り。
<?php
/**
* ファイルポインタから行を取得し、CSVフィールドを処理する
* @param resource handle
* @param int length
* @param string delimiter
* @param string enclosure
* @return ファイルの終端に達した場合を含み、エラー時にFALSEを返します。
*/
function fgetcsv_reg (&$handle, $length = null, $d = ',', $e = '"') {
$d = preg_quote($d);
$e = preg_quote($e);
$_line = "";
while (($eof != true)and(!feof($handle))) {
$_line .= (empty($length) ? fgets($handle) : fgets($handle, $length));
$itemcnt = preg_match_all('/'.$e.'/', $_line, $dummy);
if ($itemcnt % 2 == 0) $eof = true;
}
$_csv_line = preg_replace('/(?:\\r\\n|[\\r\\n])?$/', $d, trim($_line));
$_csv_pattern = '/('.$e.'[^'.$e.']*(?:'.$e.$e.'[^'.$e.']*)*'.$e.'|[^'.$d.']*)'.$d.'/';
preg_match_all($_csv_pattern, $_csv_line, $_csv_matches);
$_csv_data = $_csv_matches[1];
for($_csv_i=0;$_csv_i<count($_csv_data);$_csv_i++){
$_csv_data[$_csv_i]=preg_replace('/^'.$e.'(.*)'.$e.'$/s','$1',$_csv_data[$_csv_i]);
$_csv_data[$_csv_i]=str_replace($e.$e, $e, $_csv_data[$_csv_i]);
}
return empty($_line) ? false : $_csv_data;
}
?>
使い方は以下の通り。
<?php
$row = 1;
$handle = fopen("test.csv", "r");
while (($data = fgetcsv_reg($handle)) !== false) {
$_enc_to=mb_internal_encoding();
$_enc_from=mb_detect_order();
mb_convert_variables($_enc_to,$_enc_from,$data);
$num = count($data);
echo "$num fields in line $row:
\n";
$row++;
for ($c=0; $c < $num; $c++) {
echo nl2br($data[$c]) . "
\n";
}
}
fclose($handle);
?>
携帯のFormタグのaction属性に関して
PCブラウザでactionが空の場合は、自分自身にリクエストを送るような感じになっているが、
携帯の場合はそうもいかないらしい。
例)actionが空で/hoge/fuga.phpのformをsubmitした場合
PC…/hoge/fuga.phpにリクエストが飛ぶ
携帯…/hoge/にリクエストが飛ぶ
SoftbankのC型と言われる携帯だけと書いてある記事を見つけたが、
911SHでも再現したので、携帯でactionを省略することはご法度っぽい。
まぁ、そもそも今までFormタグのactionを空でも動くってことを知らなかったし、
今回たまたま人の書いたソースのメンテをすることになって初めて知ることができた。
規約的にもactionを空で動かそうとするってのは間違っている気がするので、
今後もきちんとactionは書いていこうと思う。
携帯の場合はそうもいかないらしい。
例)actionが空で/hoge/fuga.phpのformをsubmitした場合
PC…/hoge/fuga.phpにリクエストが飛ぶ
携帯…/hoge/にリクエストが飛ぶ
SoftbankのC型と言われる携帯だけと書いてある記事を見つけたが、
911SHでも再現したので、携帯でactionを省略することはご法度っぽい。
まぁ、そもそも今までFormタグのactionを空でも動くってことを知らなかったし、
今回たまたま人の書いたソースのメンテをすることになって初めて知ることができた。
規約的にもactionを空で動かそうとするってのは間違っている気がするので、
今後もきちんとactionは書いていこうと思う。
SoftbankのUIDの取得に関して
初めて携帯コンテンツ作っている時にはまったので、メモ。
既存の管理画面を改修して作ったはずなのに、ソフトバンクの携帯のUIDが取れなかった。
で取得できるってどのサイトにも書いてあったんですが、なぜか取得できない…。
調べてみたら、SSLの直リンクの場合はUIDが取得できないみたい。
なので、いったん非SSLのページからSSLのページに遷移する必要がある。
UID関係は結構落とし穴が多いみたい。
参考文献
SSL利用時のHTTP_X_JPHONE_UIDの落とし穴
既存の管理画面を改修して作ったはずなのに、ソフトバンクの携帯のUIDが取れなかった。
$uid = $_SERVER["HTTP_X_JPHONE_UID"];で取得できるってどのサイトにも書いてあったんですが、なぜか取得できない…。
調べてみたら、SSLの直リンクの場合はUIDが取得できないみたい。
なので、いったん非SSLのページからSSLのページに遷移する必要がある。
UID関係は結構落とし穴が多いみたい。
参考文献
SSL利用時のHTTP_X_JPHONE_UIDの落とし穴
MySQLを高速化する方法
MySQLのチューニングに関してよさそうな記事があったので、とりあえずコピってみた。
* innodb_buffer_pool_size・・・InnoDBだけを利用する場合は空きメモリの7〜8割程度を割り当てる最も重要なバッファである。余談だが、実際にはここで割り当てた値の5〜10%ぐらいを多めにメモリを使うので注意が必要だ。
* key_buffer_size・・・MyISAMだけを利用する場合は、空きメモリの3割程度を割り当てるといい。残りはファイルシステムのキャッシュ用に残しておこう。
* sort_buffer_size・・・ソート処理に利用するバッファである。OLTPでは256K〜1Mぐらいを割り当てると良い。これがあまり大きすぎると、メモリの割り当てのオーバーヘッドが大きくなるので注意しよう。DWH系の処理などで大きなソートが必要な場合、セッションごとに動的に調整すると良い。
* read_buffer_size・・・全件スキャンをするときに利用するバッファ。OLTPでは128K〜512Kぐらいを割り当てると良い。
* read_rnd_buffer_size・・・ソート処理でインデックスを利用する場合に利用するバッファ。OLTPでは256K〜1MぐらいをDWH系の処理などで大きなソートが必要な場合、セッションごとに動的に調整すると良い。
バッファは増やせば増やすほどいいかと言えばそうではない。メモリの割り当てがオーバーヘッドになるので、無駄に大きくし過ぎることは禁物である。また、バッファを増やしすぎたためにスワップが発生するとパフォーマンスが悲惨なことになるのでくれぐれも空きメモリ容量には注意しよう。
前述のようにバッファを大きくするとディスクI/Oの回数や量が減るので、必ずしも高速なディスクが性能を向上させるというわけではないが、データサイズが大きくてバッファに収まりきらない場合などにはどうしてもI/Oが大量に発生してしまう。そんな時は高速なディスク装置を利用するといい。
実は最も大事なのがクエリの最適化である。いくら他の部分を最適化したところで、毎回全件スキャンが発生していたのでは話にならない。適切にインデックスを使ったり、サブクエリをJOINに書き換えたりすることで、フェッチしないといけない行数ができるだけ少なくなるようにクエリを書きかえよう。クエリを最適化するには、まずEXPLAINで実行計画をチェックしよう。EXPLAINの見方についてはいずれ解説しようと思う。
また、テーブルから全件フェッチしてからアプリケーション側で行を絞り込むというようなロジックを実装してはいけない。必ずSQL文、つまりWHERE句で行の絞り込みができるようにしよう。
クエリを手当たり次第チューニングしていてはいくら時間があっても足りないだろう。問題のあるクエリだけをチューニングするべきであるが、そのようなクエリを見付けるにはスロークエリログや商用のクエリアナライザを用いると効果的である。
基本中の基本は、適切なデータタイプを使うということである。できるだけカラムサイズが小さくなるようなデータタイプを選ぼう。数値をVARCHAR(桁数)などのデータタイプで格納しているのをたまに見かけるが、これは誤りである。INTまたはBIGINTなどを利用したほうがずっとデータサイズが小さくなるし高速である。
また、適切なカラムに対してインデックスをつけるのも重要である。どのカラムにインデックスをつけるかは、クエリのパターンに因る。インデックスが多すぎると更新時のオーバーヘッドが大きくなるだけでなく、インデックスツリーを格納するためのデータ容量が増えてしまうので、インデックスのつけすぎには注意しよう。たまに全てのカラムにインデックスがついているテーブルを見かけるが、そのようなテーブル設計は誤りである。クエリのパターンによっては、マルチカラムインデックスやパーティショニングが必要になるなどいろいろと工夫が必要になる。
カラム数がが多くなりすぎたら、まずは正規化できるかどうかを検討してみて欲しい。DWH用途などでは逆に非正規化すると性能が向上する場合がある。
これはMySQLの醍醐味である。ストレージエンジンはそれぞれ性能特性がまったく違うので、目的に合ったストレージエンジンを選択すると劇的に性能が向上する場合がある。例えば、OLTPではInnoDB、参照系が多い場合はMyISAM、ログ目的であればARCHIVE、リアルタイム並列処理であれば NDBCLUSTERなど。他にもSun/MySQL以外のサードベンダーやコミュニティからリリースされているストレージエンジン(SPIDER、 PBXT、XtraDB、Q4M、Infobright、Kickfireなど)もあるので、目的に合わせて色々検討してみるといいだろう。
MySQLほどお手軽に、そして安価にレプリケーションを利用出来るRDBMSは他にないだろう。レプリケーションを用いてたくさんのスレーブへ参照系の処理を負荷分散するテクニックは、Webサイトなどで頻繁に利用されているテクニックである。参照系の負荷分散を行う場合だけでなく、例えばOLTPのデータを元にBIなどの処理を毎日行う場合などにも有効である。スレーブ上でBIを行えば、マスター上のOLTP系の処理に影響を与えることがない。
残念ながら、MySQLはストアドプロシージャ、ストアドファンクション、トリガなどの性能はあまりよくない。出来るだけそれらを利用せずに、ロジックをアプリケーション側に持っていくといいだろう。
Linuxであればデフォルトはext3(そろそろext4になっていくだろうか?)であるが、ext3ではなくXFSを利用すると性能が向上する場合がある。また、I/Oスケジューラを変更することで、同じext3であっても性能特性が変化する。SolarisではUFS、ZFS、QFSなどの利用を検討するといいだろう。WindowsならNTFS以外にあまり選択肢はないが、MyISAMの場合はLargeSystemCacheを有効にするなどのチューニングが必要である。
アプリケーションがDB操作が必要なときに都度MySQLサーバへ接続していたのでは、接続のためのオーバーヘッドが無視出来なくなる。そんなときはコネクションプールを利用するといい。
どんなチューニングでも、実際に効果があるかどうかは測定してみるまで分からない。また、あるアプリケーションで効果があるチューニングでも、他のアプリケーションの負荷パターンでは逆効果になってしまうということは多々ある。なので、アプリケーションの負荷を擬似的に作り出してチューニングの効果を測定することはとても重要なのである。
ちょうどSourceForge.JP Magazineで「インテル コンパイラーの実力を測る――インテル コンパイラー版MySQLは本当に速いのか?」という記事が書かれているので参照して欲しい。こちらではベンチマークも行っている。
ちなみに、SolarisにはSunStudioというサン・マイクロシステムズ純正のコンパイラがあり、こちらのコンパイラで作成したバイナリもGCCより高速になる場合が多い。残念ながら、SunStudioで作成されたバイナリは配布されていないので、SunStudioを試したい人は自分でコンパイルしよう。SunStudioは無料で配布されている。
MySQL 6.0ではさらにスレッドプールが搭載される予定である。InnoDB内部の同時実行スレッド数だけでなく、MySQLサーバの接続スレッドの同時実行数を調節することができるようになる。例えば、同時に実行するスレッド数を100までに限定したい場合には次のように指定する。
thread_handling=pool-of-threads
thread_pool_size=100
現時点ではLOCK_event_loopの競合が発生するという問題があるためスレッドプールによる性能向上は見込めないが、正式版がリリースするまでには問題は解消されスレッドプールによって同時実行性能が改善することだろう。ちなみに、MySQL 6.0のスレッドプールはlibeventで実装されている。スレッドプールもMySQL 6.0のリリースを楽しみにさせてくれる要素の一つである。
また、更新のサイズがInnoDBのログサイズより大きい場合には、COMMITの前にテーブルスペース内にUndoログを大量に作成する必要が生じるため、I/O性能が低下するという問題が発生する。I/O性能だけでなく、不要になったUNDOログはパージスレッドが回収するまでテーブルスペース内に残り続けてしまうので容量も圧迫してしまう。LOAD DATA IN FILEなどで一度に大量のデータを更新するのは避けた方がいいだろう。
もし新たなInnoDBテーブルにCSVファイルからデータを大量にロードしたい場合にはLOAD DATAコマンドを利用せず、CSVストレージエンジンからInnoDBテーブルにALTER TABLEするという技が存在するのでお勧めである。ALTER TABLE時には1万行ごとにCOMMITしてくれるので、上記のようなUNDOログの問題は発生しない。
MySQL Performance Blogにおいてfifoを使ってLOAD DATAを小刻みに行う方法が紹介されているので、興味のある人は参照して欲しい。
InnoDBのログサイズ調節方法については過去の投稿で説明したので参照して欲しい。
* 5.0 GA ... greedyオプティマイザ、Index Merge、InnoDBのTRUNCATE性能改善、NDBのCondition Pushdownなど。
* 5.0.30 ... InnoDBバッファプールのロック改善
* 5.0.50 ... クエリキャッシュのパフォーマンス改善
* 5.0.54 ... InnoDBのCPUスケーラビリティの向上
* 5.1.x ... InnoDBのAUTO-INCロックの性能改善
MySQL 5.0.30と5.0.54で行われた改善については、マイコミジャーナルの記事で詳細な説明が行われているので参照して欲しい。
さらにMySQL 6.0では性能に関するものとして次の新機能が追加される予定である。
* BKA JOIN
* Multi Read Range
* Index Condition Pushdown
* サブクエリ最適化
* スレッドプール(前述)
単純なクエリのレスポンスタイムはあまり変わらないかも知れないし、処理の内容によってはバージョン間で得手不得手が存在するが、システム全体で見た時のスループットは着実に向上しつつある。
ただし、ステートメントベースレプリケーション(SBR)しか利用出来ないMySQL 5.0以前のバージョンでは、innodb_locks_unsafe_for_binlogオプション利用時にバイナリログの一貫性を保証できないという問題があった。(そしてinnodb_locks_unsafe_for_binlog利用時には同時実行性能が改善しないというオチがある。)MySQL 5.1では行ベースレプリケーション(RBR)が追加されたため、同時実行性能を保ちつつバイナリログの一貫性を保証することが可能になっている。ただしこの変更により、READ COMMITTED分離レベル利用時またはinnodb_locks_unsafe_for_binlogオプションが有効になっている場合にはRBRの利用が必須となった。
READ COMMITTEDが利用出来るのはどのような場合か?明確なのは一回のトランザクションで複数の処理を行う必要がない場合である。例えばSNSサイトの日記データなどはREAD COMMITTEDで問題ないだろう。更新は日記の文章をINSERT、参照は最新のデータのSELECTをそれぞれ一回行うだけで実装出来るため、一回のトランザクション内で複数の処理を行う機会はあまりない。多くのWebアプリケーションでは一回のリクエストが一回のクエリに対応する。従って、多くの WebアプリケーションでREAD COMMITTEDが利用出来るだろう。
ちなみに、利用出来る分離レベルはストレージエンジンによって異なる。他のストレージエンジン、例えばMySQL ClusterではREAD COMMITTED分離レベルしか利用出来ない。 FalconではREAD COMMITTED、REPEATABLE READ、SERIALIZABLEをサポートしている。
READ COMMITTEDの有効性については別途紹介したい。
SHOW STATUSコマンドで気をつけるべき主な変数は次の通り。
* Created_tmp_disk_tables・・・ディスク上のテンポラリテーブルが作成された回数。
* Handler_read_first・・・フルインデックススキャンの回数。
* (1-Key_reads/Key_read_requests)・・・MyISAMキーバッファのキャッシュヒット率。
* Opened_tables・・・テーブルがOpenされた回数。急激に増えている場合にはテーブルキャッシュを増やす。
* Qcache_*・・・クエリキャッシュ関係。Qcache_insertsに対してQcache_hitsが少ないとヒット率が悪い。
* Select_full_join・・・2つ以上のテーブルにおいて全件同士でJOINした回数。最も致命的な兆候。
* Select_full_range_join・・・片方のテーブルで全件、もう片方のテーブルで範囲検索を行ってJOINした回数。
* Select_scan・・・全件スキャンの回数。
* Sort_scan・・・全件スキャンによるソートの回数。
* Threads_created・・・スレッドが作成された回数。急激に増えている場合にはスレッドキャッシュを増やす。
クエリのチューニングにおける基本コマンドはあくまでもEXPLAINであるが、クエリの実行状況を詳細に確認したい場合にはプロファイリング(SHOW PROFILEコマンド)を利用するといい。プロファイリングについては過去の投稿「プロファイリングで快適MySQLチューニング生活」で解説したのでそちらを参照して頂きたい。
大規模なWebサイトなどで劇的にトラフィックやデータ量が増えてくると、単一のデータベースでは対応しきれなくなってくる。そのような場合、特定のカラムなどによってデータの格納先を振り分けるテクニックが利用されることが多いのだが、そのようなテクニックをShardingと呼ぶ。(MixiのひとはLevel2分散と呼んでいるらしい。)とはいっても、Shardingが必要になるほど大規模なデータベースはどちらかといえば少数派なので、あまり一般的なケースには当てはまらないかも知れないが、そのようなニーズがある人達のためにオマケとしてMySQLにおけるShardingのソリューションを紹介しておこうと思う。
MySQL Cluster・・・サン・マイクロシステムズが提供するオフィシャルなストレージエンジンの一つである。主キーに基づいて内部的にShardingを行うので、ユーザはShardingについて意識する必要がない。
SPIDER・・・STグローバルの斯波氏によるストレージエンジンである。MySQLユーザコンファレンス2009のスライドではShardingという単語は一切登場しなかったが、SPIDERストレージエンジンがやってることはまごう事なきShardingである。SPIDERを利用するとテーブル定義以外でユーザがShardingについて意識する必要がないので便利である。
Spock Proxy・・・MySQL ProxyをベースにしたShardingソフトウェア。Luaスクリプトを利用せずにC/C++でShardingのロジックを実装しているためとても速い。今のところMySQL ProxyではShardingを実装することは出来ない。
その他・・・Hibernate Shards、HiveDB、HBase、BigTable、HScaleなど。
1. バッファを増やす、または減らす
チューニングの基本中の基本であるが、適切なバッファサイズを設定することはパフォーマンスチューニングの要である。主なバッファは次の通り。* innodb_buffer_pool_size・・・InnoDBだけを利用する場合は空きメモリの7〜8割程度を割り当てる最も重要なバッファである。余談だが、実際にはここで割り当てた値の5〜10%ぐらいを多めにメモリを使うので注意が必要だ。
* key_buffer_size・・・MyISAMだけを利用する場合は、空きメモリの3割程度を割り当てるといい。残りはファイルシステムのキャッシュ用に残しておこう。
* sort_buffer_size・・・ソート処理に利用するバッファである。OLTPでは256K〜1Mぐらいを割り当てると良い。これがあまり大きすぎると、メモリの割り当てのオーバーヘッドが大きくなるので注意しよう。DWH系の処理などで大きなソートが必要な場合、セッションごとに動的に調整すると良い。
* read_buffer_size・・・全件スキャンをするときに利用するバッファ。OLTPでは128K〜512Kぐらいを割り当てると良い。
* read_rnd_buffer_size・・・ソート処理でインデックスを利用する場合に利用するバッファ。OLTPでは256K〜1MぐらいをDWH系の処理などで大きなソートが必要な場合、セッションごとに動的に調整すると良い。
バッファは増やせば増やすほどいいかと言えばそうではない。メモリの割り当てがオーバーヘッドになるので、無駄に大きくし過ぎることは禁物である。また、バッファを増やしすぎたためにスワップが発生するとパフォーマンスが悲惨なことになるのでくれぐれも空きメモリ容量には注意しよう。
2. 高速なディスクを利用する
MySQLだけに限った話ではないが、RDBMSのボトルネックは99.99999%がディスクI/Oである。特にディスクのシークタイムによる待ち時間が大きい。理想的にはバッテリーバックアップ付きのRAID装置を利用するのがいい。最近はRAID装置に匹敵するほど高速なSSDが出てきているので楽しみである。前述のようにバッファを大きくするとディスクI/Oの回数や量が減るので、必ずしも高速なディスクが性能を向上させるというわけではないが、データサイズが大きくてバッファに収まりきらない場合などにはどうしてもI/Oが大量に発生してしまう。そんな時は高速なディスク装置を利用するといい。
3. クエリを最適化する
実は最も大事なのがクエリの最適化である。いくら他の部分を最適化したところで、毎回全件スキャンが発生していたのでは話にならない。適切にインデックスを使ったり、サブクエリをJOINに書き換えたりすることで、フェッチしないといけない行数ができるだけ少なくなるようにクエリを書きかえよう。クエリを最適化するには、まずEXPLAINで実行計画をチェックしよう。EXPLAINの見方についてはいずれ解説しようと思う。
また、テーブルから全件フェッチしてからアプリケーション側で行を絞り込むというようなロジックを実装してはいけない。必ずSQL文、つまりWHERE句で行の絞り込みができるようにしよう。
クエリを手当たり次第チューニングしていてはいくら時間があっても足りないだろう。問題のあるクエリだけをチューニングするべきであるが、そのようなクエリを見付けるにはスロークエリログや商用のクエリアナライザを用いると効果的である。
4. テーブルを最適化する
基本中の基本は、適切なデータタイプを使うということである。できるだけカラムサイズが小さくなるようなデータタイプを選ぼう。数値をVARCHAR(桁数)などのデータタイプで格納しているのをたまに見かけるが、これは誤りである。INTまたはBIGINTなどを利用したほうがずっとデータサイズが小さくなるし高速である。
また、適切なカラムに対してインデックスをつけるのも重要である。どのカラムにインデックスをつけるかは、クエリのパターンに因る。インデックスが多すぎると更新時のオーバーヘッドが大きくなるだけでなく、インデックスツリーを格納するためのデータ容量が増えてしまうので、インデックスのつけすぎには注意しよう。たまに全てのカラムにインデックスがついているテーブルを見かけるが、そのようなテーブル設計は誤りである。クエリのパターンによっては、マルチカラムインデックスやパーティショニングが必要になるなどいろいろと工夫が必要になる。
カラム数がが多くなりすぎたら、まずは正規化できるかどうかを検討してみて欲しい。DWH用途などでは逆に非正規化すると性能が向上する場合がある。
5. 目的に合ったストレージエンジンを選択する
これはMySQLの醍醐味である。ストレージエンジンはそれぞれ性能特性がまったく違うので、目的に合ったストレージエンジンを選択すると劇的に性能が向上する場合がある。例えば、OLTPではInnoDB、参照系が多い場合はMyISAM、ログ目的であればARCHIVE、リアルタイム並列処理であれば NDBCLUSTERなど。他にもSun/MySQL以外のサードベンダーやコミュニティからリリースされているストレージエンジン(SPIDER、 PBXT、XtraDB、Q4M、Infobright、Kickfireなど)もあるので、目的に合わせて色々検討してみるといいだろう。
6. レプリケーションで負荷分散する
MySQLほどお手軽に、そして安価にレプリケーションを利用出来るRDBMSは他にないだろう。レプリケーションを用いてたくさんのスレーブへ参照系の処理を負荷分散するテクニックは、Webサイトなどで頻繁に利用されているテクニックである。参照系の負荷分散を行う場合だけでなく、例えばOLTPのデータを元にBIなどの処理を毎日行う場合などにも有効である。スレーブ上でBIを行えば、マスター上のOLTP系の処理に影響を与えることがない。
7. ストアドプログラムを多用しない
残念ながら、MySQLはストアドプロシージャ、ストアドファンクション、トリガなどの性能はあまりよくない。出来るだけそれらを利用せずに、ロジックをアプリケーション側に持っていくといいだろう。
8. ファイルシステムをチューニングする
Linuxであればデフォルトはext3(そろそろext4になっていくだろうか?)であるが、ext3ではなくXFSを利用すると性能が向上する場合がある。また、I/Oスケジューラを変更することで、同じext3であっても性能特性が変化する。SolarisではUFS、ZFS、QFSなどの利用を検討するといいだろう。WindowsならNTFS以外にあまり選択肢はないが、MyISAMの場合はLargeSystemCacheを有効にするなどのチューニングが必要である。
9. コネクションプールを利用する
アプリケーションがDB操作が必要なときに都度MySQLサーバへ接続していたのでは、接続のためのオーバーヘッドが無視出来なくなる。そんなときはコネクションプールを利用するといい。
10. ベンチマークする
どんなチューニングでも、実際に効果があるかどうかは測定してみるまで分からない。また、あるアプリケーションで効果があるチューニングでも、他のアプリケーションの負荷パターンでは逆効果になってしまうということは多々ある。なので、アプリケーションの負荷を擬似的に作り出してチューニングの効果を測定することはとても重要なのである。
11. インテルコンパイラ版を使う。
MySQLのダウンロードサイトではLinux向けにインテルのコンパイラを使って作成されたバイナリが配布されている。インテル版のコンパイラは、もちろん処理によって得手不得手はあるのだが、GCCを利用して作成されたバイナリよりも良い性能を発揮することが多い。インテルコンパイラ版を使うというのは非常にお手軽な高速化テクニックであろう。ちょうどSourceForge.JP Magazineで「インテル コンパイラーの実力を測る――インテル コンパイラー版MySQLは本当に速いのか?」という記事が書かれているので参照して欲しい。こちらではベンチマークも行っている。
ちなみに、SolarisにはSunStudioというサン・マイクロシステムズ純正のコンパイラがあり、こちらのコンパイラで作成したバイナリもGCCより高速になる場合が多い。残念ながら、SunStudioで作成されたバイナリは配布されていないので、SunStudioを試したい人は自分でコンパイルしよう。SunStudioは無料で配布されている。
12. 同時実行スレッド数を調節する。
ちょっとやそっとの負荷ではあまり問題にならないが、1000のクライアントから一斉に接続してクエリを実行する場合などは同時実行スレッド数を調節するといい。処理を並列に行うとスループットの向上が期待出来るが、同時に実行中のスレッドがあまりに多すぎるとロックの競合が多発してしまって返って性能を落とす原因になってしまう。innodb_thread_concurrencyオプションを利用するとInnoDBが内部的に利用するスレッド数を調節することができる。このオプションはいくつがいいかということについては明確な答えはない。アプリケーションの負荷によるところもあるし、CPU数やOSのスケジューラにも影響されるからである。従って、適切な値を決めるにはベンチマークが必須である。逆に言うと、ベンチマーク時には必須の調整パラメータであると言える。特にCPUコア数が増えた場合には調整することにより性能の向上が見込める。innodb_thread_concurrencyオプションのデフォルト値はバージョンによって異なるが現在は8である。0に設定得るとスレッド数の上限がないことを示す。最大値は1000。まずは0、4、8、12、16、20辺りで変化させて性能を計測するといいだろう。MySQL 6.0ではさらにスレッドプールが搭載される予定である。InnoDB内部の同時実行スレッド数だけでなく、MySQLサーバの接続スレッドの同時実行数を調節することができるようになる。例えば、同時に実行するスレッド数を100までに限定したい場合には次のように指定する。
thread_handling=pool-of-threads
thread_pool_size=100
現時点ではLOCK_event_loopの競合が発生するという問題があるためスレッドプールによる性能向上は見込めないが、正式版がリリースするまでには問題は解消されスレッドプールによって同時実行性能が改善することだろう。ちなみに、MySQL 6.0のスレッドプールはlibeventで実装されている。スレッドプールもMySQL 6.0のリリースを楽しみにさせてくれる要素の一つである。
13. 適切なサイズでCOMMITする。
一度に大量の更新を行うと、更新している間はその行に対して排他ロックがかけられるためロックの競合が発生する。ロックの競合が多発すると同時実行性能の低下に繋がってしまう。逆に言うと、OLTP系のアプリケーションで用いる場合などは、一回のトランザクションでCOMMITするサイズを小さく調節することでデータベース全体のスループットを向上させるのがいいだろう。また、更新のサイズがInnoDBのログサイズより大きい場合には、COMMITの前にテーブルスペース内にUndoログを大量に作成する必要が生じるため、I/O性能が低下するという問題が発生する。I/O性能だけでなく、不要になったUNDOログはパージスレッドが回収するまでテーブルスペース内に残り続けてしまうので容量も圧迫してしまう。LOAD DATA IN FILEなどで一度に大量のデータを更新するのは避けた方がいいだろう。
もし新たなInnoDBテーブルにCSVファイルからデータを大量にロードしたい場合にはLOAD DATAコマンドを利用せず、CSVストレージエンジンからInnoDBテーブルにALTER TABLEするという技が存在するのでお勧めである。ALTER TABLE時には1万行ごとにCOMMITしてくれるので、上記のようなUNDOログの問題は発生しない。
MySQL Performance Blogにおいてfifoを使ってLOAD DATAを小刻みに行う方法が紹介されているので、興味のある人は参照して欲しい。
14. InnoDBのログサイズを調整する。
InnoDBのログファイルのサイズが小さすぎるとInnoDBの更新処理の性能が低下してしまう。前述したように大きなデータをロードする際のパフォーマンス低下も問題であるが、ログファイルのサイズが小さいとチェックポイント処理が頻繁に発生することによる影響がとても大きくなってしまう。ならば巨大なログファイルを作ればいいじゃないか?と思うだろうが話はそう簡単ではない。ログファイルのサイズが大きいと今度はクラッシュリカバリ(Redoログ適用)の時間が増えてしまうからだ。特にHA化されたMySQLサーバではクラッシュリカバリの時間が増長するとフェイルオーバーに時間がかかる時間も長くなってしまうので好ましくない。従って、適切なログファイルのサイズを選択することは非常に大切である。InnoDBのログサイズ調節方法については過去の投稿で説明したので参照して欲しい。
15. 最新版を利用する。
希に「MySQLは4.0が一番性能がよかった」などと嘯(うそぶ)く人を見かけるが、そんなことはない。MySQLもバージョンが上がるごとに性能向上のための機能が追加されている。* 5.0 GA ... greedyオプティマイザ、Index Merge、InnoDBのTRUNCATE性能改善、NDBのCondition Pushdownなど。
* 5.0.30 ... InnoDBバッファプールのロック改善
* 5.0.50 ... クエリキャッシュのパフォーマンス改善
* 5.0.54 ... InnoDBのCPUスケーラビリティの向上
* 5.1.x ... InnoDBのAUTO-INCロックの性能改善
MySQL 5.0.30と5.0.54で行われた改善については、マイコミジャーナルの記事で詳細な説明が行われているので参照して欲しい。
さらにMySQL 6.0では性能に関するものとして次の新機能が追加される予定である。
* BKA JOIN
* Multi Read Range
* Index Condition Pushdown
* サブクエリ最適化
* スレッドプール(前述)
単純なクエリのレスポンスタイムはあまり変わらないかも知れないし、処理の内容によってはバージョン間で得手不得手が存在するが、システム全体で見た時のスループットは着実に向上しつつある。
16. READ COMMITTED分離レベルを使う。
InnoDBはデフォルトではREPEATABLE READ分離レベルを利用する。REPEATABLE READ分離レベルを利用するとPHANTOM READという問題を防ぐことができるが、ロックの競合がより頻繁に発生するようになってしまう。InnoDBではREAD COMMITTED分離レベルおよびinnodb_locks_unsafe_for_binlogオプションを利用することで、ロックの競合を減らして同時実行性能を向上させることが可能であるので、同時実行性能が重要な場合にはREAD COMMITTED分離レベルを利用しよう。(ちなみに、InnoDBは他のRDBMSとは違ってREAD COMMITTED分離レベルでNON REPEATABLE READは発生しない。)ただし、ステートメントベースレプリケーション(SBR)しか利用出来ないMySQL 5.0以前のバージョンでは、innodb_locks_unsafe_for_binlogオプション利用時にバイナリログの一貫性を保証できないという問題があった。(そしてinnodb_locks_unsafe_for_binlog利用時には同時実行性能が改善しないというオチがある。)MySQL 5.1では行ベースレプリケーション(RBR)が追加されたため、同時実行性能を保ちつつバイナリログの一貫性を保証することが可能になっている。ただしこの変更により、READ COMMITTED分離レベル利用時またはinnodb_locks_unsafe_for_binlogオプションが有効になっている場合にはRBRの利用が必須となった。
READ COMMITTEDが利用出来るのはどのような場合か?明確なのは一回のトランザクションで複数の処理を行う必要がない場合である。例えばSNSサイトの日記データなどはREAD COMMITTEDで問題ないだろう。更新は日記の文章をINSERT、参照は最新のデータのSELECTをそれぞれ一回行うだけで実装出来るため、一回のトランザクション内で複数の処理を行う機会はあまりない。多くのWebアプリケーションでは一回のリクエストが一回のクエリに対応する。従って、多くの WebアプリケーションでREAD COMMITTEDが利用出来るだろう。
ちなみに、利用出来る分離レベルはストレージエンジンによって異なる。他のストレージエンジン、例えばMySQL ClusterではREAD COMMITTED分離レベルしか利用出来ない。 FalconではREAD COMMITTED、REPEATABLE READ、SERIALIZABLEをサポートしている。
READ COMMITTEDの有効性については別途紹介したい。
17. EXPLAIN以外のチューニングコマンド。
SHOW STATUSコマンドはMySQLサーバ上の統計情報を見ることができるコマンドである。スコープ(GLOBALまたはSESSION)を指定することにより、SHOW GLOBAL STATUSではサーバ全体の統計情報を、SHOW SESSION STATUSではセッションごとの統計情報を見ることが可能である。SHOW GLOBAL STATUSを利用するときは、10分ほど間隔を空けて2回情報を採取し、増分を見ることで単位時間あたりの負荷を測定するのがコツ。(逆にいうと累積情報を見てもあまり意味がない場合が多い。)また、SHOW SESSION STATUSを利用するときは、FLUSH STATUSコマンドを実行することでセッションにおける統計情報をクリアすることができる。特定のクエリ実行時の統計情報を見るときなどに便利である。SHOW STATUSコマンドで気をつけるべき主な変数は次の通り。
* Created_tmp_disk_tables・・・ディスク上のテンポラリテーブルが作成された回数。
* Handler_read_first・・・フルインデックススキャンの回数。
* (1-Key_reads/Key_read_requests)・・・MyISAMキーバッファのキャッシュヒット率。
* Opened_tables・・・テーブルがOpenされた回数。急激に増えている場合にはテーブルキャッシュを増やす。
* Qcache_*・・・クエリキャッシュ関係。Qcache_insertsに対してQcache_hitsが少ないとヒット率が悪い。
* Select_full_join・・・2つ以上のテーブルにおいて全件同士でJOINした回数。最も致命的な兆候。
* Select_full_range_join・・・片方のテーブルで全件、もう片方のテーブルで範囲検索を行ってJOINした回数。
* Select_scan・・・全件スキャンの回数。
* Sort_scan・・・全件スキャンによるソートの回数。
* Threads_created・・・スレッドが作成された回数。急激に増えている場合にはスレッドキャッシュを増やす。
クエリのチューニングにおける基本コマンドはあくまでもEXPLAINであるが、クエリの実行状況を詳細に確認したい場合にはプロファイリング(SHOW PROFILEコマンド)を利用するといい。プロファイリングについては過去の投稿「プロファイリングで快適MySQLチューニング生活」で解説したのでそちらを参照して頂きたい。
おまけ: Sharding
巷ではGoogle Friend ConnectにおけるShardingの例が紹介されている。大規模なWebサイトなどで劇的にトラフィックやデータ量が増えてくると、単一のデータベースでは対応しきれなくなってくる。そのような場合、特定のカラムなどによってデータの格納先を振り分けるテクニックが利用されることが多いのだが、そのようなテクニックをShardingと呼ぶ。(MixiのひとはLevel2分散と呼んでいるらしい。)とはいっても、Shardingが必要になるほど大規模なデータベースはどちらかといえば少数派なので、あまり一般的なケースには当てはまらないかも知れないが、そのようなニーズがある人達のためにオマケとしてMySQLにおけるShardingのソリューションを紹介しておこうと思う。
MySQL Cluster・・・サン・マイクロシステムズが提供するオフィシャルなストレージエンジンの一つである。主キーに基づいて内部的にShardingを行うので、ユーザはShardingについて意識する必要がない。
SPIDER・・・STグローバルの斯波氏によるストレージエンジンである。MySQLユーザコンファレンス2009のスライドではShardingという単語は一切登場しなかったが、SPIDERストレージエンジンがやってることはまごう事なきShardingである。SPIDERを利用するとテーブル定義以外でユーザがShardingについて意識する必要がないので便利である。
Spock Proxy・・・MySQL ProxyをベースにしたShardingソフトウェア。Luaスクリプトを利用せずにC/C++でShardingのロジックを実装しているためとても速い。今のところMySQL ProxyではShardingを実装することは出来ない。
その他・・・Hibernate Shards、HiveDB、HBase、BigTable、HScaleなど。
登録:
投稿 (Atom)