今回はMySQLのストレージエンジンという重箱の隅をつつくようなネタをメモしておきます。
私はMySQLについてはよちよちレベルなので、込み入った内容には触れられません。初心者でも理解しやすい部分での違いを中心にまとめていこうと思います。
【復習】ストレージエンジンとは
MySQLなどのデータベース管理システムが、データベースにデータを書き込んだり、読み込んだりするときに使われる基盤を「ストレージエンジン」と呼びます。初心者同士の会話の中で「MySQLを使ってデータを保存する」と表現することがありますが、実際にデータの保存処理を行っているのが、このストレージエンジンになります。MySQLの論理構造を図で載せます。
MySQLは「データベース管理システム」ですので、データベースを管理するために必要である様々な機能が搭載されています。その中の「データの読み書き」という部分を担当しているのがストレージエンジンになります。
MySQLには様々なストレージエンジンが最初から組み込まれています。その中でも特に有名なのが「InnoDB」と「MyISAM」です。どちらも「データの読み書き」というストレージエンジンに必須である機能は搭載されていますが、細かい機能に違いがあります。初心者はどちらを使えばいいのか迷うと思います。さて、どちらを使えばいいのでしょうか。
MyISAMとInnoDBの違いの中で、初心者が理解しやすいところ
いよいよ本題に入ります。MySQLのバージョン5.5以降では、InnoDBというストレージエンジンがデフォルトとなっています。InnoDBの特徴を調べて紹介することは私には難し過ぎますので、InnoDBとMyISAMの違いの中で、わりと理解しやすいものを2つだけを紹介します。
ロックの粒度
1つ目はロックの粒度です。ロックとは何でしょうか。例えば、複数のクライアントから同時に「このレコードを更新してください」という内容のリクエストが来たとします。ストレージエンジンが1つ目のリクエストの内容に沿って、更新処理を始めました。その最中に2つ目のリクエストの更新処理も始めてしまいました。こうなると、1つ目のリクエストと2つ目のリクエストが混ざり合って、誰も意図していなかった内容で更新されてしまうかもしれません。このようなことを防ぐために、ストレージエンジンは更新対象のテーブルやレコードに対して書き込みや読み込みを一時的に止めるように命令を出します。これを「ロック」と呼びます。InnoDBは対象のレコードに対してロックを行いますが、MyISAMは対象のテーブルに対してロックを行います。InnoDBは同じテーブルであっても異なるレコードであれば同時にリクエストを処理することができますが、MyISAMはそうはいきません。InnoDBのがロックの粒度が低いので、MyISAMより処理が早くなると考えられているようです。ただし、マシンへの負担はMyISAMのが小さいようですので、複数のクライアントから同時に更新などのリクエストを受けることが少ないアプリであれば、MyISAMのが良いという意見もあるようです。
トランザクション
2つ目はトランザクションの有無についてです。トランザクションを一言で説明すると「複数のSQLクエリを1つの作業としてまとめたもの」です。例えば、Aさんが自分の銀行口座からBさんの銀行口座に10,000円を振り込むとします。これをプラグラムに処理させる場合、手順はこのようになります。
1. Aさんの銀行口座の残高が10,000円以上であることを確認する。
2. Aさんの銀行口座の残高から10,000円を引く。
3. Bさんの銀行口座の残高に10,000円を足す。
この一連の手順が正常に終われば、振り込みは完了です。しかし、途中でトラブルが発生し、処理が中断されることがあるかもしれません。いずれかのステップで失敗した場合は、ロールバックしたいですね。そこで登場するのがトランザクションです。上記の一連の手順をトランザクションとしてまとめておくと、いずれかのステップで失敗した場合にロールバックを行ってくれます。InnoDBではトランザクション機能をサポートしていますが、MyISAMではサポートしていません。トランザクション機能を使いたいならば、InnoDB(もしくは、他のトランザクション機能があるストレージエンジン)を選ばないといけません。
トランザクションの実験
念のために、トランザクション機能の有無をRailsを使って確認してみます。InnoDBのテーブルを用意し、適当なコントローラーに下記のメソッドを追加して、このメソッドを呼び出してみます。
def test_transaction ActiveRecord::Base.transaction do User.create(name: 'hoge') fail end end
メソッド呼び出し後に、Userの総数を見てみます。
# InnoDBのとき User.count => 0
0件です。途中で処理が失敗していますので、ロールバックされています。同じ処理をMyISAMでも行ってみます。
# MyISAMのとき User.count => 1
データが1件残っています。トランザクションが機能していませんね。やはりMyISAMにはトランザクション機能がないようです。