page_adsence

2010年12月30日木曜日

GULLWINGに乗ってみた感想

年末に初すべりを済ませてきたので、そのときに初めて乗った011 ArtisticのGULLWINGの感想です。

まず最初に思ったのはめっちゃ柔らかい!

オーリーがめっちゃしやすくて、高さも結構出ます。
バター系トリックも思ったとおり結構やりやすくていい感じです。
スピン系のトリックはまだ初すべりだったんでなんともいえない感じですが、
まぁそんなに問題がある感じではなかったです。

全体的にかなり調子いい感じなんですが1点だけ気になったのは、
ターンのときにエッジが刺さりすぎてるってとこです。
センター部分のエッジが雪面に引っかかりすぎて、思ったよりスピードが出ず、
バランスを崩してこけたりすることが何回かありました。
やっぱキャンバーとロッカーで構造が違うのでしょうがないですね。
その辺は慣れだと思いますんでこれから徐々に慣れて行きたいと思います。

とはいえかなり面白い!
相当楽しめる板であることには間違いないので、
今後はスピントリックを色々試してみて乗り味をレビューできたらと思います。

2010年12月27日月曜日

MySQLのmy.cnfの設定に関して

今日会社でたまたま話題に出たので調べてみた。

前の会社では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 内でページングを引き起こす可能性があるので、あまり大きく設定しないでください。」とも書いてあって、結局どの位が適正なのかはわからない。

チケット駆動開発に利用されるツール

redmineとtracがメジャー
他にもMantisやcandycane(redmineのPHP版)等もある。

CentOS5.5にSubversionを導入

社内でバージョン管理されていないプロジェクトが以外と多いことを知ったので、
とりあえず自分だけでもバージョン管理をしようと思い、
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

oracleのインストール(Linux版)

初oracleインストールしたので、その時のメモ。
基本的には以下の通り。
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や画像ファイルを無理やり読み込ませると無限ループに陥るという欠点がある。
それを修正した版は以下の通り。

<?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は書いていこうと思う。

SoftbankのUIDの取得に関して

初めて携帯コンテンツ作っている時にはまったので、メモ。
既存の管理画面を改修して作ったはずなのに、ソフトバンクの携帯のUIDが取れなかった。

$uid = $_SERVER["HTTP_X_JPHONE_UID"];

で取得できるってどのサイトにも書いてあったんですが、なぜか取得できない…。
調べてみたら、SSLの直リンクの場合はUIDが取得できないみたい。
なので、いったん非SSLのページからSSLのページに遷移する必要がある。

UID関係は結構落とし穴が多いみたい。

参考文献
SSL利用時のHTTP_X_JPHONE_UIDの落とし穴

MySQLを高速化する方法

MySQLのチューニングに関してよさそうな記事があったので、とりあえずコピってみた。

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など。

MySQLでやってはいけないこと

MySQLを使用する時点で気をつけておかないといけないことを漢(オトコ)のコンピューター道さんの方で記事があったので、転載させてもらうことにした。

1. 全てのカラムにインデックスをつける

データベース初心者がもっともやってしまいがちな間違いはコレではないだろうか。インデックスはいい。検索がとても速くなるから。しかし、それと引き替えにインデックスは更新するときにコストがかかるし、その分多くのディスクスペースを消費する。特に更新にかかるコストは時に甚大で、該当するインデックスのページがキャッシュ上にない場合はディスクからいったんそのページを読み込まなければいけない。ディスクアクセスは動作にとても時間がかかるので、インデックスが多数、例えば全てのカラムに付いていたりすると「あれ?固まったか?」というような状態になってしまうことがあるだろう。インデックスは必要なカラムにだけつけるようにテーブルを設計しよう。インデックス数が増えてきたら、テーブルの分割や正規化を検討した方がいいだろう。

2. デフォルトのまま使う

何も足さない。何も引かない。

一切の手を加えずに、デフォルトのままMySQLを使ってしまう。デフォルトの状態でもそれなりのパフォーマンスを発揮出来るが、バッファが足りないので思うように性能が出ないだろう。特に、大きなテーブルを扱う時には大きなバッファが必要である。(テーブルがごく小さなものであばデフォルトのままでも何ら問題ないかも知れない。)バッファが小さいと、キャッシュされないインデックスページが多数存在することになり、特にインデックスページを更新する時にディスクアクセスが生じるのが問題になってしまうだろう。

