Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

わかった気になるMySQL

8,072 views

Published on

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

Published in: Technology
  • Hello! Get Your Professional Job-Winning Resume Here - Check our website! https://vk.cc/818RFv
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Download this 3-step guide to generating insane amounts of media coverage for your from LinkedIn: http://bit.ly/linkedin3stepguide
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

わかった気になる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

×