MySQLを使い始めて間もない人がよく陥る罠の中に、気づくと使ってるストレージエンジンがMyISAMだった!ということがある。デフォルトのストレージエンジンはMyISAMなので、MySQLに詳しくない人たちが比較的陥りやすい罠なのだ。そもそもストレージエンジンという概念自体がMySQL独自のものなので仕方のない話である。MyISAMは素晴らしいストレージエンジン(たとえばこのYahoo!の中の人による投稿で言われているように)であるが、長所もあれば短所もある。例えば、
トランザクション対応ではない。
クラッシュセーフではない。
更新と参照が入り乱れた場合の同時実行性能がよくない。
テーブルが大きく(数億行とか)なるとINSERTの性能が劣化する。
などなど。特に前者の2つが問題で、アトミックな操作が必要なところでロジックを実装出来なかったり、サーバがクラッシュした時にデータがお亡くなりになったりして喪に服する羽目になったりするなど、気づいたときにはもう遅い!という事態になってしまうので注意が必要だ。
とはいえ、MyISAMは非常に使いやすい上に高速なストレージエンジンであり、全文検索機能やテーブル圧縮など他のエンジンにはない特徴を備えているので、意図的にMyISAM上でアプリケーションを作り込んでいる場合には何ら問題はない。しかし意図しない場合は問題であり、早急にInnoDBなど他のストレージエンジンへの移行を検討する必要がある。
MyISAMからの乗り換えで筆頭に上がるのはInnoDBであるが、二つのストレージエンジンはまったく違う特性を備えているので移行には注意が必要である。ストレージエンジンのメリットは、データの格納に関するロジックを抽象化して用途に応じて透過的に変更できることであり、異なるストレージエンジンを使っても同じSQL文でテーブルのデータへアクセスできることであるが、それぞれのストレージエンジンには癖があり、特に機能面やエラー処理に差異が見受けられるのもまた事実である。前置きが長くなったが、今日はMyISAMからInnoDBに移行する際にどんなことに注意しないといけないのか?ということについて概要を紹介する。
1. InnoDBの方がデータサイズが大きい
InnoDBのデータ領域は16KBごとにページ化されており、さらにMVCCのためのメタデータ(タイムスタンプや削除フラグなど)を行ごとに持っているため、消費するデータサイズは大きくなってしまう。消費するディスクスペースはMyISAMの2~3倍を見込んでおくといいだろう。
2. トランザクション対応
MyISAMの場合、クエリが失敗するのは何か致命的なエラーが起こった場合であることが多いのであまり重要視されていない。そのため、エラー処理はしっかりと実装されてない場合が多いのではないだろうか。しかしながら、トランザクションに対応しているRDBMSまたはストレージエンジンでは、潜在的にデッドロックが発生してしまう。そのため、高負荷時にはトランザクションが頻繁にデッドロックを起こすということが想定されるので、そのためのエラー処理が重要になる。
また、もっと基本的なことであるが、AUTO COMMITモードを使用していないときには、COMMITを明示的に発行する処理が必要になる。
3. 更新処理でMyISAMとInnoDBを混在させない
InnoDBはデッドロック検知機能があり、デッドロックが発生した場合には即座に片方のトランザクションをロールバックすることができる。しかし、InnoDBとMyISAMを混在している場合、MyISAM側の挙動はInnoDBからは見えないので、デッドロックが検知できない場合がある。(しかしその場合も一定時間でタイムアウトするので安心して欲しい。)
このような理由があるので混在は望ましくない。
4. 全文検索機能がない
MyISAMの独創的かつ特徴的な機能として全文検索機能がある。MyISAMの全文検索はデフォルトでは日本語対応していないが、Sennaを使えばモウマンタイである。しかしInnoDBでは英語であるか日本語であるかに係わらず、全文検索が利用出来ない。
また、全文検索ほど利用する頻度は高くないが、空間インデックスを利用出来るのもMyISAMだけである。
5. InnoDBでは全く異なるチューニングが必要
MyISAMではkey_buffer_sizeやmyisam_sort_buffer_sizeのチューニングが必要であるが、InnoDBではinnodb_buffer_pool_sizeやログサイズ、その他各種バッファサイズのチューニングが必要になる。しかも、それらの値を調整するストラテジはMyISAMとは全然違う。
6.PRIMARY KEYの扱い
InnoDBはクラスタインデックスを用いてデータが格納されている。そのため、PRIMARY KEYを用いた検索はとっても高速であるが、セカンダリインデックスを利用した検索はそれほど速くない。また、セカンダリインデックスのリーフノードにはPRIMARY KEYの値が格納される。そのため、サイズが大きなカラム(VARCHARなど)をPRIMARY KEYにしてしまうと、無駄なディスク領域を消費してしまうので注意が必要だ。VARCHARなどの文字列は数値よりも比較にかかる計算コストが高いので、文字列をPRIMARY KEYにするのは避けるべきである。
また、明示的にPRIMARY KEYを作成しない場合には、内部的に6バイトのROWIDがPRIMARY KEYとして作成されるが、これは無駄な領域であるので極力PRIMARY KEYを明示的につけるようにしよう。
7. バックアップ
MyISAMではmysqlhotcopyコマンドを使って高速にバックアップをとることができるが、InnoDBでは別の方法を用いる必要がある。InnoDB Hot Backupは高速だが有料である。mysqldumpは時間が掛かってしまう。レプリケーションを用いるとマスターに負荷をかけずにバックアップをとることができるが、サーバーの台数が増えてしまう。悩ましいところであるが、ベストなバックアップ方法を選択しよう。
8. データ移行作業
アプリケーションのロジックを変更しなければならないので、ストレージエンジンの変更だけでなくアプリケーションの入れ替え作業も同時に実施しなければならない場合が多い。従って移行作業はサービス停止が前提となる。レプリケーションをうまく使えばダウンタイムを最小化することが可能であるが、異なるストレージエンジン間でのレプリケーションには制限があるので、事前にしっかりとテストをしておく必要があるだろう。(RBRを利用すれば多少制限はマシになる)移行そのものはALTER TABLE tbl ENGINE=InnoDBで行うと良い。
もちろん、どのストレージエンジンを使うかということを、アプリケーションの要件に合わせて選定するのがベストであるが、うっかりMyISAMでアプリケーションを組んでしまった!という場合もあるだろう。そんな人の参考になれば幸いである。
http://lab.klab.org/young/2010/07/iphone%E3%82%92%E3%82%A2%E3%83%8A%E3%83%AD%E3%82%B0%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%AB%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B%EF%BC%88%EF%BC%91/
https://sites.google.com/a/gclue.jp/iphone-app-docs/iphoneapurinyuumon--viewhen
https://sites.google.com/a/gclue.jp/iphone-app-docs/iphoneapurinyuumon--viewhen
NSObject のメモリ管理の定石である init で確保したメモリは dealloc で破棄することは間違えないのですが UIViewController では viewDidload で確保したメモリを viewDidUnload で破棄して dealloc で破棄しない例がよくあるので間違えないようにしなければなりません。
viewDidload で確保したメモリは dealloc でも破棄しなければならない
viewDidUnload
viewDidUnload はメモリ不足警告をアプリが受け取った場合に didReceiveMemoryWarning メソッドから呼び出されるものです。
メモリ不足警告が起こらない場合には viewDidUnload は呼び出されません。なので、viewDidload で確保したメモリの破棄処理を viewDidUnload のみに記述し dealloc に記述しないと、メモリ不足警告がでない場合にメモリの破棄がされません。
UIViewController でのメモリ確保と破棄の定石は次のようになります。
・init で確保したメモリは dealloc で破棄する
・viewDidload で確保したメモリは viewDidUnload と dealloc で破棄する
viewDidload で確保したメモリは dealloc でも破棄しなければならない
viewDidUnload
viewDidUnload はメモリ不足警告をアプリが受け取った場合に didReceiveMemoryWarning メソッドから呼び出されるものです。
メモリ不足警告が起こらない場合には viewDidUnload は呼び出されません。なので、viewDidload で確保したメモリの破棄処理を viewDidUnload のみに記述し dealloc に記述しないと、メモリ不足警告がでない場合にメモリの破棄がされません。
UIViewController でのメモリ確保と破棄の定石は次のようになります。
・init で確保したメモリは dealloc で破棄する
・viewDidload で確保したメモリは viewDidUnload と dealloc で破棄する
view objectはreleaseしたらnilを代入する必要があるようだ。
[hoge release];
hoge = nil;
[hoge release];
hoge = nil;
例)
- (void)applicationDidFinishLaunching:(UIApplication *)application {
-
(void)
applicationDidFinishLaunching
(UIApplication *)
application
「-」
「インスタンスメソッド」ということを表す。
インスタンスメソッドっていうのは、インスタンスになったときだけ有効なメソッドのこと。
逆は「+」で、クラスメソッドと言って、インスタンスにしなくてもクラスで使えるメソッド
(void)
戻り値
applicationDidFinishLaunching
メソッドの名前
(UIApplication *)
引数
この場合の意味
【戻り値が無く】【UIApplication型】の【application】という名前の引数を設定した【applicationDidFinishLaunching】というメソッドですよという意味
- (void)applicationDidFinishLaunching:(UIApplication *)application {
-
(void)
applicationDidFinishLaunching
(UIApplication *)
application
「-」
「インスタンスメソッド」ということを表す。
インスタンスメソッドっていうのは、インスタンスになったときだけ有効なメソッドのこと。
逆は「+」で、クラスメソッドと言って、インスタンスにしなくてもクラスで使えるメソッド
(void)
戻り値
applicationDidFinishLaunching
メソッドの名前
(UIApplication *)
引数
この場合の意味
【戻り値が無く】【UIApplication型】の【application】という名前の引数を設定した【applicationDidFinishLaunching】というメソッドですよという意味
・アウトレットは【プログラム】→【部品】へ情報を伝える配線
・アクションは、【部品】→【プログラム】へ情報を伝える配線
■アウトレットの使い方
「ビューコントローラーのヘッダーファイル」に宣言プログラムを書く。
例
IBOutlet UIDatePicker *datePicker;
IBOutlet UILabel *resultLabel;
メニューバーの「Tools」>「Connection Inspector」
「Outlet」の中にある部品(例:datePicker)の+ボタンをクリック&ドラッグ
■アクションの使い方
「ビューコントローラーのヘッダーファイル」に宣言プログラムを書く。
例
-(IBAction)calc;
この場合calcメソッドを呼び出し
アウトレット同様、クリック&ドラッグでつなぐことができる
「@」のことを、「コンパイラディレクティブ」と呼ぶ。
直訳「コンパイラに命令する」という意味。
@interface ~ @end
Objective-C言語での「クラスの宣言」
@implementation ~ @end
Objective-C言語での「クラスの実装」
直訳「コンパイラに命令する」という意味。
@interface ~ @end
Objective-C言語での「クラスの宣言」
@implementation ~ @end
Objective-C言語での「クラスの実装」
■DATE_FORMAT関数
月集計に利用した
Datetime型をY-Mでグループ化し抽出
select DISTINCT DATE_FORMAT(SALESDATE,'%Y-%m') as NEW_NAME, SUM(カラム名) as GOUKEI
from YOUR_TABLE
group by NEW_NAME;
■TIME_FORMAT関数
時間集計に利用した
time型をH:00でグループ化し抽出
select TIME_FORMAT(TIME_COLUMN,'%H:00') as NEW_NAME, SUM(カラム名) as GOUKEI
from YOUR_TABLE
group by NEW_NAME;
月集計に利用した
Datetime型をY-Mでグループ化し抽出
select DISTINCT DATE_FORMAT(SALESDATE,'%Y-%m') as NEW_NAME, SUM(カラム名) as GOUKEI
from YOUR_TABLE
group by NEW_NAME;
■TIME_FORMAT関数
時間集計に利用した
time型をH:00でグループ化し抽出
select TIME_FORMAT(TIME_COLUMN,'%H:00') as NEW_NAME, SUM(カラム名) as GOUKEI
from YOUR_TABLE
group by NEW_NAME;
utf-8に色々種類があって今まで意味もわからず
utf-8 binを使っていたが、ちゃんとしなくてはと思い調べてみた。
以下引用
MySQLでutf8を指定するときに、
(たぶん)一般的には以下の2つのうちいずれかを用いると思います。
・utf8-general-ci(デフォルト)
・utf8-unicode-ci
で、今回のメモで残したいことは、
このとき、日本語を使うなら必ず
・utf8-general-ci
を使いましょうってこと。
なんで、こんなメモを残すかというと、
それぞれのキャラクタセットの違いは、
・utf8-general-ci:比較条件の拡張なし
・utf8-unicode-ci:比較条件の拡張あり
のみなんで、MySQLの公式では、
utf8-unicode-ciを使いましょうみたいなことが書いてありますので、
ローカルの環境(windows)でutf8-unicode-ciを設定したら、
ひらがなの「ほ」で条件を指定したのに、
「ぼ」や「ボ」や「ホ」といったものに一致するという珍事にはまってしまったためです。
通常はデフォルトがutf8-general-ci
utf8_bin の場合、部分一致探索 LIKE などの使用時に英字の大文字小文字が区別されてしまう。大文字小文字を区別されないようにするためには、照会順序として utf8_general_ci を使用すればよい。
utf8_general_ci と utf8_unicode_ci では、
探索等において、文字列照合の正確性と速度のトレードオフ。正確性を重視するのであれば utf8_unicode_ci を使用して、速度を重視するのであれば utf8_general_ci を使用すればいいみたい。
utf-8 binを使っていたが、ちゃんとしなくてはと思い調べてみた。
以下引用
MySQLでutf8を指定するときに、
(たぶん)一般的には以下の2つのうちいずれかを用いると思います。
・utf8-general-ci(デフォルト)
・utf8-unicode-ci
で、今回のメモで残したいことは、
このとき、日本語を使うなら必ず
・utf8-general-ci
を使いましょうってこと。
なんで、こんなメモを残すかというと、
それぞれのキャラクタセットの違いは、
・utf8-general-ci:比較条件の拡張なし
・utf8-unicode-ci:比較条件の拡張あり
のみなんで、MySQLの公式では、
utf8-unicode-ciを使いましょうみたいなことが書いてありますので、
ローカルの環境(windows)でutf8-unicode-ciを設定したら、
ひらがなの「ほ」で条件を指定したのに、
「ぼ」や「ボ」や「ホ」といったものに一致するという珍事にはまってしまったためです。
通常はデフォルトがutf8-general-ci
utf8_bin の場合、部分一致探索 LIKE などの使用時に英字の大文字小文字が区別されてしまう。大文字小文字を区別されないようにするためには、照会順序として utf8_general_ci を使用すればよい。
utf8_general_ci と utf8_unicode_ci では、
探索等において、文字列照合の正確性と速度のトレードオフ。正確性を重視するのであれば utf8_unicode_ci を使用して、速度を重視するのであれば utf8_general_ci を使用すればいいみたい。
MessageBox.Show("コメント"); //メッセージボックスを表示
フォーム名.Close(); //フォームを閉じる
Application.Exit(); //プログラムを終了する