1との合わせ技で一本。

3. インデックスを利用しない

インデックスなどというこざかしい技は利用しない。オトコは黙って真っ向からテーブルスキャン!!などと馬鹿なことを考えてはいけない。データベースの検索はインデックスを使ってナンボである。インデックスをつけすぎるのも良くないが、インデックスを使わないのはもっといけない。特にインデックスを使わずにJOINをするようなクエリ(FULL JOIN)は強烈に遅いので気をつけよう。

4. バッファを多く割り当てすぎる

バッファは多ければ多いほど高速になる。しかし、欲張って空きメモリをはるかに上回るバッファを割り当ててしまっては元も子もない。スワップが発生してしまうからだ。スワップが発生すると、目を覆いたくなるほど性能が劣化する。MySQLはメモリへアクセスしているつもりでも、ディスクへのR/Wが完了しないと処理を継続することが出来ないからだ。しっかりと空きメモリを確認し、スワップが起きないように気をつけよう。

セッションごとのバッファには要注意だ。これらは負荷に応じて多く割り当てられる可能性があるからだ。システムの負荷がスカスカのときに大丈夫だと安心していたら、負荷がピークになったときにスワップが発生してエライ目にあった・・・などということにならないよう気をつけよう。パッケージに含まれるmy- innodb-heavy-4G.cnfサンプルファイルには注意が必要である。これは、heavyなクエリ、つまり複雑なクエリを処理するための構成であって、セッションごとのバッファが多く割り当てられている。負荷が重いという意味ではないのである。

5. ネストしたサブクエリ

既に解説したように、MySQLのサブクエリは外側から順に評価される。従って、サブクエリをネストしていると、内側のサブクエリは無駄に何回も実行されてしまうことになる。特にIN句を用いているときは注意が必要である。

ネストしたサブクエリのように遅いクエリが存在すると、ロックモードによっては他のセッションの参照や更新がブロックされてしまうことがある。システム全体の足を引っ張ってしまうわけである。その他には、JOIN + ORDER BY + LIMITを合わせて利用する場合、一行ずつINSERTするような場合、ストアドプログラムを多用しているような場合には注意が必要である。

6. ストレージエンジンの用法を間違えている

たまに見かけるのが、InnoDBなのにテーブルロックを利用している場合である。InnoDBは行レベルで排他制御を行うので、テーブルロックを利用するとわざわざ同時実行性能を下げてしまうことになる。

トランザクションが必要なのにMyISAMを利用しているというような場合もある。BEGIN;....COMMIT;などとやっているが、MyISAM ではCOMMITをしても意味がないので意味がないし、ロールバックは一切出来ない。トランザクションが必要なら、トランザクション対応のエンジンを利用しよう。

InnoDBを利用している時にデッドロック時のエラー処理がない場合がある。トランザクションは常にデッドロックによるロールバックの発生を意識して利用しなければならない。MyISAMを利用していたプログラムをInnoDBに変換した場合に、このような過ちを犯してしまいがちなのだろう。

7. バラバラの文字コードを使う

MySQLの便利なところは、文字コードの変換を自動的に行ってくれるところである。それにあぐらをかいて文字コードに無頓着でいると、痛い目を見ることになるだろう。文字コードはテーブルごとに設定することができるが、セッションに対して設定することも可能である。また、システムレベルで利用する文字コードも設定することが可能である。全ての文字コードがバラバラだと、テーブルから行をフェッチ --->文字列処理関数--->クライアントへ送信、という処理を行うような場合に、各ステップの間で文字コードの変換が発生してしまう。全てをアプリケーションが利用する文字コードへ統一しよう。

8. 同時接続数が多すぎる

MySQLは同時接続数が増えても軽快に動作する・・・ということはない。数千の接続が同時にアクティブな状態では、ロックの競合が多発してスラッシングが起きてしまう。適切な接続数を見極めよう。クライアント数が増えた場合には、レプリケーションや MySQL Clusterによるスケールアウトを検討するといいだろう。

上記とは異なる問題であるが、徐々にセッション数が増えてしまい、新規の接続が出来なくなるというような問題をたまに見かける。これは、単にアプリケーションがセッションを閉じない場合に生じる問題であり、例外発生時などに的確にセッションを閉じるように注意されたい。

