わかった気になるMySQL

375 views

Published on

2017/06/23 GMOテクノロジーブートキャンプ

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
375
On SlideShare
0
From Embeds
0
Number of Embeds
79
Actions
Shares
0
Downloads
1
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

わかった気になるMySQL

  1. 1. わかった 気になる MySQL 〜SELECTステートメント編〜 2017/06/23 yoku0825の中の⼈ GMOテクノロジーブートキャンプ
  2. 2. おことわり この資料で述べられる⾒解はyoku0825の中の⼈のノリによ るものであり、所属する組織または所属しない組織の意⾒を 代表するわけがありません この資料を読んだりセッションを聞いたりしてもたぶんSQL を書けるようにはなりません 1/105
  3. 3. つまり 2/105
  4. 4. テキトーに聞 いといてくだ さい 3/105
  5. 5. MySQL触った ことある⼈︖ 4/105
  6. 6. MySQL で きる ⼈︖ 5/105
  7. 7. MySQL知 らない⼈︖ 6/105
  8. 8. MySQLより 年下の⼈︖ 7/105
  9. 9. MySQL 1.0は1995年 リリースらしい Past, Present and future of MySQL and variants Part 1: Ghosts of MySQL Past | Ramblings 8/105
  10. 10. こんな画⾯触ったことある⼈︖ 9/105
  11. 11. こういうやつの⽅が好きな⼈︖ 10/105
  12. 12. MySQL好 きな⼈︖ 11/105
  13. 13. 三度の飯より MySQLが好き な⼈︖ 12/105
  14. 14. MySQLやめる か煙草やめるか って⾔われたら 13/105
  15. 15. ⼈間やめ る⼈︖ 14/105
  16. 16. \こんにちは/ yoku0825の中の⼈@GMOメディア オラクれない- ポスグれない- マイエスキューエる- ⽣息域 Twitter: @yoku0825- Blog: ⽇々の覚書- MyNA ML: ⽇本MySQLユーザ会- MySQL Casual: Slack- 15/105
  17. 17. 普段やってること 障害対応 社内サポートデスク DBに特化した運⽤, 設計, ショット作業 教育, 啓蒙活動 技術研究 16/105
  18. 18. 障害対応 開発陣はDBサーバーにSSHログインできない AP起因でMySQLがぶん回ったりするものを含む HWの交換はお任せ、OSを再セットアップしてもらってから が出番 17/105
  19. 19. 社内サポートデスク 新しい開発環境のDBが欲しいんですけど APサーバーが追加されたからGRANTしてほしいんだけど クエリー遅いんですけど このサーバー撤去するんでDBどかして欲しいんですけど DBサーバー増やす︖ 減らす︖ 18/105
  20. 20. DBに特化した運⽤, 設計, ショット作業 DBレイヤーのグランドデザイン バックアップの記録, 保管- シャーディング- mikasafabric for MySQL + MySQL Router- 必要リソースの⾒積もり- MySQL, Percona Server, MariaDB, Mroonga, Spider, PXC, ..- バージョンアップ戦略の策定- 監視設計 Seconds̲Behind̲Master, Max̲connections, Show̲processlist, .. - PMP for Cacti- テーブルサイズ, 権限変更検知, パラメーター変更検知, ロック競合, ..- 19/105
  21. 21. DBに特化した運⽤, 設計, ショット作業 フツーの ALTER TABLE でないオンラインスキーマ変更 pt-osc- SET SESSION sql_log_bin= 0 からの ALTER TABLE .. ADD KEY .. ALGORITHM= INPLACE でRSU - スロークエリーチューニング 件数だけは毎⽇通知- 前⽇⽐でハネたら anemoeater でドリルダウン- メンテのついでにマイナーバージョンアップとかメジャーバ ージョンアップとか 20/105
  22. 22. 教育, 啓蒙活動 新⼊社員研修 社内勉強会 おもむろにPRやIssueに出現してマサカリを投げて去る 21/105
  23. 23. 技術研究 MySQL 8.0 エコシステム各種の検証 ⼿抜き監査ログクライアント mikasafabric for MySQL 何故かDocker全般 22/105
  24. 24. MySQL #とは 世界でもっとも普及している、オープン ソース データ ベース https://www.mysql.com/jp/ 23/105
  25. 25. MySQL #とは 永続化可能な サーバーまたいでアクセスできる 排他・共有ロック機能付きの グローバル変数のすごいやつ 異論は認める MySQLおじさんの逆襲 24/105
  26. 26. 置いとい て 25/105
  27. 27. 今⽇は基礎として SELECTステート メントの話(だけ) をします 26/105
  28. 28. 簡素化したSELECTステートメント SELECT column1, column2, .. FROM table1 WHERE column1 = '..' ORDER BY column2; 27/105
  29. 29. 肩慣らし 28/105
  30. 30. 肩慣らし 何のエラーが出る︖ SEECT -- Invalid Syntax nonexistent_column_in_select_list FROM nonexistent_table WHERE nonexistent_column_in_where_clause = '..' ORDER BY nonexistent_column_in_orderby_clause; 29/105
  31. 31. 答え MySQL error code 1064 (ER̲PARSE̲ERROR): %s near ʻ%-.80sʼ at line %d ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for th e right syntax to use near '..' 30/105
  32. 32. 肩慣らし 何のエラーが出る︖ SELECT nonexistent_column_in_select_list FROM nonexistent_table WHERE nonexistent_column_in_where_clause = '..' ORDER BY nonexistent_column_in_orderby_clause; 31/105
  33. 33. 答え MySQL error code 1146 (ER̲NO̲SUCH̲TABLE): Table ʻ%-.192s.%-.192sʼ doesnʼt exist ERROR 1146 (42S02): Table 'test.nonexistent_table' doesn't exist 32/105
  34. 34. 肩慣らし 何のエラーが出る︖ SELECT nonexistent_column_in_select_list FROM table1 WHERE nonexistent_column_in_where_clause = '..' ORDER BY nonexistent_column_in_orderby_clause; 33/105
  35. 35. 答え MySQL error code 1054 (ER̲BAD̲FIELD̲ERROR): Unknown column ʻ%-.192sʼ in ʻ%-.192sʼ ERROR 1054 (42S22): Unknown column 'nonexistent_column_in_select_ list' in 'field list' 34/105
  36. 36. 肩慣らし 何のエラーが出る︖ SELECT column1 FROM table1 WHERE nonexistent_column_in_where_clause = '..' ORDER BY nonexistent_column_in_orderby_clause; 35/105
  37. 37. 答え MySQL error code 1054 (ER̲BAD̲FIELD̲ERROR): Unknown column ʻ%-.192sʼ in ʻ%-.192sʼ ERROR 1054 (42S22): Unknown column 'nonexistent_column_in_where_c lause' in 'where clause' 36/105
  38. 38. 肩慣らし 何のエラーが出る︖ SELECT column1 FROM table1 WHERE column1 = '..' ORDER BY nonexistent_column_in_orderby_clause; 37/105
  39. 39. 答え MySQL error code 1054 (ER̲BAD̲FIELD̲ERROR): Unknown column ʻ%-.192sʼ in ʻ%-.192sʼ ERROR 1054 (42S22): Unknown column 'nonexistent_column_in_orderby _clause' in 'order clause' 38/105
  40. 40. ER̲BAD̲FIELD̲ERROR MySQL error code 1054 (ER̲BAD̲FIELD̲ERROR): Unknown column ʻ%-.192sʼ in ʻ%-.192sʼ ERROR 1054 (42S22): Unknown column 'nonexistent_column_in_select_ list' in 'field list' ERROR 1054 (42S22): Unknown column 'nonexistent_column_in_where_c lause' in 'where clause' ERROR 1054 (42S22): Unknown column 'nonexistent_column_in_orderby _clause' in 'order clause' 39/105
  41. 41. 取り敢えずわかること シンタックスエラーが⼀番強い パースできないとどれがオブジェクトでどれがキーワードかわからな い - FROM句だけ特別っぽい たぶん、対象オブジェクトを確定してアクセス権限のチェックしない といけないから - 他は頭から読んでる︖ SQLパーザーは先頭から再帰的に構⽂解析している- 40/105
  42. 42. シンプルな SELECTにも ⾊々ある 41/105
  43. 43. ストアドファンク ションとか使った 複雑なSELECTには もっと⾊々(ry 42/105
  44. 44. SELECTステートメント SELECT [ALL | DISTINCT | DISTINCTROW ] [HIGH_PRIORITY] [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] select_expr [, select_expr ...] [FROM table_references [PARTITION partition_list] [WHERE where_condition] [GROUP BY {col_name | expr | position} [ASC | DESC], ... [WITH ROLLUP]] [HAVING where_condition] [ORDER BY {col_name | expr | position} [ASC | DESC], ...] [LIMIT {[offset,] row_count | row_count OFFSET offset}] [PROCEDURE procedure_name(argument_list)] [INTO OUTFILE 'file_name' [CHARACTER SET charset_name] export_options | INTO DUMPFILE 'file_name' | INTO var_name [, var_name]] [FOR UPDATE | LOCK IN SHARE MODE]] https://dev.mysql.com/doc/refman/5.7/en/select.html 43/105
  45. 45. シンタックス このへんはフツーに使うだろうからやらない DISTINCT- STRAIGHT̲JOIN- LIMIT- INTO OUTFILE- [FOR UPDATE | LOCK IN SHARE MODE]- 44/105
  46. 46. シンタックス(1) select_expr select̲listとも- 雑に⾔うと「カラム名を列挙するとこ」- exprの名が⽰すように、式も記述できる- 最低1つ必要- 45/105
  47. 47. MySQLのbool評価式 真なら1, 偽なら0, UNKNOWNならNULLが返る mysql80 5> SELECT DAYOFWEEK('2017/06/23') = 6; +-----------------------------+ | DAYOFWEEK('2017/06/23') = 6 | +-----------------------------+ | 1 | +-----------------------------+ 1 row in set (0.03 sec) mysql80 5> SELECT DAYOFWEEK('2017/06/23') = 5; +-----------------------------+ | DAYOFWEEK('2017/06/23') = 5 | +-----------------------------+ | 0 | +-----------------------------+ 1 row in set (0.01 sec) 46/105
  48. 48. こんな結果セットがあった時に mysql80 5> WITH RECURSIVE june AS ( -> SELECT CAST('2017/06/01' AS DATE) AS dt -> UNION ALL -> SELECT DATE_ADD(dt, INTERVAL 1 DAY) AS dt FROM june WHER E dt < '2017/06/30') -> SELECT * FROM june; +------------+ | dt | +------------+ | 2017-06-01 | | 2017-06-02 | | 2017-06-03 | .. | 2017-06-28 | | 2017-06-29 | | 2017-06-30 | +------------+ 30 rows in set (0.00 sec) 47/105
  49. 49. こんな キモい こともできる 1または0だからSUMがきく mysql80 5> WITH RECURSIVE june AS ( -> SELECT CAST('2017/06/01' AS DATE) AS dt -> UNION ALL -> SELECT DATE_ADD(dt, INTERVAL 1 DAY) AS dt FROM june WHER E dt < '2017/06/30') -> SELECT SUM(WEEKDAY(dt) IN (5, 6)) AS not_working, SUM(WEEK DAY(dt) NOT IN (5, 6)) AS working FROM june; +-------------+---------+ | not_working | working | +-------------+---------+ | 8 | 22 | +-------------+---------+ 1 row in set (0.01 sec) 48/105
  50. 50. SELECTステートメント SELECT [ALL | DISTINCT | DISTINCTROW ] [HIGH_PRIORITY] [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] select_expr [, select_expr ...] [FROM table_references [PARTITION partition_list] [WHERE where_condition] [GROUP BY {col_name | expr | position} [ASC | DESC], ... [WITH ROLLUP]] [HAVING where_condition] [ORDER BY {col_name | expr | position} [ASC | DESC], ...] [LIMIT {[offset,] row_count | row_count OFFSET offset}] [PROCEDURE procedure_name(argument_list)] [INTO OUTFILE 'file_name' [CHARACTER SET charset_name] export_options | INTO DUMPFILE 'file_name' | INTO var_name [, var_name]] [FOR UPDATE | LOCK IN SHARE MODE]] 49/105
  51. 51. シンタックス(2) table_references テーブルじゃなくてテーブルリファレンス、なのが楽しいところ- JOINした結果やサブクエリーなど、割とあらゆるSELECTの出⼒結果 がそのままテーブルリファレンスになれる - 特別なキーワードとして FROM dual がある。FROM 句⾃体の省略もで きる - 50/105
  52. 52. こんな⼊れ⼦も mysql80 5> SELECT * FROM ( -> SELECT * FROM ( -> SELECT * FROM ( -> SELECT * FROM ( -> SELECT * FROM ( -> SELECT NOW() -> ) AS t1 -> ) AS t2 -> ) AS t3 -> ) AS t4 -> ) AS t5; +---------------------+ | NOW() | +---------------------+ | 2017-06-21 19:49:16 | +---------------------+ 1 row in set (0.00 sec) 51/105
  53. 53. SELECTステートメント SELECT [ALL | DISTINCT | DISTINCTROW ] [HIGH_PRIORITY] [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] select_expr [, select_expr ...] [FROM table_references [PARTITION partition_list] [WHERE where_condition] [GROUP BY {col_name | expr | position} [ASC | DESC], ... [WITH ROLLUP]] [HAVING where_condition] [ORDER BY {col_name | expr | position} [ASC | DESC], ...] [LIMIT {[offset,] row_count | row_count OFFSET offset}] [PROCEDURE procedure_name(argument_list)] [INTO OUTFILE 'file_name' [CHARACTER SET charset_name] export_options | INTO DUMPFILE 'file_name' | INTO var_name [, var_name]] [FOR UPDATE | LOCK IN SHARE MODE]] 52/105
  54. 54. シンタックス(3) where_condition WHEREとHAVINGで指定する- 真偽値(ところによりNULL)を返す式- 実は定数でもイケる- MySQLでは0が偽、0以外が真、NULLがUNKNOWN ただしFALSEは0のシノニム、TRUEは1のシノニム - WHERE 1 って書くと全ての⾏で真になる- 53/105
  55. 55. 知ってると⾯⽩い mysql80 5> SELECT * FROM t1; +-----+------+ | num | val | +-----+------+ | 1 | one | | 2 | two | +-----+------+ 2 rows in set (0.01 sec) mysql80 5> SELECT * FROM t1 WHERE num; +-----+------+ | num | val | +-----+------+ | 1 | one | <-- numは0じゃないので真 | 2 | two | <-- numは0じゃないので真 +-----+------+ 2 rows in set (0.00 sec) 54/105
  56. 56. 知ってると⾯⽩い mysql80 5> SELECT * FROM t1 WHERE num = TRUE; +-----+------+ | num | val | +-----+------+ | 1 | one | <-- TRUEは1だからnum = 1 +-----+------+ 1 row in set (0.00 sec) mysql80 24> SELECT * FROM t1 WHERE num IS TRUE; +-----+------+ | num | val | +-----+------+ | 1 | one | <-- 1 IS TRUE => 真 | 2 | two | <-- 2 IS TRUE => 真 +-----+------+ 2 rows in set (0.02 sec) 55/105
  57. 57. NULLに対する演算(1) NULL + 1 NULL - 1 NULL * 1 NULL / 1 CONCAT(NULL, 'ぽ', 'ガッ') 56/105
  58. 58. NULLに対する演算(1) mysql80 25> SELECT NULL + 1, NULL - 1, NULL * 1, NULL / 1, CONCAT (NULL, 'ぽ', 'ガッ')G *************************** 1. row *************************** NULL + 1: NULL NULL - 1: NULL NULL * 1: NULL NULL / 1: NULL CONCAT(NULL, 'ぽ', 'ガッ'): NULL 1 row in set (0.00 sec) 57/105
  59. 59. NULLに対する演算(2) NULL AND NULL NULL AND TRUE NULL AND FALSE NULL OR NULL NULL OR TRUE NULL OR FALSE 58/105
  60. 60. NULLに対する演算(2) mysql80 25> SELECT NULL AND NULL, NULL AND TRUE, NULL AND FALS E, NULL OR NULL, NULL OR TRUE, NULL OR FALSEG *************************** 1. row *************************** NULL AND NULL: NULL NULL AND TRUE: NULL NULL AND FALSE: 0 NULL OR NULL: NULL NULL OR TRUE: 1 NULL OR FALSE: NULL 1 row in set (0.00 sec) 59/105
  61. 61. NOT NULL推奨 ある整数Aは A = 1 または A <> 1- A == 1のテストとA == 0のテストを書けば境界値テストでカバーで きる - ある整数型のNULLABLEなカラムに格納された値Bは B = 1 または B <> 1 または B IS NULLである- B == 1のテストとB == 0とdefined(B) == falseの3つのテストを 書かないといけない - 60/105
  62. 62. テストケースの増⼤ WHERE句にカラムを並べたとして カラムの数 NOT NULL 境界値の数 1 o 2^1=2 2 o 2^2=4 3 o 2^3=8 1 x 3^1=3 2 x 3^2=9 3 x 3^3=27 61/105
  63. 63. NOT NULL推奨 62/105
  64. 64. (余談) NULLABLEは伝播する NULLに対する演算をしてもNULLが返らない演算をNULLセ ーフな演算と呼ぶ たとえば IS NULL 演算⼦はNULLセーフ演算⼦- NULLに対する非NULLセーフな演算の結果はNULL いくつかのカラムがNOT NULLであったとしても、それと NULLABLEなカラムの値を非NULLセーフな関数で演算して しまったらその結果はNULLABLE ひとつのNULLABLEなカラムと演算する可能性のあるカラム 全てに三値論理を適⽤する︖ 63/105
  65. 65. 閑話休題 64/105
  66. 66. SELECTステートメント SELECT [ALL | DISTINCT | DISTINCTROW ] [HIGH_PRIORITY] [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] select_expr [, select_expr ...] [FROM table_references [PARTITION partition_list] [WHERE where_condition] [GROUP BY {col_name | expr | position} [ASC | DESC], ... [WITH ROLLUP]] [HAVING where_condition] [ORDER BY {col_name | expr | position} [ASC | DESC], ...] [LIMIT {[offset,] row_count | row_count OFFSET offset}] [PROCEDURE procedure_name(argument_list)] [INTO OUTFILE 'file_name' [CHARACTER SET charset_name] export_options | INTO DUMPFILE 'file_name' | INTO var_name [, var_name]] [FOR UPDATE | LOCK IN SHARE MODE]] 65/105
  67. 67. シンタックス(4) col_name | expr | position GROUP BY や ORDER BY で指定するやつ- カラム名、評価式、select̲list内のオフセット(1オリジン)で指定で きる - ORDER BY RAND() なんてのは1⾏ごとに RAND() が⾛って、その結果 (0から1までのDOUBLE)でソートするからランダムな結果セットが 返る - ORDER BY NULL なんてキーワードもある- expr の⽰す通り、値を返す式でもイケる- 66/105
  68. 68. ⾶び道具っぽいORDER BY指定 mysql80 5> SELECT * FROM t1; +-----+-------+ | num | val | +-----+-------+ | 1 | one | | 2 | two | | 3 | three | | 4 | four | | 5 | five | +-----+-------+ 5 rows in set (0.00 sec) mysql80 5> SELECT num, val FROM t1 ORDER BY 2 DESC; -- select_listの2要素目=valのD ESCソート +-----+-------+ | num | val | +-----+-------+ | 2 | two | | 3 | three | | 1 | one | | 4 | four | | 5 | five | +-----+-------+ 5 rows in set (0.00 sec) 67/105
  69. 69. ⾶び道具ORDER BYその2 mysql80 23> SELECT * FROM t1 ORDER BY FIELD (num, 1, 4, 5, 3, 2); +-----+-------+ | num | val | +-----+-------+ | 1 | one | | 4 | four | | 5 | five | | 3 | three | | 2 | two | +-----+-------+ 5 rows in set (0.03 sec) mysql80 23> SELECT *, FIELD (num, 1, 4, 5, 3, 2) AS sort_expr FROM t1 ORDER BY FIEL D (num, 1, 4, 5, 3, 2); +-----+-------+-----------+ | num | val | sort_expr | +-----+-------+-----------+ | 1 | one | 1 | | 4 | four | 2 | | 5 | five | 3 | | 3 | three | 4 | | 2 | two | 5 | +-----+-------+-----------+ 5 rows in set (0.00 sec) 68/105
  70. 70. NULLABLEなカラムのソート mysql80 24> SELECT * FROM t1 ORDER BY num; +------+------+ | num | val | +------+------+ | NULL | NULL | | 1 | one | | 2 | two | +------+------+ 3 rows in set (0.00 sec) mysql80 24> SELECT * FROM t1 ORDER BY num DESC; +------+------+ | num | val | +------+------+ | 2 | two | | 1 | one | | NULL | NULL | +------+------+ 3 rows in set (0.00 sec) 69/105
  71. 71. SELECTステートメント SELECT [ALL | DISTINCT | DISTINCTROW ] [HIGH_PRIORITY] [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] select_expr [, select_expr ...] [FROM table_references [PARTITION partition_list] [WHERE where_condition] [GROUP BY {col_name | expr | position} [ASC | DESC], ... [WITH ROLLUP]] [HAVING where_condition] [ORDER BY {col_name | expr | position} [ASC | DESC], ...] [LIMIT {[offset,] row_count | row_count OFFSET offset}] [PROCEDURE procedure_name(argument_list)] [INTO OUTFILE 'file_name' [CHARACTER SET charset_name] export_options | INTO DUMPFILE 'file_name' | INTO var_name [, var_name]] [FOR UPDATE | LOCK IN SHARE MODE]] 70/105
  72. 72. クエリーキャッシュ関連 [SQL̲CACHE|SQL̲NO̲CACHE] クエリー単位でクエリーキャッシュの有効/無効を切り替え られる query̲cache̲type 指定なし SQL̲CACHE SQL̲NO̲CACHE 0(DISABLE) x x x 1(ENABLE) o o x 2(DEMAND) x o x 71/105
  73. 73. 笑えるコード 653 static bool has_no_cache_directive(const char *sql, uint offset, 654 size_t query_length) 655 { .. 671 if (my_toupper(system_charset_info, sql[i]) == 'S' && 672 my_toupper(system_charset_info, sql[i+1]) == 'Q' && 673 my_toupper(system_charset_info, sql[i+2]) == 'L' && 674 my_toupper(system_charset_info, sql[i+3]) == '_' && 675 my_toupper(system_charset_info, sql[i+4]) == 'N' && 676 my_toupper(system_charset_info, sql[i+5]) == 'O' && 677 my_toupper(system_charset_info, sql[i+6]) == '_' && 678 my_toupper(system_charset_info, sql[i+7]) == 'C' && 679 my_toupper(system_charset_info, sql[i+8]) == 'A' && 680 my_toupper(system_charset_info, sql[i+9]) == 'C' && 681 my_toupper(system_charset_info, sql[i+10]) == 'H' && 682 my_toupper(system_charset_info, sql[i+11]) == 'E' && 683 my_isspace(system_charset_info, sql[i+12])) 684 return true; mysql-5.7.18/sql/sql̲cache.cc 72/105
  74. 74. 過去の遺物 [HIGH̲PRIORITY] [SQL̲SMALL̲RESULT] [SQL̲BIG̲RESULT] [SQL̲BUFFER̲RESULT] [PROCEDURE analyse()] 73/105
  75. 75. 闇なやつ [SQL̲CALC̲FOUND̲ROWS] ORDER BY .. LIMIT .. のbreakを無効にする代わりに、 COUNT(*) も⼀緒に取得する 取得した COUNT(*) は SELECT FOUND_ROWS() でアクセス可能- 2回テーブルスキャンするよりは速いけど、 ORDER BY .. LIMIT .. の早抜けできなくなるので遅い 74/105
  76. 76. 闇なやつ mysql80 7397205> SELECT * FROM city ORDER BY population DESC LIMIT 10; .. 10 rows in set (0.01 sec) mysql80 7397205> SELECT FOUND_ROWS(); +--------------+ | FOUND_ROWS() | +--------------+ | 10 | +--------------+ 1 row in set (0.00 sec) mysql80 7397205> SELECT sql_calc_found_rows * FROM city ORDER BY population DESC LIMIT 10; .. 10 rows in set (0.00 sec) mysql80 7397205> SELECT FOUND_ROWS(); +--------------+ | FOUND_ROWS() | +--------------+ | 4079 | +--------------+ 1 row in set (0.00 sec) 75/105
  77. 77. 5.6とそれ以降ではパーティションがテーブルリファレン スに指定できる FROM t1 PARTITION (p1, p2) 特定のパーティションだけをあたかもテーブルのようにアク セスできる 上⼿く使うとWHERE句をいっこかっ⾶ばせたり、不要なパ ーティションへのアクセスをさせないように指定できる(実 際に1つのパーティションアクセスだけで完結するクエリー でも、オプティマイザーがそれを確定できない場合は全パー ティションアクセスになる、など) 76/105
  78. 78. パーティション指定アクセス mysql80 22> SHOW CREATE TABLE t2G *************************** 1. row *************************** Table: t2 Create Table: CREATE TABLE `t2` ( `dt` date NOT NULL, `val` varchar(32) COLLATE utf8mb4_ja_0900_as_cs DEFAULT NULL, PRIMARY KEY (`dt`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_ja_0900_as_cs /*!50100 PARTITION BY LIST (month(dt) mod 2) (PARTITION p_odd VALUES IN (1) ENGINE = InnoDB, PARTITION p_even VALUES IN (0) ENGINE = InnoDB) */ 1 row in set (0.00 sec) $ ll /usr/mysql/8.0.1/data/d1/t2* -rw-r----- 1 yoku0825 yoku0825 131072 Jun 22 11:39 /usr/mysql/8.0.1/dat a/d1/t2#P#p_even.ibd -rw-r----- 1 yoku0825 yoku0825 131072 Jun 22 11:39 /usr/mysql/8.0.1/dat a/d1/t2#P#p_odd.ibd 77/105
  79. 79. パーティション指定アクセス mysql80 22> EXPLAIN SELECT * FROM t2 WHERE dt BETWEEN '2017/06/01' AND '2017/06/30'; +----+-------------+-------+--------------+-------+---------------+---------+---------+------+------+----------+--------- ----+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extr a | +----+-------------+-------+--------------+-------+---------------+---------+---------+------+------+----------+--------- ----+ | 1 | SIMPLE | t2 | p_odd,p_even | range | PRIMARY | PRIMARY | 3 | NULL | 5 | 100.00 | Using wh ere | +----+-------------+-------+--------------+-------+---------------+---------+---------+------+------+----------+--------- ----+ 1 row in set, 1 warning (0.04 sec) mysql80 22> EXPLAIN SELECT * FROM t2 PARTITION (p_even) WHERE dt BETWEEN '2017/06/01' AND '2017/06/30'; +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------- --+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extr a | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------- --+ | 1 | SIMPLE | t2 | p_even | range | PRIMARY | PRIMARY | 3 | NULL | 5 | 100.00 | Using wher e | +----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+----------- --+ 1 row in set, 1 warning (0.00 sec) 78/105
  80. 80. SELECT処理のイメージ イメージなのでちょくちょく 嘘 79/105
  81. 81. SELECT処理のイメージ(1) まず結果セットのベースになるFROM句の要素全体に注目 ただし相関サブクエリー以外のサブクエリーはこれに先⽴って解決される- Whole Table 80/105
  82. 82. SELECT処理のイメージ(2) WHERE句に従ってフィルタリング Whole Table Filtered Set 81/105
  83. 83. SELECT処理のイメージ(3) ソートしたり集約したり Whole Table Filtered Set Sorted Set 82/105
  84. 84. SELECT処理のイメージ(4) 結果セットに必要な列だけをバッファに詰めてできあがり Whole Table Filtered Set Sorted Set 83/105
  85. 85. GROUP BY, HAVING, ORDER BY, LIMIT WHEREで件数を絞り込んだあとに GROUP BYで集約した後に HAVINGでフィルターして ORDER BYで並べ替えてから LIMITで指定した⾏数だけ返す 84/105
  86. 86. もうちょっ と細かく︖ 85/105
  87. 87. よく使う図 InnoDB API Handler API Executor Optimizer Parser MySQL Protocol HS Protocol HS Plugin memcached Protocol InnoDB Memcached HTTP/MySQL X 86/105
  88. 88. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 87/105
  89. 89. Connection Handling ソケット, ポートからの接続を待ち受ける 接続があったらclone(またはスレッドキャッシュから取り 出してディスパッチ) ⼀次認証 88/105
  90. 90. Parser MySQLプロトコルのパース SQL構⽂のパース ⼆次認証(データベース, テーブル単位のアクセス権限チ ェック) クエリーキャッシュ処理 ジェネラルログ 89/105
  91. 91. Optimizer 統計情報の取得(Storage Engine APIを叩いてるっぽい) クエリーの書き換えを含めた実⾏計画の決定 90/105
  92. 92. Executor オプティマイザーから渡された実⾏計画の通りにHandlerを 叩く Handlerから戻ってきた結果を使って実⾏計画の残りを実⾏ Using where; Using filesort; Using temporary; はコイツが頑張っ てる証拠 - スローログ, バイナリーログ 91/105
  93. 93. Handler ストレージエンジンの抽象化レイヤー あんまり意識することはない 92/105
  94. 94. Storage Engine 実際にデータを格納するレイヤー プラガブルアーキテクチャーなので、ほとんどの機能はこの レイヤーで実装されている ストレージエンジンごとにトランザクション対応が違うとか- ストレージエンジンごとにロック粒度が違うとか- ストレージエンジンごとにバッファが違うとか- 93/105
  95. 95. え︖ もっ と細かく︖ 94/105
  96. 96. SELECTステートメントの⼀⽣(1) 3306ポートへのアクセスがmysqld̲mainによって捕捉さ れ、⼦スレッドが割り当てられる handle̲connectionからdo̲commandを経て dispatch̲commandへ、ここでユーザー認証 dispatch̲commandでMySQLプロトコルをパースし、 COM̲QUERYパケットとしてSQLパーサーにかけられる mysql̲parseを通ってparse̲sqlからMYSQLparseを呼ぶ MYSQLparseの中でthd->lexに⾊々⼊る mysql̲parseまで戻ってここでジェネラルログ出⼒ Max Query per Hourの判定が⼊って パスワードがEXPIREされてないかの判定が⼊って やっとmysql̲execute̲commandに⾶ぶ 95/105
  97. 97. SELECTステートメントの⼀⽣(2) read̲onlyオプションの判定が⼊ったりしたあと lex->sql̲commandで振り分け処理がされつつ select̲precheckが呼ばれてアカウントの権限が評価され execute̲sqlcom̲selectにわたり、 MAX̲EXECUTION̲TIMEのタイマーがセットされて open̲tables̲for̲queryでテーブルキャッシュを開き sql̲select.ccのhandle̲queryに⾏ってオブジェクト名の評 価がされ query̲cache.store̲queryを呼び SELECT̲LEX::optimizeからのJOIN::optimize呼び出し、 ここからがオプティマイザー 96/105
  98. 98. SELECTステートメントの⼀⽣(3) 可能であればOUTER JOIN ⇒ INNER JOINの書き換えとか 結合条件をWHERE句に移動する処理とか SEMIJOINならここでビットマップインデックスを作る sql̲mode=ONLY̲FULL̲GROUP̲BYの場合の評価はここ パーティションプルーニングが効く場合はここでプルーニン グ アクセスプランを評価・確定させて JOIN::execからdo̲selectに渡って最終的にhandlerから ha̲innobaseクラスにわたる 97/105
  99. 99. SELECTステートメントの⼀⽣(4) ⾏のフェッチが終わったらソートしたりフィルタリングした り⾊々してから close̲thread̲tablesでテンポラリーテーブル消したり close̲thread̲tableでテーブル閉じてテーブルキャッシュ に⼊れて mdl̲context.release̲transactional̲locksでメタデータロ ックを解放して 使ったオブジェクトを⾊々freeして Query̲result̲send::send̲dataで結果セットを返す 98/105
  100. 100. 諸元 SQLパーサー sql/sql̲yacc.yy, sql/sql̲yacc.cc エグゼキューター sql/sql̲executor.cc, sql/sql̲select.cc 汎⽤ユーティリティー sql/sql̲class.cc InnoDBストレージエンジン storage/innobase/ 99/105
  101. 101. え、まだまだ ⾜りない︖ 100/105
  102. 102. ↑やら↑ 101/105
  103. 103. ↓ない↓ 102/105
  104. 104. まとめ︖ 普段何気なく使っているMySQLもアプリケーション ⾊んな内部実装があって、得意なことがあったり苦⼿なこと があったり それをユーザーに意識させないためにSQLというレイヤーが あるので それより下を勉強するのはなかなか⼤変 世界は広くて、そういう世界で戦っているエンジニアもいる にはいる 103/105
  105. 105. まとめ︖ 困った時はいつでもどうぞ MyNA ML: ⽇本MySQLユーザ会- MySQL Casual: Slack- おじさんズ welcome you! 104/105
  106. 106. Questions and/or Suggestions? 105/105

×