ruby on rails on mysql チューニング入門
DESCRIPTION
Rails 3 系+MySQL を利用しているサービス向けに 1. どのようにボトルネックを探すのか 2. どのような設計を行えばいいのか 3. Rails上でどのようなコードを書けばいいのか の3点に絞ってこのプレゼンをみてチューニングを行えるように資料作成を行いましたTRANSCRIPT
![Page 1: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/1.jpg)
佐藤大資 (@eccyan)
Ruby on Railson MySQL
チューニング入門
![Page 2: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/2.jpg)
Ruby on Rails を使えば簡単に誰でもWeb サービスを構築する事が出来ます。
![Page 3: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/3.jpg)
Ruby on Rails を使えば簡単に誰でもWeb サービスを構築する事が出来ます。
しかし、 SQL を理解せずにActive Record を利用していると・・・
![Page 4: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/4.jpg)
![Page 5: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/5.jpg)
そこで今回は、
![Page 6: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/6.jpg)
そこで今回は、
![Page 7: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/7.jpg)
そこで今回は、
![Page 8: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/8.jpg)
そこで今回は、
に絞ってチューニングの取り掛り方法をお伝えします。
![Page 9: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/9.jpg)
ActiveRecord って何?
ActiveRecord って何?
![Page 10: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/10.jpg)
ActiveRecord って何?• DB テーブルの 1 行が 1 つのクラス
ActiveRecord って何?
![Page 11: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/11.jpg)
ActiveRecord って何?• DB テーブルの 1 行が 1 つのクラス• 色々な RDBMS を同じソースコードで(MySQL, PostgreSQL, SQLite, SQL Server, Sybase, and Oracle)
ActiveRecord って何?
![Page 12: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/12.jpg)
ActiveRecord って何?• DB テーブルの 1 行が 1 つのクラス• 色々な RDBMS を同じソースコードで(MySQL, PostgreSQL, SQLite, SQL Server, Sybase, and Oracle)• SQL 再利用の仕組みがある
ActiveRecord って何?
![Page 13: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/13.jpg)
ストアドプロシージャ?
ActiveRecord って何?
DELIMITER // DROP PROCEDURE IF EXISTS proc1// CREATE PROCEDURE proc1()BEGIN SELECT VERSION();END;//DELIMITER ;
![Page 14: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/14.jpg)
ストアドプロシージャ?
ActiveRecord って何?
DELIMITER // DROP PROCEDURE IF EXISTS proc1// CREATE PROCEDURE proc1()BEGIN SELECT VERSION();END;//DELIMITER ;負荷大丈夫?
![Page 15: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/15.jpg)
ストアドプロシージャ?• 処理速度は早いが、負荷集中する• Master-Slave 構成の場合、更新系の
負荷分散が出来ない• RDBMS 毎に構文やノウハウが変わる
ActiveRecord って何?
![Page 16: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/16.jpg)
オレオレライブラリ?
ActiveRecord って何? /** * ページングクエリ (LIMIT 付き ) を実行する * * 注 : $sql は SELECT で始めてください。 * $sql に SELECT SQL_CALC_FOUND_ROWS を入れないでください。 * * ページングパラメータ ($paging) について * <pre> * page ページ番号 (1-) empty や 0 以下の場合は 1 が仮定される * pagesize ページサイズ empty や 0 以下の場合は最後まで * no_count true: カウント不要 , empty: カウント必要 * </pre> * * @param stdclass $db DB ハンドラ * @param string $sql SQL * @param string $params パラメータ * @param array $paging ページングパラメータ * @param string $key null: 結果は array of array * $key を指定すると結果をある列の配列で返す * @return array array('total' => 全件数 , 'list' => 結果 ) * ただし、 $pageing['no_count'] が true の場合は * 結果のみを返します。 */ public function doPagingQuery($db, $sql, $params, $paging, $key = null) { if (empty($paging['no_count'])) { $sql = preg_replace('/SELECT/i', 'SELECT SQL_CALC_FOUND_ROWS', $sql, 1); }
// ページング条件 $this->createLimitClause($limitClause, $params, $paging); $sql .= ' ' . $limitClause;
// 実行 $list = $db->query($sql, $params)->result_array(); if ($key) { foreach ($list as &$value) { $value = $value[$key]; } }
if (empty($paging['no_count'])) { // 全件数を取得する $totalResult = $db->query('SELECT FOUND_ROWS() AS total') ->row_array(); $total = $totalResult['total']; } }
![Page 17: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/17.jpg)
オレオレライブラリ?
ActiveRecord って何? /** * ページングクエリ (LIMIT 付き ) を実行する * * 注 : $sql は SELECT で始めてください。 * $sql に SELECT SQL_CALC_FOUND_ROWS を入れないでください。 * * ページングパラメータ ($paging) について * <pre> * page ページ番号 (1-) empty や 0 以下の場合は 1 が仮定される * pagesize ページサイズ empty や 0 以下の場合は最後まで * no_count true: カウント不要 , empty: カウント必要 * </pre> * * @param stdclass $db DB ハンドラ * @param string $sql SQL * @param string $params パラメータ * @param array $paging ページングパラメータ * @param string $key null: 結果は array of array * $key を指定すると結果をある列の配列で返す * @return array array('total' => 全件数 , 'list' => 結果 ) * ただし、 $pageing['no_count'] が true の場合は * 結果のみを返します。 */ public function doPagingQuery($db, $sql, $params, $paging, $key = null) { if (empty($paging['no_count'])) { $sql = preg_replace('/SELECT/i', 'SELECT SQL_CALC_FOUND_ROWS', $sql, 1); }
// ページング条件 $this->createLimitClause($limitClause, $params, $paging); $sql .= ' ' . $limitClause;
// 実行 $list = $db->query($sql, $params)->result_array(); if ($key) { foreach ($list as &$value) { $value = $value[$key]; } }
if (empty($paging['no_count'])) { // 全件数を取得する $totalResult = $db->query('SELECT FOUND_ROWS() AS total') ->row_array(); $total = $totalResult['total']; } }
誰が保守するの?
![Page 18: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/18.jpg)
オレオレライブラリ?• 属人性が高い• OSS に比べて枯れていない• プラグマブルで無く、代替出来ない
ActiveRecord って何?
![Page 19: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/19.jpg)
AR で再利用可能なコードを書こう• Arel• Relation• Scope
ActiveRecord って何?
![Page 20: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/20.jpg)
Scope
ActiveRecord って何?
scope :active, where(active: true) scope :inactive, where(active: false)
![Page 21: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/21.jpg)
Scope
ActiveRecord って何?
scope :active, where(active: true) scope :inactive, where(active: false)
scope :adult_categories, lambda { # 大人向けカテゴリの ID は 1, 5, 6 active.where(category_id: [1, 5, 6]) }
![Page 22: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/22.jpg)
負荷の少ないクエリとは?
負荷の少ないクエリとは?
![Page 23: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/23.jpg)
負荷の少ないクエリとは?• 検索処理が軽い
負荷の少ないクエリとは?
![Page 24: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/24.jpg)
負荷の少ないクエリとは?• 検索処理が軽い• 並び替え処理が軽い
負荷の少ないクエリとは?
![Page 25: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/25.jpg)
負荷の少ないクエリとは?• 検索処理が軽い• 並び替え処理が軽い• 結合処理が軽い
負荷の少ないクエリとは?
![Page 26: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/26.jpg)
負荷の少ないクエリとは?• 検索処理が軽い• 並び替え処理が軽い• 結合処理が軽い
EXPLAIN で調べよう
負荷の少ないクエリとは?
![Page 27: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/27.jpg)
EXPLAIN• インデックスが必要か確認できる• 結合順序が最適か確認できる
負荷の少ないクエリとは?
![Page 28: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/28.jpg)
EXPLAIN
負荷の少ないクエリとは?
mysql> EXPLAIN SELECT `answers`.`id` AS t0_r0, `answers`.`old_id` AS t0_r1, `answers`.`question_id` AS t0_r2, `answers`.`user_id` AS t0_r3, `answers`.`question_user_id` AS t0_r4, `answers`.`to_user_id` AS t0_r5, `answers`.`parent_id` AS t0_r6, `answers`.`content` AS t0_r7, `answers`.`ng_word` AS t0_r8, `answers`.`check` AS t0_r9, `answers`.`active` AS t0_r10, `answers`.`created_at` AS t0_r11, `answers`.`updated_at` AS t0_r12, `questions`.`id` AS t1_r0, `questions`.`old_id` AS t1_r1, `questions`.`old_random_key` AS t1_r2, `questions`.`parent_category_id` AS t1_r3, `questions`.`category_id` AS t1_r4, `questions`.`user_id` AS t1_r5, `questions`.`to_user_id` AS t1_r6, `questions`.`type_id` AS t1_r7, `questions`.`is_serious` AS t1_r8, `questions`.`content` AS t1_r9, `questions`.`ng_word` AS t1_r10, `questions`.`display` AS t1_r11, `questions`.`check` AS t1_r12, `questions`.`access_count` AS t1_r13, `questions`.`use_image` AS t1_r14, `questions`.`image_url` AS t1_r15, `questions`.`link_url` AS t1_r16, `questions`.`active` AS t1_r17, `questions`.`answer_last_updated_at` AS t1_r18, `questions`.`created_at` AS t1_r19, `questions`.`updated_at` AS t1_r20, `questions`.`last_answer_id` AS t1_r21 FROM `answers` INNER JOIN `users` ON `users`.`id` = `answers`.`user_id` INNER JOIN `questions` ON `questions`.`id` = `answers`.`question_id` WHERE `answers`.`active` = 1 AND (answers.user_id = 1) AND (answers.question_user_id != 1) AND (answers.to_user_id = 1 OR answers.to_user_id is NULL) AND (questions.active = 1) AND (questions.id IN ( SELECT temp_a.question_id FROM answers as temp_a WHERE temp_a.user_id = 1 )) AND (answers.ng_word IN (0, 1)) GROUP BY answers.question_id ORDER BY questions.answer_last_updated_at desc;
![Page 29: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/29.jpg)
EXPLAIN
負荷の少ないクエリとは?
+----+--------------------+-----------+----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------+---------+------------------------------------+------+----------------------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+--------------------+-----------+----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------+---------+------------------------------------+------+----------------------------------------------+| 1 | PRIMARY | users | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index; Using temporary; Using filesort || 1 | PRIMARY | answers | ref | index_answers_on_question_id_and_user_id_and_created_at,index_answers_on_question_id_and_created_at,index_answers_on_user_id_and_created_at,index_answers_on_to_user_id_and_created_at,index_answers_on_question_user_id_and_created_at | index_answers_on_user_id_and_created_at | 4 | const | 196 | Using where || 1 | PRIMARY | questions | eq_ref | PRIMARY,index_questions_on_id | PRIMARY | 4 | rio_production.answers.question_id | 1 | Using where || 2 | DEPENDENT SUBQUERY | temp_a | index_subquery | index_answers_on_question_id_and_user_id_and_created_at,index_answers_on_question_id_and_created_at,index_answers_on_user_id_and_created_at | index_answers_on_question_id_and_user_id_and_created_at | 8 | func,const | 4 | Using index; Using where |+----+--------------------+-----------+----------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------+---------+------------------------------------+------+----------------------------------------------+4 rows in set (0.01 sec)
![Page 30: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/30.jpg)
select_type
負荷の少ないクエリとは?
SIMPLE キーまたは JOIN
PRIMARY 外部クエリを示すSUBQUERY 相関関係のないサブクエリ
DEPENDENT SUBQUERY
相関関係のあるサブクエリ
UNCACHEABLE SUBQUERY
実行毎に結果が変わるサブクエリ
DERIVED FROM 句のサブクエリ
![Page 31: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/31.jpg)
select_type
負荷の少ないクエリとは?
SIMPLE キーまたは JOIN
PRIMARY 外部クエリを示すSUBQUERY 相関関係のないサブクエリ
DEPENDENT SUBQUERY
相関関係のあるサブクエリ
UNCACHEABLE SUBQUERY
実行毎に結果が変わるサブクエリ
DERIVED FROM 句のサブクエリ
![Page 32: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/32.jpg)
サブクエリ• SQL 内部で更に SQL を発行し取得する事• 遅いのは相関関係のあるサブクエリ
負荷の少ないクエリとは?
![Page 33: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/33.jpg)
相関サブクエリクエリとサブクエリが相互関係している
負荷の少ないクエリとは?
EXPLAINSELECT * FROM questions WHERE questions.id IN (SELECT answers.question_id FROM answers WHERE question_user_id = questions.user_id );
![Page 34: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/34.jpg)
相関サブクエリクエリとサブクエリが相互関係している
負荷の少ないクエリとは?
EXPLAINSELECT * FROM questions WHERE questions.id IN (SELECT answers.question_id FROM answers WHERE question_user_id = questions.user_id );
![Page 35: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/35.jpg)
相関サブクエリさて、このクエリは?
負荷の少ないクエリとは?
EXPLAINSELECT * FROM questions WHERE questions.id IN (SELECT question_id FROM answers WHERE question_user_id = 1 );
![Page 36: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/36.jpg)
相関サブクエリさて、このクエリは?
負荷の少ないクエリとは?
EXPLAINSELECT * FROM questions WHERE questions.id IN (SELECT question_id FROM answers WHERE question_user_id = 1 );
| id | select_type || 1 | PRIMARY | | 2 | DEPENDENT SUBQUERY |
![Page 37: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/37.jpg)
相関サブクエリさて、このクエリは?
負荷の少ないクエリとは?
EXPLAINSELECT * FROM questions WHERE questions.id IN (SELECT question_id FROM answers WHERE question_user_id = 1 );
| id | select_type || 1 | PRIMARY | | 2 | DEPENDENT SUBQUERY |
なんで!?
![Page 38: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/38.jpg)
相関サブクエリさて、このクエリは?
負荷の少ないクエリとは?
EXPLAINSELECT * FROM questions WHERE questions.id IN (SELECT question_id FROM answers WHERE question_user_id = 1 );
![Page 39: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/39.jpg)
相関サブクエリさて、このクエリは?
負荷の少ないクエリとは?
EXPLAINSELECT * FROM questions WHERE EXISTS (SELECT 1 FROM answers WHERE question_user_id = 1 AND answers.question_id = questions.id );
![Page 40: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/40.jpg)
相関サブクエリさて、このクエリは?
負荷の少ないクエリとは?
EXPLAINSELECT * FROM questions WHERE EXISTS (SELECT 1 FROM answers WHERE question_user_id = 1 AND answers.question_id = questions.id );
![Page 41: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/41.jpg)
サブクエリの問題点• 可読性が減る• アルゴリズムの変動リスク• インデックス等の環境による遅延リスク
負荷の少ないクエリとは?
![Page 42: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/42.jpg)
type
負荷の少ないクエリとは?
const PRIMARY KEY/UNIQUE を利用するアクセスeq_ref JOIN で PRIMARY KEY/UNIQUE を利用するアクセス
ref PRIMARY KEY/UNIQUE 以外のインデックスで等価検索を利用するアクセス
range インデックスを利用する範囲検索。index インデックス全体をスキャンするアクセス
( フルインデックススキャン )ALL テーブル全体をスキャンするアクセス
( フルテーブルスキャン )
![Page 43: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/43.jpg)
type
負荷の少ないクエリとは?
const PRIMARY KEY/UNIQUE を利用するアクセスeq_ref JOIN で PRIMARY KEY/UNIQUE を利用するアクセス
ref PRIMARY KEY/UNIQUE 以外のインデックスで等価検索を利用するアクセス
range インデックスを利用する範囲検索。index インデックス全体をスキャンするアクセス
( フルインデックススキャン )ALL テーブル全体をスキャンするアクセス
( フルテーブルスキャン )
![Page 44: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/44.jpg)
フルインデックススキャン• Index condition pushdown を狙う高頻度の場合• 実行タイミングを変更–更新時に集計やソートを行う–キュー等を利用して非同期化
• 仕様の変更–件数に制限をかける–別テーブルやデータストアに保存
負荷の少ないクエリとは?
![Page 45: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/45.jpg)
フルテーブルスキャン• インデックスを追加する• Index Condition Pushdown を狙う高頻度の場合• フルインデックススキャンと同じ
負荷の少ないクエリとは?
![Page 46: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/46.jpg)
Index Condition Pushdown(ICP)• MySQL 5.6 から• マルチカラムインデックスを有効活用す
る
負荷の少ないクエリとは?
![Page 47: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/47.jpg)
Index Condition Pushdown(ICP)• MySQL 5.6 から• マルチカラムインデックスを有効活用す
る
負荷の少ないクエリとは?
![Page 48: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/48.jpg)
Index Condition Pushdown(ICP)ICP を使わない場合 (Sex = 1, 10 ≦ Age < 20)
負荷の少ないクエリとは?
Sex Age1 15
1 30
2 18
1 40
2 27
2 13
1 14
1 24
1 50
Id Sex Age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![Page 49: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/49.jpg)
Index Condition Pushdown(ICP)ICP を使わない場合 (Sex = 1, 10 ≦ Age < 20)
負荷の少ないクエリとは?
Sex Age1 15
1 30
2 18
1 40
2 27
2 13
1 14
1 24
1 50
Id Sex Age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![Page 50: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/50.jpg)
Index Condition Pushdown(ICP)ICP を使わない場合 (Sex = 1, 10 ≦ Age < 20)
負荷の少ないクエリとは?
Sex Age1 15
1 30
2 18
1 40
2 27
2 13
1 14
1 24
1 50
Id Sex Age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![Page 51: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/51.jpg)
Index Condition Pushdown(ICP)ICP を使う場合 (Sex = 1, 10 ≦ Age < 20)
負荷の少ないクエリとは?
Sex Age1 15
1 30
2 18
1 40
2 27
2 13
1 14
1 24
1 50
Id Sex Age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![Page 52: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/52.jpg)
Index Condition Pushdown(ICP)ICP を使う場合 (Sex = 1, 10 ≦ Age < 20)
負荷の少ないクエリとは?
Sex Age1 15
1 30
2 18
1 40
2 27
2 13
1 14
1 24
1 50
Id Sex Age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![Page 53: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/53.jpg)
Index Condition Pushdown(ICP)ICP を使う場合 (Sex = 1, 10 ≦ Age < 20)
負荷の少ないクエリとは?
Sex Age1 15
1 30
2 18
1 40
2 27
2 13
1 14
1 24
1 50
Id Sex Age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![Page 54: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/54.jpg)
Index Condition Pushdown(ICP)ICP を使う場合 (Sex = 1, 10 ≦ Age < 20)
負荷の少ないクエリとは?
Sex Age1 15
1 30
2 18
1 40
2 27
2 13
1 14
1 24
1 50
Id Sex Age
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![Page 55: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/55.jpg)
Extra
負荷の少ないクエリとは?
Using where WHERE の検索条件がインデックスだけでは解決出来ない。
Using index インデックスだけで条件を解決できる。Using filesort クイックソートでソートを行っている。
Using temporary 実行にテンポラリテーブルが必要。
![Page 56: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/56.jpg)
Extra
負荷の少ないクエリとは?
Using where WHERE の検索条件がインデックスだけでは解決出来ない。
Using index インデックスだけで条件を解決できる。Using filesort クイックソートでソートを行っている。
Using temporary 実行にテンポラリテーブルが必要。
![Page 57: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/57.jpg)
Using filesort• インデックスを追加する• ソート条件を 1 テーブルに集中させる• ソート実行を早めにさせる
負荷の少ないクエリとは?
![Page 58: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/58.jpg)
Using filesortこのクエリの場合は?
負荷の少ないクエリとは?
EXPLAIN SELECT * FROM users WHERE active = 1 ORDER BY type_id;
+----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+| 1 | SIMPLE | users | ALL | NULL | NULL | NULL | NULL | 191660 | Using where; Using filesort |+----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+1 row in set (0.00 sec)
![Page 59: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/59.jpg)
Using filesortこのクエリの場合は?
負荷の少ないクエリとは?
mysql> SHOW CREATE TABLE users \G;*************************** 1. row *************************** Table: usersCreate Table: CREATE TABLE `users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
・・・
PRIMARY KEY (`id`), KEY `index_users_on_old_id` (`old_id`), KEY `index_users_on_provider_and_uid` (`provider`,`uid`), KEY `index_users_on_old_random_key` (`old_random_key`)) ENGINE=InnoDB AUTO_INCREMENT=174849 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci1 row in set (0.00 sec)
ERROR:No query specified
![Page 60: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/60.jpg)
Using filesortこのクエリの場合は?
負荷の少ないクエリとは?
EXPLAIN SELECT * FROM users WHERE active = 1 ORDER BY type_id;
![Page 61: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/61.jpg)
Using temporary• GROUP BY / DISTINCT を見直す• 行数を考慮して JOIN 条件の見直す• サブクエリを検討する• テーブル / カラム追加を検討する
負荷の少ないクエリとは?
![Page 62: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/62.jpg)
Using temporary• GROUP BY / DISTINCT を見直す• 行数を考慮して JOIN 条件の見直す• サブクエリを検討する• テーブル / カラム追加を検討する
負荷の少ないクエリとは?
![Page 63: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/63.jpg)
高速化の仕組み• インデックス• パーティション
高速化の仕組み
![Page 64: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/64.jpg)
インデックスとは?• 辞書の索引を付けること• B-tree を利用している• 正確には最下層にデータ保持する
B+tree
高速化の仕組み
![Page 65: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/65.jpg)
B-tree とは ?定義• 根は葉であるか 2~m の子をもつ• 根・葉以外の節は m/2 以上の子をもつ• 根から葉までの深さが等しい
高速化の仕組み
![Page 66: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/66.jpg)
B-tree とは ?探索方法• 最左値より小さければ最左部分木へ進む• 最左値より大きければ次の値と比較し、
小さければ最左の次の部分木へ進む• 上記を反復する
高速化の仕組み
![Page 67: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/67.jpg)
B-tree とは ?
高速化の仕組み
10
![Page 68: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/68.jpg)
B-tree とは ?
高速化の仕組み
10
5
![Page 69: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/69.jpg)
B-tree とは ?
高速化の仕組み
5 10
2020
![Page 70: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/70.jpg)
B-tree とは ?
高速化の仕組み
205
10
47
![Page 71: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/71.jpg)
B-tree とは ?
高速化の仕組み
20 475
10
5050
![Page 72: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/72.jpg)
B-tree とは ?
高速化の仕組み
505
10 47
20
7
![Page 73: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/73.jpg)
B-tree とは ?
高速化の仕組み
505 7
10 47
20
32
![Page 74: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/74.jpg)
B-tree とは ?
高速化の仕組み
9505 7
10 47
20 329
![Page 75: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/75.jpg)
B-tree とは ?
50
10 47
20 32
5 9
7
高速化の仕組み
![Page 76: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/76.jpg)
B-tree とは ?
50
10 47
20 32
5 9
7
高速化の仕組み
![Page 77: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/77.jpg)
B-tree とは ?
50
10 47
20 32
5 9
7
高速化の仕組み
![Page 78: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/78.jpg)
B-tree とは ?
高速化の仕組み
5 9 20 32 50
7
10
47
![Page 79: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/79.jpg)
MySQL(InnoDB) のインデックス探索• リーフページが大量の場合は打ち切り
ルートから探索を行う• 9ページまで読み打ち切る (MySQL
5.6.4)• リーフページサイズは 16KB
高速化の仕組み
![Page 80: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/80.jpg)
パーティションとは?• 水平分割(行による分割)
高速化の仕組み
![Page 81: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/81.jpg)
リスク• 分割方法によっては速度が遅くなる• 既存のクエリに分割キー条件を
追加しなければならない場合がある• 分割キーを PK にする必要がある• パーティション変更はサービス停止が必
要
高速化の仕組み
![Page 82: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/82.jpg)
リターン• メモリへの読み込みが早くなる• 上記に伴いディスクアクセスも少なくな
る• 可用性の高い設計になる
高速化の仕組み
![Page 83: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/83.jpg)
テルミーの例新着が遅くなった• 元々 created_at で分割をしていた• クエリに時間による制限や
ソート以外の条件が多かった• 上記の理由で ID による分割に変更
高速化の仕組み
![Page 84: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/84.jpg)
テルミーの例新着が遅くなった
高速化の仕組み
EXPLAIN PARTITIONSSELECT `questions`.* FROM `questions` WHERE `questions`.`active` = 1 ORDER BY created_atLIMIT 10 OFFSET 0 \G;
![Page 85: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/85.jpg)
テルミーの例新着が遅くなった
高速化の仕組み
id: 1 select_type: SIMPLE table: questions partitions: p0,p1,p2,p3,…,p509,p510,p511 type: ALLpossible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 1179993 Extra: Using where; Using filesort
![Page 86: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/86.jpg)
テルミーの例新着が遅くなった• ID によるソートを行うように• 取得時に ID の範囲を指定するように
高速化の仕組み
![Page 87: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/87.jpg)
テルミーの例新着が遅くなった
高速化の仕組み
EXPLAIN PARTITIONSSELECT `questions`.* FROM `questions` WHERE `questions`.`active` = 1 AND (id > 3) AND (id < 14) LIMIT 10 OFFSET 0 \G;
![Page 88: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/88.jpg)
テルミーの例新着が遅くなった
高速化の仕組み
EXPLAIN PARTITIONSSELECT `questions`.* FROM `questions` WHERE `questions`.`active` = 1 AND (id > 3) AND (id < 14) LIMIT 10 OFFSET 0 \G;
id: 1 select_type: SIMPLE table: questions partitions: p4,p5,p6,p7,p8,p9,p10,p11,p12,p13 type: rangepossible_keys: PRIMARY,index_questions_on_id key: PRIMARY key_len: 4 ref: NULL rows: 9 Extra: Using where1 row in set (0.00 sec)
![Page 89: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/89.jpg)
テルミーの例新着が遅くなった
高速化の仕組み
id: 1 select_type: SIMPLE table: questions partitions: p4,p5,p6,p7,p8,p9,p10,p11,p12,p13 type: rangepossible_keys: PRIMARY,index_questions_on_id key: PRIMARY key_len: 4 ref: NULL rows: 9 Extra: Using where1 row in set (0.00 sec)
![Page 90: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/90.jpg)
Rails でのチューニング• ActiveRecord が生成する SQL を知る
Rails でのチューニング
![Page 91: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/91.jpg)
Rails でのチューニング• ActiveRecord が生成する SQL を知る• Eager Loading を利用する
Rails でのチューニング
![Page 92: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/92.jpg)
Rails でのチューニング• ActiveRecord が生成する SQL を知る• Eager Loading を利用する• 更新時コールバックでの集計• バックグラウンドでの集計
Rails でのチューニング
![Page 93: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/93.jpg)
ActiveRecord が生成する SQL を知る
Rails でのチューニング
![Page 94: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/94.jpg)
ActiveRecord が生成する SQL を知るto_sql
Rails でのチューニング
pry(main)> Question.uniq.to_sql
![Page 95: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/95.jpg)
ActiveRecord が生成する SQL を知るto_sql
Rails でのチューニング
pry(main)> Question.uniq.to_sql=> "SELECT DISTINCT `questions`.* FROM `questions` "
![Page 96: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/96.jpg)
ActiveRecord が生成する SQL を知るexplain
Rails でのチューニング
pry(main)> Question.uniq.explain
![Page 97: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/97.jpg)
ActiveRecord が生成する SQL を知るexplain
Rails でのチューニング
pry(main)> Question.uniq.explain Question Load (19503.4ms) SELECT DISTINCT `questions`.* FROM `questions` EXPLAIN (9.4ms) EXPLAIN SELECT DISTINCT `questions`.* FROM `questions`=> "EXPLAIN for: SELECT DISTINCT `questions`.* FROM `questions` \n+----+-------------+-----------+------+---------------+------+---------+------+--------+-------+\n| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |\n+----+-------------+-----------+------+---------------+------+---------+------+--------+-------+\n| 1 | SIMPLE | questions | ALL | NULL | NULL | NULL | NULL | 195195 | |\n+----+-------------+-----------+------+---------------+------+---------+------+--------+-------+\n1 row in set (0.01 sec)\n"
![Page 98: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/98.jpg)
ActiveRecord が生成する SQL を知る実装を行う前に pry 等で• 生成されるクエリ (to_sql)• 実行計画 (explain)を確認する。
Rails でのチューニング
![Page 99: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/99.jpg)
ActiveRecord が生成する SQL を知るNewRelic• Transaction tracing• Slow SQLの設定をオンにすれば
Rails でのチューニング
![Page 100: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/100.jpg)
ActiveRecord が生成する SQL を知るNewRelic
Rails でのチューニング
![Page 101: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/101.jpg)
ActiveRecord が生成する SQL を知るrack-mini-profiler• https://github.com/MiniProfiler/rack-
mini-profiler
• 環境毎に gem を入れるだけで簡単
Rails でのチューニング
![Page 102: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/102.jpg)
ActiveRecord が生成する SQL を知るrack-mini-profiler
Rails でのチューニング
![Page 103: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/103.jpg)
ActiveRecord が生成する SQL を知るmysqldumpslow• スローログを合計時間で集計してくれる• ログがあればローカルで実行できる• gzip も読み込んでくれる• MySQL デフォルト
Rails でのチューニング
![Page 104: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/104.jpg)
ActiveRecord が生成する SQL を知るmysqldumpslow
Rails でのチューニング
$ mysqldumpslow -t 5 -s t mysql-slow.log*
Reading mysql slow query log from mysql-slow.log
Count: 14591 Time=1.57s (22851s) Lock=0.00s (5s) Rows=10.0 (145910), rio_slave[rio_slave]@8hosts SELECT `questions`.* FROM `questions` WHERE `questions`.`display` = 'S' AND `questions`.`to_user_id` IS NULL AND `questions`.`active` = N AND (answer_last_updated_at != created_at) AND (questions.created_at >= 'S') AND (ng_word IN (N, N)) ORDER BY answer_last_updated_at desc LIMIT N OFFSET N
…
![Page 105: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/105.jpg)
Eager Loading を利用する
Rails でのチューニング
![Page 106: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/106.jpg)
Eager Loading を利用するEager Loading とは?• 「積極的に読み込む」• 先にデータを取得しておくこと• インスタンス変数による自動キャッシュ
Rails でのチューニング
![Page 107: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/107.jpg)
Eager Loading を利用する
Rails でのチューニング
Question.limit(10).each do |q| p “#{q.user} posted #{q.content}”end
![Page 108: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/108.jpg)
Eager Loading を利用する
Rails でのチューニング
Question.limit(10).each do |q| p “#{q.user} posted #{q.content}”end Question Load (9.8ms) SELECT `questions`.* FROM `questions` User Load (5.2ms) SELECT `users`.* FROM `users` WHERE User Load (4.1ms) SELECT `users`.* FROM `users` WHERE User Load (4.8ms) SELECT `users`.* FROM `users` WHERE User Load (7.9ms) SELECT `users`.* FROM `users` WHERE User Load (5.7ms) SELECT `users`.* FROM `users` WHERE User Load (5.4ms) SELECT `users`.* FROM `users` WHERE User Load (4.5ms) SELECT `users`.* FROM `users` WHERE User Load (4.4ms) SELECT `users`.* FROM `users` WHERE User Load (4.2ms) SELECT `users`.* FROM `users` WHERE User Load (4.4ms) SELECT `users`.* FROM `users` WHERE
![Page 109: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/109.jpg)
Eager Loading を利用する
Rails でのチューニング
Question.includes(:user).limit(10).each do |q| p “#{q.user} posted #{q.content}”end Question Load (9.8ms) SELECT `questions`.* FROM `questions` User Load (5.2ms) SELECT `users`.* FROM `users` WHERE User Load (4.1ms) SELECT `users`.* FROM `users` WHERE User Load (4.8ms) SELECT `users`.* FROM `users` WHERE User Load (7.9ms) SELECT `users`.* FROM `users` WHERE User Load (5.7ms) SELECT `users`.* FROM `users` WHERE User Load (5.4ms) SELECT `users`.* FROM `users` WHERE User Load (4.5ms) SELECT `users`.* FROM `users` WHERE User Load (4.4ms) SELECT `users`.* FROM `users` WHERE User Load (4.2ms) SELECT `users`.* FROM `users` WHERE User Load (4.4ms) SELECT `users`.* FROM `users` WHERE
![Page 110: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/110.jpg)
Eager Loading を利用する
Rails でのチューニング
Question.includes(:user).limit(10).each do |q| p “#{q.user} posted #{q.content}”end Question Load (6.9ms) SELECT `questions`.* FROM `questions` User Load (5.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (724, 1402, 2277, 3154, 3696, 4180, 4551, 5375, 6090, 6890)
![Page 111: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/111.jpg)
Eager Loading を利用する
Rails でのチューニング
Question.includes(:user).limit(10).each do |q| p “#{q.user} posted #{q.content}”end Question Load (6.9ms) SELECT `questions`.* FROM `questions` User Load (5.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (724, 1402, 2277, 3154, 3696, 4180, 4551, 5375, 6090, 6890)
![Page 112: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/112.jpg)
Eager Loading を利用する
Rails でのチューニング
# 指定リレーショナルオブジェクトを先読みQuestion.includes(:user) Question.includes(:user, :category)
# 指定リレーションオブジェクトのフィールドを先読みQuestion.includes(category: [:parent_category])Question.includes(category: [:parent_category, :answer_good_logs])
# 更に入れ子にも出来ます・・・
![Page 113: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/113.jpg)
Eager Loading を利用する
Rails でのチューニング
# 指定リレーショナルオブジェクトを先読みQuestion.includes(:user) Question.includes(:user, :category)
# 指定リレーションオブジェクトのフィールドを先読みQuestion.includes(category: [:parent_category])Question.includes(category: [:parent_category, :answer_good_logs])
# 更に入れ子にも出来ます・・・
![Page 114: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/114.jpg)
Eager Loading チューニングのフロー1. キャッシュを切る2. rack-mini-profiler で大量発行 SQL を探
す3. 対象のコントローラを探す4. Includes を追加する5. rack-mini-profiler で確認
Rails でのチューニング
![Page 115: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/115.jpg)
Eager Loading チューニングのフロースローログに乗らない細かい SQL も大きな負荷削減になることが結構有ります
Rails でのチューニング
![Page 116: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/116.jpg)
インスタンス変数によるキャッシュ
Rails でのチューニング
![Page 117: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/117.jpg)
インスタンス変数によるキャッシュ1接続内に複数回 SQL が発行される場合
Rails でのチューニング
Question.first.tap do |q| 10.times { "answers count is #{q.answers.count}" }end Question Load (8.0ms) SELECT `questions`.* FROM `questions` (4.2ms) SELECT COUNT(*) FROM `answers` WHERE (3.6ms) SELECT COUNT(*) FROM `answers` WHERE (5.3ms) SELECT COUNT(*) FROM `answers` WHERE (3.8ms) SELECT COUNT(*) FROM `answers` WHERE (3.8ms) SELECT COUNT(*) FROM `answers` WHERE (5.7ms) SELECT COUNT(*) FROM `answers` WHERE (3.8ms) SELECT COUNT(*) FROM `answers` WHERE (3.4ms) SELECT COUNT(*) FROM `answers` WHERE (3.8ms) SELECT COUNT(*) FROM `answers` WHERE (5.4ms) SELECT COUNT(*) FROM `answers` WHERE
![Page 118: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/118.jpg)
インスタンス変数によるキャッシュ1接続内に複数回 SQL が発行される場合
Rails でのチューニング
class Question < ActiveRecord::Base
・・・
def answers_count @answers_count ||= self.answers.count end
・・・
end
![Page 119: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/119.jpg)
インスタンス変数によるキャッシュ1接続内に複数回 SQL が発行される場合
Rails でのチューニング
Question.first.tap do |q| 10.times { "answers count is #{q.answers_count}" }end Question Load (5.1ms) SELECT `questions`.* FROM `questions` (3.6ms) SELECT COUNT(*) FROM `answers` WHERE
![Page 120: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/120.jpg)
更新時コールバックでの集計• カウント集計
• 最後に更新のあった解答
Rails でのチューニング
![Page 121: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/121.jpg)
更新時コールバックでの集計• カウント集計–更新時に親へカウントアップ /ダウン
• 最後に更新のあった投稿–更新時に親へ保存
Rails でのチューニング
![Page 122: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/122.jpg)
更新時コールバックでの集計どの様なテーブル設計の場合なの?
Rails でのチューニング
Users (Master)
name
…
Posts (Transaction)
user_id
content
…1:n
![Page 123: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/123.jpg)
更新時コールバックでの集計どの様なテーブル設計の場合なの?
Rails でのチューニング
Users (Master)
name
…
Posts (Transaction)
user_id
content
…
UserPostStatus (Transaction)
user_id
last_post_id
posted_count
…
1:n
![Page 124: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/124.jpg)
更新時コールバックでの集計どの様なテーブル設計の場合なの?
Rails でのチューニング
Users (Master)
name
…
Posts (Transaction)
user_id
content
…
UserPostStatus (Transaction)
user_id
last_post_id
posted_count
…
1:1
1:n
![Page 125: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/125.jpg)
更新時コールバックでの集計どの様なテーブル設計の場合なの?
Rails でのチューニング
Users (Master)
name
…
Posts (Transaction)
user_id
content
…
UserPostStatus (Transaction)
user_id
last_post_id
posted_count
…
1:1 1:1
1:n
![Page 126: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/126.jpg)
更新時コールバックでの集計どの様なテーブル設計の場合なの?
Rails でのチューニング
Users (Master)
name
…
Posts (Transaction)
user_id
content
…
UserPostStatus (Transaction)
user_id
last_post_id
posted_count
…都度カウントアップ /カウントダウン都度更新
1:1 1:1
1:n
![Page 127: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/127.jpg)
更新時コールバックでの集計どの様なテーブル設計の場合なの?
Rails でのチューニング
Users (Transaction)
name
last_post_id
posted_count
…
1:1 or nPosts (Transaction)
user_id
content
…
![Page 128: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/128.jpg)
更新時コールバックでの集計どの様に更新処理を行うのか?
Rails でのチューニング
class User < ActiveRecord::Base attr_accessible :last_post_id belongs_to :last_post, class_name: Post.to_s, foreign_key: :last_answer_id
def update_last_post(post = nil) new_last_post = post || Post.last_post(self.id).first self.update_column :last_post_id, new_last_post.try(:id) end
・・・
end
![Page 129: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/129.jpg)
更新時コールバックでの集計どの様に更新処理を行うのか?
Rails でのチューニング
class User < ActiveRecord::Base attr_accessible :last_post_id belongs_to :last_post, class_name: Post.to_s, foreign_key: :last_answer_id
def update_last_post(post = nil) new_last_post = post || Post.last_post(self.id).first self.update_column :last_post_id, new_last_post.try(:id) end
・・・
end
![Page 130: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/130.jpg)
更新時コールバックでの集計どの様に更新処理を行うのか?
Rails でのチューニング
class Post < ActiveRecord::Base
after_save { user.update_last_post(self) }
・・・
end
![Page 131: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/131.jpg)
更新時コールバックでの集計注意点• 冗長性が高いものであること• 再集計が可能であること
Rails でのチューニング
![Page 132: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/132.jpg)
総括• データストアの知識が重要• プロファイルでボトルネックを探せるか• メンテナンス性とトレードオフにならない
ように設計• データベース設計を変更する勇気を持とう
総括
![Page 133: Ruby on Rails on MySQL チューニング入門](https://reader036.vdocuments.mx/reader036/viewer/2022062312/55518ef2b4c90596028b5768/html5/thumbnails/133.jpg)
参考文献漢のコンピュータ道http://nippondanji.blogspot.jp/2009/03/mysqlexplain.htmlhttp://nippondanji.blogspot.jp/2009/03/using-filesort.htmlhttp://nippondanji.blogspot.jp/2012/10/mysql-56.htmlMySQL Practive Wikihttp://www.mysqlpracticewiki.com/index.php/Extra_fieldSH2 の日記http://d.hatena.ne.jp/sh2/20111217十番目のムーサhttp://d.hatena.ne.jp/psappho/20111101/1320152348 MySQL SQL オプティマイザのコスト計算アルゴリズムhttp://dbstudy.info/files/20120310/mysql_costcalc.pdfMySQL Reference Manualshttp://dev.mysql.com/doc/
総括