9. 他のアプリケーションがリソースを食い過ぎる

MySQLが稼働しているホストで、他のアプリケーションも動作している場合には、メモリやCPU、ディスクアクセスなどの資源が枯渇しないように注意を払う必要がある。メモリが足りなくなってしまった場合、MySQLはOOMキラーの格好の餌食となるだろう。(OOMキラーに終了させられないように設定しておくと良い。)

10. いにしえのバージョンを使う

MySQLの開発者達は、日々バグの修正を行って膨大な数の問題を修正している。従って、新しいバージョンのMySQLは新しい機能が追加されているだけでなく、バグが少なく安定している。とても古いバージョン、特に3.xや4.xを使っているような場合には気をつけよう。

古いバージョンにだけしか搭載されていない機能にも要注意である。例えば、かつてはBerkeley DBがストレージエンジンとして搭載されていることがあった。しかし、BDBストレージエンジンが安定することはなかった。MyISAMの前身である ISAMストレージエンジンというのがあるが、ISAMは既にメンテナンスが終了している。MyISAMのRAIDモードなどというものもかつては存在したが、5.0以降のバージョンには搭載されていない。

MySQLだけを最新にしても、OSが古すぎると元も子もない。Linuxカーネル 2.4またはそれ以前、FreeBSD 5.x、Solaris 8など、まだまだ使われている場合も多いが、古いOSを使うメリットはあまりない。OSも出来るだけ新しいものを利用しよう。

2010年12月17日金曜日

ワキシングテーブルが来た!

今年こそはワックスをちゃんとかけようと思い、ワキシングテーブル(eSPORTSオリジナルWAX STAND)を購入しました。

普通にガリウムとかのワックステーブル買うと2万位しますが、これは6000円位と格安でした。
まぁ安かろう悪かろうって感じでそこまで期待してたわけではないんですが、
ワックステーブル本体の作りは悪くなかったです。
しかも下に引くシートみたいなのも付いてきたので、そこそこいい買い物でした。
本体は結構がっしりしてるし、ちょっとやそっとで壊れる感じはしません。
まぁ、細かい部分の作りに関してはちょっとちゃっちい感じがしますが、
普通にワックスかける分には問題なかったです。

ただ、ワックステーブルがクロスする形状になっているので、
幅を広くすると高さが低くなるってしまうので、
板を1枚マジックテープで押さえずにかけようとすると、
ちょっと高さが足りなくなって中腰状態でかけないといけなくなります。
そこが唯一の欠点かな。
それ以外は良好です。

今年は頑張ってワックス掛けるぞー。

2010年12月16日木曜日

セッションとクッキーの違いに関して

よく忘れるのでメモ。
セッションはサーバサイドに保存される値で、クッキーはクライアントサイドに保存される値のこと。

2010年12月15日水曜日

HTMLコーディングをきちんとしていない場合の罠

きちんとHTMLをコーディングしている人と仕事をしている限り100%起こらない現象ですが、
HTMLコーディング初の人と一緒にやった時にはまった。

inputのnameにsubmitって名前があった関係で、JSからformをsubmitしようとしたらエラーになった。

vimで文字コードを自動認識するようにする

以下のソースを.vimrcにコピペすれば自動認識される。

if &encoding !=# 'utf-8'
  set encoding=japan
  set fileencoding=japan
endif
if has('iconv')
  let s:enc_euc = 'euc-jp'
  let s:enc_jis = 'iso-2022-jp'
  " iconvがeucJP-msに対応しているかをチェック
  if iconv("\x87\x64\x87\x6a", 'cp932', 'eucjp-ms') ==# "\xad\xc5\xad\xcb"
    let s:enc_euc = 'eucjp-ms'
    let s:enc_jis = 'iso-2022-jp-3'
    " iconvがJISX0213に対応しているかをチェック
  elseif iconv("\x87\x64\x87\x6a", 'cp932', 'euc-jisx0213') ==# "\xad\xc5\xad\xcb"
    let s:enc_euc = 'euc-jisx0213'
    let s:enc_jis = 'iso-2022-jp-3'
  endif
  " fileencodingsを構築
  if &encoding ==# 'utf-8'
    let s:fileencodings_default = &fileencodings
    let &fileencodings = s:enc_jis .','. s:enc_euc .',cp932'
    let &fileencodings = &fileencodings .','. s:fileencodings_default
    unlet s:fileencodings_default
  else
    let &fileencodings = &fileencodings .','. s:enc_jis
    set fileencodings+=utf-8,ucs-2le,ucs-2
    if &encoding =~# '^\(euc-jp\|euc-jisx0213\|eucjp-ms\)$'
      set fileencodings+=cp932
      set fileencodings-=euc-jp
      set fileencodings-=euc-jisx0213
      set fileencodings-=eucjp-ms
      let &encoding = s:enc_euc
      let &fileencoding = s:enc_euc
    else
      let &fileencodings = &fileencodings .','. s:enc_euc
    endif
  endif
  " 定数を処分
  unlet s:enc_euc
  unlet s:enc_jis
