データベースを使おう
DESCRIPTION
@amachang による資料です!TRANSCRIPT
データベースを使おう
InnoDB でやるお (^ω^)
テスト用にデータベースを 用意しました
• friend (約 1000 万件) – user_id VARCHAR(128) – friend_id VARCHAR(128) – … – PRIMARY KEY (user_id, friend_id) – UNIQUE KEY (friend_id, user_id)
• user (約 100 万件) – id VARCHAR(128) – name VARCHAR(256) – gender_id INT UNSIGNED – lang VARCHAR(10) – created_at DATETIME – … – PRIMARY KEY (id)
試してみよう
• 普通の SELECT 文 – SELECT * FROM user LIMIT 10; – SELECT * FROM user;
• 条件指定 – SELECT * FROM user WHERE id = '109092915251428393573';
– SELECT * FROM user WHERE name = '天野仁史'; – SELECT id, name FROM user WHERE created_at > DATE_SUB(NOW(), INTERVAL 10 MINUTE);
• 同姓同名ランキング – SELECT user.name, COUNT(id) FROM user GROUP BY user.name ORDER BY COUNT(id) DESC LIMIT 10;
試してみよう
• 従業員数ランキング – SELECT company.id, company.name, COUNT(user_employment.id)
FROM user_employment LEFT JOIN company ON user_employment.company_id = company.id GROUP BY company.id ORDER BY COUNT(user_employment.id) DESC LIMIT 10;
• 企業の従業員一覧 – SELECT user.id, user.name FROM user LEFT JOIN user_employment
ON user.id = user_employment.user_id LEFT JOIN company ON user_employment.company_id = company.id WHERE company.name = 'Google';
• 共通の友達 – SELECT * FROM friend AS f1 LEFT JOIN friend AS f2 ON f1.friend_id =
f2.user_id WHERE f1.user_id = '109092915251428393573' AND f2.friend_id = '113100517422007103669'
想像してみよう Id name gender_id lang created_at
1 佐藤太郎 1 ja 2011-‐01-‐01 22:32:44
2 鈴木花子 0 en 2011-‐01-‐01 23:32:44
3 山田二郎 1 id 2011-‐01-‐03 22:32:44
4 天野仁史 1 zh-‐cn 2011-‐01-‐05 22:32:44
5 飯塚修平 1 zh-‐tw 2011-‐01-‐06 22:32:44
… … … …
123013810 猫ひろし 1 en 2011-‐12-‐31 22:32:44
SELECT * FROM user WHERE created_at < '2011-‐08-‐01' LIMIT 10;
想像してみよう Id name gender_id lang created_at
1 佐藤太郎 1 ja 2011-‐01-‐01 22:32:44
2 鈴木花子 0 en 2011-‐01-‐01 23:32:44
3 山田二郎 1 id 2011-‐01-‐03 22:32:44
4 天野仁史 1 zh-‐cn 2011-‐01-‐05 22:32:44
5 飯塚修平 1 zh-‐tw 2011-‐01-‐06 22:32:44
… … … …
123013810 猫ひろし 1 en 2011-‐12-‐31 22:32:44
SELECT * FROM user WHERE created_at < '2011-‐08-‐01' LIMIT 10;
10 件見つかるまで探すぜ!
遅いよね
想像してみよう
Id name gender_id lang created_at
1 佐藤太郎 1 ja 2011-‐01-‐01 22:32:44
2 鈴木花子 0 en 2011-‐01-‐01 23:32:44
3 山田二郎 1 id 2011-‐01-‐03 22:32:44
4 天野仁史 1 zh-‐cn 2011-‐01-‐05 22:32:44
5 飯塚修平 1 zh-‐tw 2011-‐01-‐06 22:32:44
… … … …
123013810 猫ひろし 1 en 2011-‐12-‐31 22:32:44
SELECT * FROM user WHERE created_at < '2011-‐08-‐01' LIMIT 10;
8
4 12
2 6
2 分木で 2 分探索するぜ!
あたまいいね
インデックスの仕組み
• B+ 木 – 探索が速くなる
• O(n/2) → O(log(n)) – 挿入は遅くなる
• O(1) → O(log(n))
• ハッシュ関数を使う場合もあるよ – 検索も挿入も O(1) – 範囲検索はできない
wikipedia: B+ 木から
複合インデックス
• 複数のカラムに条件を指定して探索したい場合は、複合インデックスを使う
• 二つのキーを、上下の桁として合わせた値に対して木が作られるので、順番が大切 – どちらが上の桁で、下の桁になるかが重要
EXPLAIN • select_type
– サブクエリの種類 – DEPENDENT SUBQUERY, UNCACHEABLE SUBQUERY は要改善
• type – const
• primary key や unique index を探索に使う – eq_ref
• JOIN に primary key や unique index を使ってる – range
• インデックスを使った範囲 – ref
• インデックス使ってる – index
• フルインデックススキャン。要改善 – ALL
• フルテーブルスキャン。要改善 • key
– 実際に使われるインデックス
DEPENDENT SUBQUERY • WHERE IN (SUBQUERY) は遅い – このクエリが
• SELECT user_id FROM friend WHERE user_id IN (SELECT friend_id FROM friend WHERE user_id = ' 109092915251428393573 ') AND friend_id = ' 113100517422007103669';
– 以下のクエリに変更される • SELECT user_id FROM friend AS f1 EXISTS (SELECT 1 FROM friend AS f2 WHERE f2.user_id = ' 109092915251428393573 ' AND f2.friend_id = f1.user_id) AND f1.friend_id = '113100517422007103669';
• hmp://nippondanji.blogspot.com/2009/03/mysql_25.html を参考に
トランザクションを知ろう
• 処理途中の中途半端な状態が、影響しないようにすること – アトミック性 (Atomicity)
• トランザクションに含まれるタスクが全て実行されるか、あるいは全く実行されないことを保証する
– 一貫性 (Consistency) • トランザクション終了時に与えられたルールを守っている。 • 外部キー制約など
– 分離性 (Isolaoon) • 処理が直列化されている • 速度が犠牲になるため、独立性を一部しか実装しないことが多い
– トランザクション分離レベル
– 永続性 (Durability) • クラッシュしても一度コミットされたデータは復元できる
(wikipedia: ACID から)
トランザクション分離レベル
• SERIALIZABLE – 完全に分離性を実現
• REPEATABLE READ (InnoDB のデフォルト) – 一度、読んだデータが変更されることがない – ファントムリード
• READ COMMITTED – 常にコミット済みのデータのみを読み取る – ファントムリード – ファジーリード
• READ UNCOMMITTED – ファントムリード – ファジーリード – ダーティリード
異常な読み込み
• ダーティリード – 他のトランザクションが ROLLBACK したはずなのに、読めてる
• ファジーリード – 同じ行を 2 回読んだとき、その間に他のトランザクションが値を変更したのが読めてしまう
• ファントムリード – 同じ条件で行を読んだのに、他のトランザクションによる挿入で、1回目と2回目で結果が変わる
ロック
• InnoDB – X (排他)ロック
• DELETE, UPDATE, INSERT , SELECT FOR UPDATE – S (共有)ロック
• SELECT … FROM … LOCK IN SHARE MODE
– 一貫非ロック読み取り • SELECT … FROM …
ロックの監視
• /etc/my.cnf ignore-‐builtin-‐innodb plugin-‐load=innodb=ha_innodb_plugin.so;innodb_trx=ha_innodb_plugin.so;innodb_locks=ha_innodb_plugin.so;innodb_lock_waits=ha_innodb_plugin.so;innodb_cmp=ha_innodb_plugin.so;innodb_cmp_reset=ha_innodb_plugin.so;innodb_cmpmem=ha_innodb_plugin.so;innodb_cmpmem_reset=ha_innodb_plugin.so
SELECT * FROM information_schema.INNODB_LOCKS;