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.

ペパボ de MySQL

7,642 views

Published on

2016/02/22 GMOペパボでMySQL勉強会

* わりと前にやったやつ成分が多めです。
* Excel方眼紙のページがないのは仕様です。

Published in: Technology
  • Be the first to comment

ペパボ de MySQL

  1. 1. あんちぽくんさん「ネタは雑 + 5.7 がいいです︕」 いつのまにかあんちぽちゃんさんになっていたことに気付かなかっ た 2016/02/22 yoku0825の中の⼈ Pepabo de MySQL
  2. 2. \こんにちは/ yoku0825@とある企業のDBA の中の⼈ オラクれない- ポスグれない- マイエスキューエる- Twitter: @yoku0825 Blog: ⽇々の覚書 Oracle ACE Details MySQL 5.7 Community Contributor Award 1/69
  3. 3. このスライド書いた中の⼈です 2/69
  4. 4. ちなみに 3/69
  5. 5. というわけで安⼼ して 地雷友達 5.7 使っていいです よ :) 4/69
  6. 6. なお ウチは明⽇ 5.7のカットオーバーがあります 明⽇からまた5.7についてぐちぐち呟いてたら痛い目⾒たん だと思ってください 5/69
  7. 7. ざっくりMySQL 5.7 2013/02 MySQL 5.6.10 GA 2013/04 MySQL 5.7.1 DMR11 2015/03 MySQL 5.7.6 DMR16 2015/04 MySQL 5.7.7 RC 2015/08 MySQL 5.7.8 RC 2015/10 MySQL 5.7.9 GA 2015/12 MySQL 5.7.10 GA 2016/02 MySQL 5.7.11 GA 6/69
  8. 8. MySQL 5.7 2015/04 MySQL 5.7.7 RC ここまではまあいい 2015/08 MySQL 5.7.8 RC JSON型, virtual generated columnの拡張, InnoDB Page Compression 2015/10 MySQL 5.7.9 GA innodb_default_row_format, JSON -> operator, innodb_numa_interleave 2015/12 MySQL 5.7.10 GA Bug Fix 2016/02 MySQL 5.7.11 GA InnoDB Tablespace Encryption 7/69
  9. 9. MySQL 5.7 2015/04 MySQL 5.7.7 RC ここまではまあいい 2015/08 MySQL 5.7.8 RC 新機能!! 2015/10 MySQL 5.7.9 GA 新機能!! 2015/12 MySQL 5.7.10 GA Bug Fix 2016/02 MySQL 5.7.11 GA 新機能!! 8/69
  10. 10. General Available # とは 9/69
  11. 11. MySQL 5.7について雑に⾔っておきたいこと 取り敢えず⼀通りなアレは slideshare と ブログ に置いて きました 気に⼊ってる機能だけ伝えます 10/69
  12. 12. お気に⼊り sysスキーマ を 5.6で 使うこと generated column GTIDのオンライン有効化 11/69
  13. 13. sysスキーマ 元 ps̲helper リポジトリーの名前はdbahelper。。- performance̲schema, information̲schemaのビューと、 performance̲schema関連の設定をゴニョるプロシージャ の詰め合わせ なのでperformance_schema= ONが⼤前提- 合わせて使いたいinnodb_monitor_enable= all- 実は5.6から使える(使ってる。便利)- 12/69
  14. 14. sys.metrics 監視に良さそうな項目の詰め合わせ。 もうSHOW ENGINE INNODB STATUSをパースしなくてもいい んだ。。 - mysql> SELECT * FROM sys.metrics; +-----------------------------------------------+---------------------+--------------------------------------+---------+ | Variable_name | Variable_value | Type | Enabled | +-----------------------------------------------+---------------------+--------------------------------------+---------+ | aborted_clients | 3288 | Global Status | YES | | aborted_connects | 1533301 | Global Status | YES | | binlog_cache_disk_use | 0 | Global Status | YES | | binlog_cache_use | 6788464032 | Global Status | YES | .. | buffer_flush_adaptive | 23215268 | InnoDB Metrics - buffer | YES | | buffer_flush_adaptive_pages | 8 | InnoDB Metrics - buffer | YES | | buffer_flush_adaptive_total_pages | 257407436 | InnoDB Metrics - buffer | YES | | buffer_flush_avg_page_rate | 7 | InnoDB Metrics - buffer | YES | | buffer_flush_background | 4894219 | InnoDB Metrics - buffer | YES | .. | compression_pad_decrements | 9 | InnoDB Metrics - compression | YES | | compression_pad_increments | 9 | InnoDB Metrics - compression | YES | | compress_pages_compressed | 362107 | InnoDB Metrics - compression | YES | | compress_pages_decompressed | 155406 | InnoDB Metrics - compression | YES | .. 13/69
  15. 15. sys.schema̲index̲statistics どのインデックスを使ってどれだけハンドラーが呼ばれた か、そのレイテンシーの合計は…というのが⾒られる。 あんまり使われてないインデックスをあぶりだすのは後述の sys.schema_unused_indexesビューでもいける。 mysql> SELECT * FROM sys.schema_index_statisticsG *************************** 1. row *************************** table_schema: xxxxxxx table_name: quiz_xxxxxxxx index_name: idx_xxxxxxxxx rows_selected: 352020501193 select_latency: 5.83 d rows_inserted: 0 insert_latency: 0 ps rows_updated: 0 update_latency: 0 ps rows_deleted: 0 delete_latency: 0 ps 1 row in set (0.04 sec) 14/69
  16. 16. sys.schema̲unused̲indexes 名前そのものズバリ、使われてない(統計開始から⼀度も使 われていない)インデックスを⼀覧してくれる。 ただし、performance_schema.setup_actors, performance_schema.setup_objects, performance_schema.setup_instruments= 'wait/io/table/ sql/handler'あたりの設定に依存する。吊るしで使ってるな らフツーのテーブルではmysqldの起動時から全部統計が有効 になっている。 アクセス具合によるので、マスターで使ってなくてスレーブ で使ってるインデックスとかあるので注意。 15/69
  17. 17. sys.statement̲analysis sys.format_statementというストアドファンクションを噛ん でいるせいで、queryカラムが切り詰められる。 performance_schema.events_statements_summary_by_dig est.digest_textに切り詰められていないステートメント があるので、USING (digest)でJOINしてやるとpt- query-digestっぽい出⼒が得られる。 - sys.x$statement_analysisでもいいんだけど、queryカラ ム以外も全部展開されちゃって⾒にくい。 - mysql> SELECT * FROM sys.statement_analysisG *************************** 2. row *************************** query: SELECT `xxxx_REPORT` . `UNIQU ... S `PURE_TOTAL_xxxx` , SUM ( 16/69
  18. 18. sys.statements̲with̲errors̲or̲warnings カジュアルに桁切り詰めワーニングを無視してるステートメ ントとか⼀網打尽にできる。 --gtid-enforce-consistency= WARNと組み合わせて使うと夢 が広がる。 17/69
  19. 19. sys.innodb̲lock̲waits SH2さんのステートメントとの別離の時 MySQL InnoDBにおけるロック競合の解析⼿順 - SH2の ⽇記 - KILLするためのステートメントも出⼒してくれるあたりがロ ック しかもKILL QUERYとKILLを両⽅出⼒する芸の細かさ- 18/69
  20. 20. sys.innodb̲lock̲waits mysql> DESC sys.innodb_lock_waits; +------------------------------+---------------------+------+-----+---------------------+-------+ | Field | Type | Null | Key | Default | Extra | +------------------------------+---------------------+------+-----+---------------------+-------+ | wait_started | datetime | YES | | NULL | | | wait_age | time | YES | | NULL | | | wait_age_secs | bigint(21) | YES | | NULL | | | locked_table | varchar(1024) | NO | | | | | locked_index | varchar(1024) | YES | | NULL | | | locked_type | varchar(32) | NO | | | | | waiting_trx_id | varchar(18) | NO | | | | | waiting_trx_started | datetime | NO | | 0000-00-00 00:00:00 | | | waiting_trx_age | time | YES | | NULL | | | waiting_trx_rows_locked | bigint(21) unsigned | NO | | 0 | | | waiting_trx_rows_modified | bigint(21) unsigned | NO | | 0 | | | waiting_pid | bigint(21) unsigned | NO | | 0 | | | waiting_query | longtext | YES | | NULL | | | waiting_lock_id | varchar(81) | NO | | | | | waiting_lock_mode | varchar(32) | NO | | | | | blocking_trx_id | varchar(18) | NO | | | | | blocking_pid | bigint(21) unsigned | NO | | 0 | | | blocking_query | longtext | YES | | NULL | | | blocking_lock_id | varchar(81) | NO | | | | | blocking_lock_mode | varchar(32) | NO | | | | | blocking_trx_started | datetime | NO | | 0000-00-00 00:00:00 | | | blocking_trx_age | time | YES | | NULL | | | blocking_trx_rows_locked | bigint(21) unsigned | NO | | 0 | | | blocking_trx_rows_modified | bigint(21) unsigned | NO | | 0 | | | sql_kill_blocking_query | varchar(32) | YES | | NULL | | | sql_kill_blocking_connection | varchar(26) | YES | | NULL | | +------------------------------+---------------------+------+-----+---------------------+-------+ 26 rows in set (0.01 sec) 19/69
  21. 21. 危険なヤツら sys.innodb̲buffer̲stats̲by̲schema sys.innodb̲buffer̲stats̲by̲table sys.schema̲table̲statistics̲with̲buffer これ注意。 迂闊に触ると死ぬ ことがある。 内部的にinformaition̲schema.innodb̲buffer̲pageを⾒ ているので、でかいバッファプール(経験則だと20GB超) でやるとたまに突き刺さる。 特にCtrl + Cでクエリーを殺すと突き刺さる率が俺の中 では⾼い - 20/69
  22. 22. 名前で判りそうなやつら sys.statements̲with̲temp̲tables sys.statements̲with̲full̲table̲scans sys.statements̲with̲runtimes̲in̲95th̲percentile sys.statements̲with̲sorting sys.statements̲with̲temp̲tables 21/69
  23. 23. sysスキーマというかperformance̲schemaの特徴 コードの中に”instrument”と呼ばれる記録ポイントが埋め 込まれていて、そこを通るたびにメモリー上に記録する 本質的にプロファイラー- ディスクに落とさない(オンメモリーストレージエンジン )でhistoryを増やせば増やすだけメモリー⾷う メモリーは起動時確保(5.6) 5.7で変わったっぽい。メモリー使⽤量が穏やかに。 - 22/69
  24. 24. sysスキーマというかperformance̲schemaの特徴 プロファイラーなので、テーブルにアクセスしても実⾏中の 処理には影響が出ない(実⾏済みの処理の統計が記録されて いるだけ) それに対してi̲sはテーブルアクセス時に各種情報をかき 集める だからinnodb̲buffer̲pageはしこたま重い - デフォルトではプロファイラーっぽい表⽰をするための instrumentは無効 23/69
  25. 25. sys(というかperformance̲schema)がもたらすもの ジェネラルログやスローログ(あるいはlong_query_time) が適切に設定されていないと尻尾を掴むことが難しかった クソ ダメなクエリーが p̲sはダイジェスト単位とはいえ 後追い で検索できる。- テンポラリーテーブルになってるか、ソートした⾏数はいく つだったかはSHOW GLOBAL STATUSしかなかった。 SQLダイジェスト単位 で検索可能。- スローログにも記録されなかった情報がある。今までは 記録したかったらPercona ServerかMariaDBを選んで た。 - 24/69
  26. 26. MySQL 5.6 performance̲schema= ON + sysスキー マ かなり良い MySQL 5.6以降でp̲sが有効なら是非⼊れるべき。- ただしS/N⽐が悪い(ノイズ多い)- 万能ではない 検索できるところまでは良くても ノイズをノイズとして⾒分ける能⼒ そのクエリーをチューニングする能⼒ - は相変わらず必要- MySQL Workbenchはsysの結果を⾷ってグラフを作る機能 を提供したらもっと流⾏ると思う 25/69
  27. 27. sysスキーマのストアドプロシージャ/ファンクション SELECT routine_name, routine_type FROM information_schema.routines WHERE routine_schema= 'sys'で確認できる。 MySQL :: MySQL 5.7 Reference Manual :: 22.4.4 sys Schema Stored Procedures - ⼀番お世話になるのは今までの統計情報の履歴を吹っ⾶ばし てくれる sys.ps_truncate_all_tables ある程度チューニングしたら履歴を吹っ⾶ばして状況を 確認しなおす - sys.create_synonym_dbには感⼼した。 26/69
  28. 28. sysスキーマ on 5.6 mysql/mysql-sys: The MySQL sys schema からcloneし てきて突っ込むだけ。 sys作成時はbinlogオフにするので、マスターで実⾏して もスレーブには作られない。 - $ git clone https://github.com/mysql/mysql-sys $ cd mysql-sys $ mysql -uroot -pxxx < ./sys_56.sql 27/69
  29. 29. generated columnで関数インデックス ⽣成された列にNOT NULL制約、UNIQUE制約もかけられる (疑似チェック制約が作れる) 直接値を投⼊することはできないが、「カラム」としては定 義されるのでSELECT *とかINSERT INTO .. VALUESに注意。 STOREDタイプは全⽂検索にも対応 STORED or VIRTUAL INDEXあり INDEXなし VIRTUAL(デフォルト) ALTER TABLE時 SELECT時 STORED ALTER TABLE時 ALTER TABLE時 28/69
  30. 30. generated columnでcovering index mysql> ALTER TABLE t2 ADD v_lang varchar(16) AS (val->'$.lang'), ADD KEY(v_lang ); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain SELECT v_lang, COUNT(*) AS c FROM t2 GROUP BY v_lang; +----+-------------+-------+------------+-------+---------------+--------+------ ---+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_l en | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+------ ---+------+------+----------+-------------+ | 1 | SIMPLE | t2 | NULL | index | v_lang | v_lang | 1 9 | NULL | 74 | 100.00 | Using index | +----+-------------+-------+------------+-------+---------------+--------+------ ---+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) 29/69
  31. 31. generated columnで全⽂検索 KEY (v_text) WITH PARSER MeCab; t supported for generated columns. ULLTEXT KEY (v_text) WITH PARSER MeCab; Query OK, 100 rows affected (0.37 sec) Records: 100 Duplicates: 0 Warnings: 0 ext) against ('MySQL') LIMIT 3G *************************** 1. row *************************** v_id: 654573993006010368 v_text: "RT @mysql_jp: MySQL 5.7 GAと同時にMySQL Fabricによる構成や各種レプリケーション構成 へのアクセスをシンプルにするMySQL RouterもGAとなっています https://t.co/u3e1E2OSoV #mysql_jp" val->'$.created_at': "Mon Oct 19 14:17:58 +0000 2015" *************************** 2. row *************************** v_id: 654573993006010368 v_text: "RT @mysql_jp: MySQL 5.7 GAおよびMySQL Router GAなどの技術情報はMySQL 最新情報セミナ ー2015秋にてご紹介い たします。ぜひご参加下さい。なお東京は11月9日にも第2回を開催予定です http://t.c o/0Drp08uNbW #m…" val->'$.created_at': "Mon Oct 19 14:22:05 +0000 2015" *************************** 3. row *************************** v_id: 654573993006010368 v_text: "RT @mysql_jp: MySQL 5.7の機能概要はこちらです http://t.co/FZ1Qa7HTfon新機能の一覧 はOracle ACE(MySQL)のyoku0825さんによるこちらの記事をご参照下さい https://t.co/WclCToU1Xrn#mysq…" val->'$.created_at': "Tue Oct 20 02:00:03 +0000 2015" 3 rows in set (0.00 sec) 30/69
  32. 32. 逆パターン # wget http://downloads.mysql.com/docs/world.sql.gz # mysqladmin create world # zcat world.sql.gz | mysql world mysql> SELECT * FROM world.City LIMIT 3; +----+----------+-------------+----------+------------+ | ID | Name | CountryCode | District | Population | +----+----------+-------------+----------+------------+ | 1 | Kabul | AFG | Kabol | 1780000 | | 2 | Qandahar | AFG | Qandahar | 237500 | | 3 | Herat | AFG | Herat | 186800 | +----+----------+-------------+----------+------------+ 3 rows in set (0.00 sec) 31/69
  33. 33. generated columnでJSONにしてしまう mysql> ALTER TABLE City ADD v_json BLOB AS (json_object('name', N ame, 'countrycode', CountryCode, 'district', District, 'populatio n', Population)) STORED; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SELECT v_json FROM City LIMIT 3G *************************** 1. row *************************** v_json: {"name": "Kabul", "district": "Kabol", "population": 1780 000, "countrycode": "AFG"} *************************** 2. row *************************** v_json: {"name": "Qandahar", "district": "Qandahar", "population ": 237500, "countrycode": "AFG"} *************************** 3. row *************************** v_json: {"name": "Herat", "district": "Herat", "population": 1868 00, "countrycode": "AFG"} 3 rows in set (0.00 sec) 32/69
  34. 34. 挙句InnoDB Memcached(JSON型だと結果がおかしく なったのでBLOB型でSTORED) # mysql < /usr/share/mysql/innodb_memcached_config.sql mysql> INSTALL PLUGIN daemon_memcached SONAME "libmemcached.so"; mysql> ALTER TABLE world.City ADD c1 int DEFAULT 0, ADD c2 bigint DEFAUL T 0, ADD c3 int DEFAULT 0; Query OK, 0 rows affected (0.23 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> DELETE FROM innodb_memcache.containers; mysql> INSERT INTO innodb_memcache.containers VALUES ('world_json', 'wor ld', 'City', 'ID', 'v_json', 'c1', 'c2', 'c3', 'PRIMARY'); # service mysqld restart # memcat --servers=127.0.0.1:11211 1 {"name": "Kabul", "district": "Kabol", "population": 1780000, "countryco de": "AFG"} 33/69
  35. 35. 範囲検索でORDER BYまでキーが使えないやつも mysql> ALTER TABLE Country ADD KEY (population, gnp); Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain SELECT Name FROM Country WHERE Population > 100000000 ORD ER BY GNP DESC; +----+-------------+---------+------------+-------+---------------+----- -------+---------+------+------+----------+----------------------------- ----------+ | id | select_type | table | partitions | type | possible_keys | ke y | key_len | ref | rows | filtered | Extr a | +----+-------------+---------+------------+-------+---------------+----- -------+---------+------+------+----------+----------------------------- ----------+ | 1 | SIMPLE | Country | NULL | range | Population | Popu lation | 4 | NULL | 10 | 100.00 | Using index condition; Usin g filesort | +----+-------------+---------+------------+-------+---------------+----- -------+---------+------+------+----------+----------------------------- ----------+ 1 row in set, 1 warning (0.00 sec) 34/69
  36. 36. generated columnならこの通り mysql> ALTER TABLE Country ADD is_over_100mil tinyint AS (IF(Population > 100000 00, 1, 0)), ADD KEY (is_over_100mil, GNP); Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain SELECT Name FROM Country WHERE is_over_100mil = 1 ORDER BY GNP DE SC; +----+-------------+---------+------------+------+----------------+------------- ---+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | ke y | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+----------------+------------- ---+---------+-------+------+----------+-------------+ | 1 | SIMPLE | Country | NULL | ref | is_over_100mil | is_over_100m il | 2 | const | 78 | 100.00 | Using where | +----+-------------+---------+------------+------+----------------+------------- ---+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) 35/69
  37. 37. 昇順と降順が混じったソートも mysql> ALTER TABLE Country ADD KEY (is_over_100mil, GNP, GNPOld); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain SELECT Name FROM Country USE INDEX(is_over_100mil_2) WHERE is_ove r_100mil = 1 ORDER BY GNP ASC, GNPOld DESC LIMIT 1; +----+-------------+---------+------------+------+------------------+----------- -------+---------+-------+------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | ke y | key_len | ref | rows | filtered | Extr a | +----+-------------+---------+------------+------+------------------+----------- -------+---------+-------+------+----------+-----------------------------+ | 1 | SIMPLE | Country | NULL | ref | is_over_100mil_2 | is_over_10 0mil_2 | 2 | const | 78 | 100.00 | Using where; Using filesort | +----+-------------+---------+------------+------+------------------+----------- -------+---------+-------+------+----------+-----------------------------+ 1 row in set, 1 warning (0.00 sec) 36/69
  38. 38. generated columnならこの通り mysql> ALTER TABLE Country ADD invert_gnpold float(10, 2) AS (0 - GNPOld), ADD K EY (is_over_100mil, GNP, invert_gnpold); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain SELECT Name FROM Country WHERE is_over_100mil = 1 ORDER BY GNP AS C, invert_gnpold ASC LIMIT 1; +----+-------------+---------+------------+------+------------------------------ --------------------+------------------+---------+-------+------+----------+---- ---------+ | id | select_type | table | partitions | type | possible_key s | key | key_len | ref | row s | filtered | Extra | +----+-------------+---------+------------+------+------------------------------ --------------------+------------------+---------+-------+------+----------+---- ---------+ | 1 | SIMPLE | Country | NULL | ref | is_over_100mil,is_over_100mil _2,is_over_100mil_3 | is_over_100mil_3 | 2 | const | 78 | 100.00 | Usi ng where | +----+-------------+---------+------------+------+------------------------------ --------------------+------------------+---------+-------+------+----------+---- ---------+ 1 row in set, 1 warning (0.00 sec) 37/69
  39. 39. MySQLにないCHECK制約もgenerated columnならこ の通り mysql> ALTER TABLE Country ADD v_check tinyint AS (IF(GNPOld < 10 00, NULL, 1)) NOT NULL; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> INSERT INTO Country SET Name= 'Dummy', GNPOld= 0.01; ERROR 1048 (23000): Column 'v_check' cannot be null mysql> INSERT INTO Country SET Name= 'Dummy', GNPOld= 1000.01; Query OK, 1 row affected (0.00 sec) 38/69
  40. 40. 似非CHECK制約の仕様 VIRTUALかつセカンダリーインデックスなしだと実⾏時評 価になるので、「今までの似非CHECK制約違反は無視して ALTER TABLE完了、その後の更新には似非CHECK制約適 ⽤」 STOREDまたはセカンダリーインデックスありだとALTER TABLE時に評価されるので「似非CHECK制約違反があると ALTER TABLEがエラーになる」 mysql57> ALTER TABLE Country ADD v_check tinyint AS (IF(GNPOld < 1000, NULL, 1)) STORED NOT NULL; ERROR 1048 (23000): Column 'v_check' cannot be null mysql57> ALTER TABLE Country ADD v_check tinyint AS (IF(GNPOld < 1000, NULL, 1)) NOT NULL, ADD KE Y (v_check); ERROR 1048 (23000): Column 'v_check' cannot be null mysql57> ALTER TABLE Country ADD v_check tinyint AS (IF(GNPOld < 1000, NULL, 1)) NOT NULL; Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 39/69
  41. 41. ⾊々できる クエリーの書き換えは必要になる カラム指定せずにINSERTしようとするお⾏儀の悪いクエ リー - generatedなカラムとフツーのカラムを⾒分けるにはSHOW FULL COLUMNSのExtra列をちゃんとパースしないとダメ mysqldumpとの相性が悪い。。- ORMとの相性も悪い。。 ActiveRecord︕︕1 @kamipo++ => ActiveRecord とMySQL 5.7 - 開発者との相性も(今のところ)悪い。。 もっと広く知られるようになれば問題はなくなると思 う。 - 40/69
  42. 42. GTIDのオンライン有効化 masterslave OFF OFF̲PERMISSIVE ON̲PERMISSIVE ON OFF ○ ○ ○ × OFF̲PERMIS SIVE ○ ○ ○ × ON̲PERMISS IVE × ○ ○ ○ ON × ○ ○ ○ 41/69
  43. 43. GTIDのオンライン有効化 OFF GTIDを振らない。GTIDの振られたイベントが来るとエラる OFF̲PERMISSIVE (5.7から) GTIDを振らないけど、GTIDの振られたイベントが来ても⽂句 を⾔わない ON̲PERMISSIVE (5.7から) GTIDを振るけど、GTIDの振られてないイベントが来ても⽂句 を⾔わない ON GTIDを振るし、GTIDの振られてないイベントが来るとエラる 42/69
  44. 44. GTIDのオンライン有効化 enforce_gtid_consistencyの取りうる値がON, OFF, WARN の3種類 && SET GLOBALでオンライン変更可能になった enforce_gtid_consistency= WARNだと、ON̲PERMISSIVE 以上のgtid̲modeではエラーになるステートメントがワー ニングで出⼒される sys.statements_with_errors_or_warningsで拾っていく スタイル - 43/69
  45. 45. GTIDで嬉しいこと CHANGE MASTER TO master_auto_position= 1できるようにな る master_log_file, master_log_posはgtid_executedなどの 情報からマスターとスレーブが勝⼿にネゴってくれる - さっくり作る、さっくり切り替える、さっくり捨てる に 向いてる データサイズ⼤きくなってくると、「さっくり作る」 がアレになってくるかな。。 Dockerと相性が良かった Dockerはせいぜい開発環境⽤にくらいしか考えてない けど - 44/69
  46. 46. GTIDで嬉しいこと MHAは5.6のクラッシュセーフスレーブ設定だと動かない、 他の選択肢が必要になってきた 松信さんには「いや動くでしょ︖」って⾔われたけど relay_log_info_repository= TABLEだと動かない。。 - mysqlfailover, mysqlrpladminはダメすぎてつらい MySQL Fabricがいいな コマンドラインにしては割とグラフィカルで楽しい- Fabric対応コネクターとかMySQL Routerとかは夢- master_ip_failover_script, report_scriptに相当するも のはない やるとしたらこんな感じのまた書く気がする Redis Sentinelを運⽤してみたお話 - 45/69
  47. 47. 続いて雑 なやつ 46/69
  48. 48. FAQ あの資料雑なの︖ 雑です 雑 #とは Executorに仕事させないためにインデックス使ってね、だけ で、肝⼼のインデックスの貼り⽅書いてないあたりが 書けよ だってめんどくさいんだもん 47/69
  49. 49. 前回のあ らすじ 48/69
  50. 50. クエリーのライフサイクル client connection_handling parser optimizer executor handler storage_engine application mysqld 49/69
  51. 51. クエリーのライフサイクル Connection Handling Parser Optimizer Executor Handler Storage Engine 50/69
  52. 52. どこが遅い︖ connection_handling parser optimizer executor handler storage_engine mysqld 51/69
  53. 53. Executorが遅い理由 MySQLの不得意なことをやろうとしている ⾊々パターンはあるけど、⼀⾔で⾔うならこれにあたる- Executorに仕事をさせたら負け 52/69
  54. 54. MySQLの不得意なこと 複数のインデックスを使いこなす 相関サブクエリー これらはExecutorのお仕事 53/69
  55. 55. 複数のインデックスに弱い理由 MySQLは原則1クエリー内で1つのテーブルあたり1つのイ ンデックスしか使わない インデックスマージという例外が⼀応あるにはあるが、 後付け - そもそもこういう設計思想なんだと思う- テーブルスキャンに強い/弱いはストレージエンジンのレイ ヤー MyISAMはスキャンに強く、InnoDBは弱い- 関数演算、(インデックスで解決できない)フィルター、 (インデックスで解決できない)ソートはExecutorのお仕 事(= 遅い) 54/69
  56. 56. みんな⼤好き Excel⽅眼紙 55/69
  57. 57. ルール 右に動いていいです 左には動いてはいけません 下か上に動いていいですが ⼀度上か下に動くと右には進めなくなります 56/69
  58. 58. ざっくり AND演算⼦は 右に進む OR演算⼦, IN演算⼦, 不等号演算⼦は 下に進む エグゼキューターが仕事するためには ⼀番右にいないとい けない 57/69
  59. 59. JOIN︖ ⽅眼紙作れなかった。。 58/69
  60. 60. MySQLでも⼈間でも最初に考える(であろう)こと WHEREにインデックスを利かせる 下にしか進めなくなっても困らないくらいの範囲ならエ グゼキューターでも戦える - 59/69
  61. 61. MySQLでも⼈間でも次に考える(であろう)こと WHEREにインデックスを利かせてORDER BYにインデックスを 利かせる ⼗分範囲を狭めてから、「上から下に順番になぞる」- 60/69
  62. 62. WHERE狙いのキー, ORDER BY狙いのキー WHEREとORDER BY .. LIMITを同時にカバーできるインデック スが作成できない時に WHEREをストレージエンジンに任せてフェッチする⾏を減 らしてファイルソートをExecutorにやらせるか - ORDER BY .. LIMITをストレージエンジンに任せて ExecutorにフィルターさせつつLIMITでループを抜けるの を期待するか - 61/69
  63. 63. WHERE狙いのキー, ORDER BY狙いのキー 「JOINが遅い」はJOINが遅いんじゃなくてJOINの参照表で ソートしようとしてExecutorがソートしちゃってるケース あとはオプティマイザーにバグがあって、駆動表を Executorがフィルターして参照表の参照回数が減る可能 性を考慮していない。 - MySQLにはヒストグラム統計がないので、WHERE狙いと ORDER BY狙いのどちらが本当に速いのかMySQLは知らない (憶えてない) See also, http://www.slideshare.net/yoku0825/ whereorder-by 62/69
  64. 64. ⼈間がAIに今のところ勝てること ⼈間は得てして 正しい 統計情報を知っているケースがある サービスの特性(⼥性は少ないからWHERE gender= 'F' ORDER BY birthdayはWHEREで狙った⽅がいいだろうし、 WHERE gender= 'M' ORDER BY birthdayはORDER BY狙いの ⽅が良いだろう、とか) - サンプリングでない実際の分布(InnoDBの統計情報は飽 くまでサンプリングなのでたまに踏み外す) - LIMITの考慮(5.6とそれ以前のMySQLはORDER BY .. LIMITの時でも積極的にWHEREを狙う) - 63/69
  65. 65. オプティマイザーの強化 MySQL 5.6 Multi-Range Read, Batched Key Access(デフォルトOFF), Semi-JOIN, Materialized MySQL 5.7 ORDER BY .. LIMIT Optimization, Configurable cost Model, JOIN optimization ホントに賢くなってて、「ほらMySQLバカでしょ︖」って デモがしづらくなった 64/69
  66. 66. 相関サブクエリー 外側のクエリーが内側のサブクエリーの条件になっているケ ース 内側のサブクエリーが外側の条件になるケースはMySQL ⽤語では単にサブクエリーと呼ぶ(個⼈的には明確に区 別するために「キャッシャブルなサブクエリー」と呼ん でる) - しかしWHERE INはキャッシャブルなサブクエリーのはず でも相関サブクエリーになるというダメ仕様が5.5とそれ 以前にあってだな。。 - 65/69
  67. 67. シンプルな話、外側のクエリーに1⾏マッチするたびに内 側のクエリーを実⾏してしまう ⾏のフェッチのみならず、クエリーまるごと実⾏する。 外側テーブルのサイズが⼤きくなると加速度的に遅くな るのはこのせい。 - mysql> SHOW PROFILE; +--------------------------------+----------+ | Status | Duration | +--------------------------------+----------+ | starting | 0.000023 | | .. | .. | | executing | 0.000006 | | Sending data | 0.000006 | | .. | .. | | executing | 0.000005 | | Sending data | 0.000014 | | executing | 0.000005 | | Sending data | 0.000047 | | end | 0.000010 | | .. | .. | +--------------------------------+----------+ 2000025 rows in set (4.78 sec) 66/69
  68. 68. 相関サブクエリーの書き換え⽅ 7割⽅JOINに書き換えられる JOINに落とし込んでUSE INDEXまでつければ5.5とそれ以 前でも戦える - どうしてもロジックとしてJOINに書き換えられないケース もある CREATE TEMPORARY TABLEを使って落とし込むのがいい テンポラリーテーブルはクラッシュアンセーフ( 更新 ステートメントには使わない︕ ) そもそもクラッシュアンセーフだから迷わずMyISAM を選んでいい ⼤した量でなくてもインデックス張る。劇的に違う - のがいい、というか、SQLだけでやるにはそれしかない- 67/69
  69. 69. 書き換える時に必要なもの 再帰テスト 無いと死ぬ- ポジション上、返す結果が変わらないことの担保をしな いといけない パラメーター渡して、新旧クエリーの2パターン投げて 結果を⽐較するだけでも取り敢えずいいんだ 最近はコマンドラインクライアントで REPEATABLE-READでトランザクション内でやればオ ブジェクト⽐較するだけで済む 分布に極端な偏りがあるパラメーターはいくつかバリ エーションを⽤意した⽅がいい(同じ形をしていても パラメーターで実⾏計画は変わる) - 68/69
  70. 70. Questions and/or Suggestions? 69/69

×