endif
" 日本語を含まない場合は fileencoding に encoding を使うようにする
if has('autocmd')
  function! AU_ReCheck_FENC()
    if &fileencoding =~# 'iso-2022-jp' && search("[^\x01-\x7e]", 'n') == 0
      let &fileencoding=&encoding
    endif
  endfunction
  autocmd BufReadPost * call AU_ReCheck_FENC()
endif
" 改行コードの自動認識
set fileformats=unix,dos,mac
" □とか○の文字があってもカーソル位置がずれないようにする
if exists('&ambiwidth')
  set ambiwidth=double
endif

2010年12月8日水曜日

Flash経由でのクリップボードコピーに関して

Flash Player10からセキュリティの仕様が変わったため、
JSでイベントを取得してFlashに渡す方法が使えなくなってしまった。
解決策としては、Flashを使ってボタンを生成するか、
透過のFlashをボタン等の上にかぶせるような形で実装するしか方法がなくなってしまった。

参考文献
Adobeの文章
http://www.adobe.com/jp/devnet/flashplayer/articles/fplayer10_security_changes_02.html#head4

対応策
http://www.b-r-u.net/flashclipboard.html

2010年12月6日月曜日

fopenのmodeに関して

オペレーティングシステムファミリが異なると行末も異なります。 テキストファイルに書き出し、そこに改行を加えたいとき、 オペレーティングシステムにあわせた正しい改行コードを使用する必要があります。 Unix ベースのシステムでは改行に \n キャラクタを使用します。 Windows ベースのシステムでは \r\n を使用します。 マッキントッシュベースのシステムでは \r を使用します。

間違った改行コードでファイルに書き込むと、 他のアプリケーション上でそのファイルを開いた際に変な風に見えてしまいます。

Windows上では、\nを\r\nに透過的に変換する text-mode変換フラグ('t')が提供されます。 それに対し、'b'を使って強制的にバイナリモードにすることもできます。 その場合データの変換はされません。 このフラグを使用するには、'b' または 't'を mode引数の最後に追加してください。

デフォルトの変換モードは SAPI と使用している PHP のバージョンによって異なります。 したがって、互換性の意味から、常に適切なフラグを指定することが推奨されます。 plain-text ファイルを使用する場合には 't' モードを指定すべきであり、 改行に \n を使用すると、 メモ帳のようなアプリケーションで読めることを期待できます。 それ以外のケースでは 'b' を使うべきです。

バイナリファイルを扱っている際に 'b' フラグを指定しなかった場合、 画像ファイルが壊れたり、\r\n キャラクタがおかしくなる等の問題を抱えてしまうでしょう。

PHPのセッション管理に関して

PHPで$_SESSIONを使用した場合、セッションが不要になった際には必ずセッションを破棄する処理が必要になる。
session_destroyだけでは$_SESSIONに保存されている内容は破棄されないので、
必ず空の配列を代入し、$_SESSIONの内容をクリアする必要がある。
<?php
$_SESSION = array();
session_destroy();

2010年12月3日金曜日

SQLに関してのメモ

SQL文の処理手続きの良しあしは,主にディスク・アクセス回数で判断されます(少ない方が良い)。なぜなら,ディスク・アクセス処理はほかの処理に比べて遅く,全体の処理時間を大きく左右するからです。ディスク・アクセス回数を少なくする最も基本的な技術はインデックスで,主に使われるのはBツリー・インデックスです。Bツリー・インデックスは常に効果が得られるわけではなく,カラム値の種類が少ない場合や,レコード数の少ない場合はほとんど効果がありません。二つのテーブルを検索するとき,ジョインが行われます。