ペパボ de mysql

70
あんちぽくんさん「ネタは雑 + 5.7 がいいです︕」 いつのまにかあんちぽちゃんさんになっていたことに気付かなかっ 2016/02/22 yoku0825の中の⼈ Pepabo de MySQL

Upload: yoku0825

Post on 08-Jan-2017

5.776 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: ペパボ de MySQL

あんちぽくんさん「ネタは雑 + 5.7がいいです︕」

いつのまにかあんちぽちゃんさんになっていたことに気付かなかった

2016/02/22

yoku0825の中の⼈Pepabo de MySQL

Page 2: ペパボ de MySQL

\こんにちは/

yoku0825@とある企業のDBA の中の⼈オラクれない-ポスグれない-マイエスキューエる-

Twitter: @yoku0825Blog: ⽇々の覚書Oracle ACE DetailsMySQL 5.7 Community Contributor Award

1/69

Page 3: ペパボ de MySQL

このスライド書いた中の⼈です

2/69

Page 4: ペパボ de MySQL

ちなみに

3/69

Page 5: ペパボ de MySQL

というわけで安⼼して 地雷友達 5.7使っていいです

よ :)4/69

Page 6: ペパボ de MySQL

なお

ウチは明⽇ 5.7のカットオーバーがあります明⽇からまた5.7についてぐちぐち呟いてたら痛い目⾒たんだと思ってください

5/69

Page 7: ペパボ de MySQL

ざっくりMySQL 5.7

2013/02 MySQL 5.6.10 GA2013/04 MySQL 5.7.1 DMR112015/03 MySQL 5.7.6 DMR162015/04 MySQL 5.7.7 RC2015/08 MySQL 5.7.8 RC2015/10 MySQL 5.7.9 GA2015/12 MySQL 5.7.10 GA2016/02 MySQL 5.7.11 GA

6/69

Page 8: ペパボ de MySQL

MySQL 5.7

2015/04 MySQL 5.7.7 RCここまではまあいい

2015/08 MySQL 5.7.8 RCJSON型, virtual generated columnの拡張, InnoDB Page Compression

2015/10 MySQL 5.7.9 GAinnodb_default_row_format, JSON -> operator, innodb_numa_interleave

2015/12 MySQL 5.7.10 GABug Fix

2016/02 MySQL 5.7.11 GAInnoDB Tablespace Encryption 7/69

Page 9: ペパボ de MySQL

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 GABug Fix

2016/02 MySQL 5.7.11 GA新機能!!

8/69

Page 10: ペパボ de MySQL

General Available #とは

9/69

Page 11: ペパボ de MySQL

MySQL 5.7について雑に⾔っておきたいこと

取り敢えず⼀通りなアレは slideshare と ブログ に置いてきました気に⼊ってる機能だけ伝えます

10/69

Page 12: ペパボ de MySQL

お気に⼊り

sysスキーマ を 5.6で 使うこと

generated columnGTIDのオンライン有効化

11/69

Page 13: ペパボ de MySQL

sysスキーマ

元 ps̲helperリポジトリーの名前はdbahelper。。-

performance̲schema, information̲schemaのビューと、performance̲schema関連の設定をゴニョるプロシージャの詰め合わせなのでperformance_schema= ONが⼤前提-合わせて使いたいinnodb_monitor_enable= all-実は5.6から使える(使ってる。便利)-

12/69

Page 14: ペパボ de MySQL

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

Page 15: ペパボ de MySQL

sys.schema̲index̲statistics

どのインデックスを使ってどれだけハンドラーが呼ばれたか、そのレイテンシーの合計は…というのが⾒られる。あんまり使われてないインデックスをあぶりだすのは後述のsys.schema_unused_indexesビューでもいける。

mysql> SELECT * FROM sys.schema_index_statistics\G*************************** 1. row *************************** table_schema: xxxxxxx table_name: quiz_xxxxxxxx index_name: idx_xxxxxxxxx rows_selected: 352020501193select_latency: 5.83 d rows_inserted: 0insert_latency: 0 ps rows_updated: 0update_latency: 0 ps rows_deleted: 0delete_latency: 0 ps1 row in set (0.04 sec)

14/69

Page 16: ペパボ de MySQL

sys.schema̲unused̲indexes

名前そのものズバリ、使われてない(統計開始から⼀度も使われていない)インデックスを⼀覧してくれる。ただし、performance_schema.setup_actors, performance_schema.setup_objects, performance_schema.setup_instruments= 'wait/io/table/

sql/handler'あたりの設定に依存する。吊るしで使ってるならフツーのテーブルではmysqldの起動時から全部統計が有効になっている。アクセス具合によるので、マスターで使ってなくてスレーブで使ってるインデックスとかあるので注意。

15/69

Page 17: ペパボ de MySQL

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_analysis\G

*************************** 2. row ***************************

query: SELECT `xxxx_REPORT` . `UNIQU ... S `PURE_TOTAL_xxxx` , SUM ( 16/69

Page 18: ペパボ de MySQL

sys.statements̲with̲errors̲or̲warnings

カジュアルに桁切り詰めワーニングを無視してるステートメントとか⼀網打尽にできる。--gtid-enforce-consistency= WARNと組み合わせて使うと夢が広がる。

17/69

Page 19: ペパボ de MySQL

sys.innodb̲lock̲waits

SH2さんのステートメントとの別離の時MySQL InnoDBにおけるロック競合の解析⼿順 - SH2の⽇記

-

KILLするためのステートメントも出⼒してくれるあたりがロックしかもKILL QUERYとKILLを両⽅出⼒する芸の細かさ-

18/69

Page 20: ペパボ de MySQL

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

Page 21: ペパボ de MySQL

危険なヤツら

sys.innodb̲buffer̲stats̲by̲schemasys.innodb̲buffer̲stats̲by̲tablesys.schema̲table̲statistics̲with̲bufferこれ注意。 迂闊に触ると死ぬ ことがある。内部的にinformaition̲schema.innodb̲buffer̲pageを⾒ているので、でかいバッファプール(経験則だと20GB超)でやるとたまに突き刺さる。特にCtrl + Cでクエリーを殺すと突き刺さる率が俺の中では⾼い

-

20/69

Page 22: ペパボ de MySQL

名前で判りそうなやつら

sys.statements̲with̲temp̲tablessys.statements̲with̲full̲table̲scanssys.statements̲with̲runtimes̲in̲95th̲percentilesys.statements̲with̲sortingsys.statements̲with̲temp̲tables

21/69

Page 23: ペパボ de MySQL

sysスキーマというかperformance̲schemaの特徴

コードの中に”instrument”と呼ばれる記録ポイントが埋め込まれていて、そこを通るたびにメモリー上に記録する本質的にプロファイラー-ディスクに落とさない(オンメモリーストレージエンジン)でhistoryを増やせば増やすだけメモリー⾷うメモリーは起動時確保(5.6)5.7で変わったっぽい。メモリー使⽤量が穏やかに。

-

22/69

Page 24: ペパボ de MySQL

sysスキーマというかperformance̲schemaの特徴

プロファイラーなので、テーブルにアクセスしても実⾏中の処理には影響が出ない(実⾏済みの処理の統計が記録されているだけ)それに対してi̲sはテーブルアクセス時に各種情報をかき集めるだからinnodb̲buffer̲pageはしこたま重い

-

デフォルトではプロファイラーっぽい表⽰をするためのinstrumentは無効

23/69

Page 25: ペパボ de MySQL

sys(というかperformance̲schema)がもたらすもの

ジェネラルログやスローログ(あるいはlong_query_time)が適切に設定されていないと尻尾を掴むことが難しかった クソ ダメなクエリーが

p̲sはダイジェスト単位とはいえ 後追い で検索できる。-テンポラリーテーブルになってるか、ソートした⾏数はいくつだったかはSHOW GLOBAL STATUSしかなかった。

SQLダイジェスト単位 で検索可能。-スローログにも記録されなかった情報がある。今までは記録したかったらPercona ServerかMariaDBを選んでた。

-

24/69

Page 26: ペパボ de MySQL

MySQL 5.6 performance̲schema= ON + sysスキーマ

かなり良いMySQL 5.6以降でp̲sが有効なら是非⼊れるべき。-ただしS/N⽐が悪い(ノイズ多い)-

万能ではない検索できるところまでは良くてもノイズをノイズとして⾒分ける能⼒そのクエリーをチューニングする能⼒

-

は相変わらず必要-MySQL Workbenchはsysの結果を⾷ってグラフを作る機能を提供したらもっと流⾏ると思う

25/69

Page 27: ペパボ de MySQL

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

Page 28: ペパボ de MySQL

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

Page 29: ペパボ de MySQL

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

Page 30: ペパボ de MySQL

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_len | ref | rows | filtered | Extra |+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+| 1 | SIMPLE | t2 | NULL | index | v_lang | v_lang | 19 | NULL | 74 | 100.00 | Using index |+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-------------+1 row in set, 1 warning (0.00 sec)

29/69

Page 31: ペパボ de MySQL

generated columnで全⽂検索

mysql> ALTER TABLE t2 ADD v_text text AS (val->'$.text'), ADD FULLTEXTERROR 3106 (HY000): 'Fulltext index on virtual generated column' is no

mysql> ALTER TABLE t2 ADD v_text text AS (val->'$.text') STORED, ADD FQuery OK, 100 rows affected (0.37 sec)Records: 100 Duplicates: 0 Warnings: 0

mysql> SELECT v_id, v_text, val->'$.created_at' FROM t2 WHERE match(v_t*************************** 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.co/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/FZ1Qa7HTfo\n新機能の一覧はOracle ACE(MySQL)のyoku0825さんによるこちらの記事をご参照下さい https://t.co/WclCToU1Xr\n#mysq…"val->'$.created_at': "Tue Oct 20 02:00:03 +0000 2015"3 rows in set (0.00 sec)

30/69

Page 32: ペパボ de MySQL

逆パターン

# 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

Page 33: ペパボ de MySQL

generated columnでJSONにしてしまう

mysql> ALTER TABLE City ADD v_json BLOB AS (json_object('name', Name, 'countrycode', CountryCode, 'district', District, 'population', Population)) STORED;Query OK, 0 rows affected (0.02 sec)Records: 0 Duplicates: 0 Warnings: 0

mysql> SELECT v_json FROM City LIMIT 3\G*************************** 1. row ***************************v_json: {"name": "Kabul", "district": "Kabol", "population": 1780000, "countrycode": "AFG"}*************************** 2. row ***************************v_json: {"name": "Qandahar", "district": "Qandahar", "population": 237500, "countrycode": "AFG"}*************************** 3. row ***************************v_json: {"name": "Herat", "district": "Herat", "population": 186800, "countrycode": "AFG"}3 rows in set (0.00 sec)

32/69

Page 34: ペパボ de MySQL

挙句InnoDB Memcached(JSON型だと結果がおかしくなったのでBLOB型でSTORED)

# mysql < /usr/share/mysql/innodb_memcached_config.sqlmysql> INSTALL PLUGIN daemon_memcached SONAME "libmemcached.so";

mysql> ALTER TABLE world.City ADD c1 int DEFAULT 0, ADD c2 bigint DEFAULT 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', 'world', '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, "countrycode": "AFG"}

33/69

Page 35: ペパボ de MySQL

範囲検索で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 ORDER BY GNP DESC;+----+-------------+---------+------------+-------+---------------+------------+---------+------+------+----------+---------------------------------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+-------+---------------+------------+---------+------+------+----------+---------------------------------------+| 1 | SIMPLE | Country | NULL | range | Population | Population | 4 | NULL | 10 | 100.00 | Using index condition; Using filesort |+----+-------------+---------+------------+-------+---------------+------------+---------+------+------+----------+---------------------------------------+1 row in set, 1 warning (0.00 sec)

34/69

Page 36: ペパボ de MySQL

generated columnならこの通り

mysql> ALTER TABLE Country ADD is_over_100mil tinyint AS (IF(Population > 10000000, 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 DESC;+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+| 1 | SIMPLE | Country | NULL | ref | is_over_100mil | is_over_100mil | 2 | const | 78 | 100.00 | Using where |+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+-------------+1 row in set, 1 warning (0.00 sec)

35/69

Page 37: ペパボ de MySQL

昇順と降順が混じったソートも

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_over_100mil = 1 ORDER BY GNP ASC, GNPOld DESC LIMIT 1;+----+-------------+---------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+-------------+---------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+| 1 | SIMPLE | Country | NULL | ref | is_over_100mil_2 | is_over_100mil_2 | 2 | const | 78 | 100.00 | Using where; Using filesort |+----+-------------+---------+------------+------+------------------+------------------+---------+-------+------+----------+-----------------------------+1 row in set, 1 warning (0.00 sec)

36/69

Page 38: ペパボ de MySQL

generated columnならこの通り

mysql> ALTER TABLE Country ADD invert_gnpold float(10, 2) AS (0 - GNPOld), ADD KEY (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 ASC, invert_gnpold ASC LIMIT 1;+----+-------------+---------+------------+------+--------------------------------------------------+------------------+---------+-------+------+----------+-------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | 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 | Using where |+----+-------------+---------+------------+------+--------------------------------------------------+------------------+---------+-------+------+----------+-------------+1 row in set, 1 warning (0.00 sec)

37/69

Page 39: ペパボ de MySQL

MySQLにないCHECK制約もgenerated columnならこの通り

mysql> ALTER TABLE Country ADD v_check tinyint AS (IF(GNPOld < 1000, 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

Page 40: ペパボ de MySQL

似非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 KEY (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

Page 41: ペパボ de MySQL

⾊々できる

クエリーの書き換えは必要になるカラム指定せずにINSERTしようとするお⾏儀の悪いクエリー

-

generatedなカラムとフツーのカラムを⾒分けるにはSHOW

FULL COLUMNSのExtra列をちゃんとパースしないとダメmysqldumpとの相性が悪い。。-ORMとの相性も悪い。。

ActiveRecord︕︕1 @kamipo++ => ActiveRecordとMySQL 5.7

-

開発者との相性も(今のところ)悪い。。もっと広く知られるようになれば問題はなくなると思う。

-

40/69

Page 42: ペパボ de MySQL

GTIDのオンライン有効化

masterslave OFF OFF̲PERMISSIVE ON̲PERMISSIVE ON

OFF ○ ○ ○ ×

OFF̲PERMISSIVE

○ ○ ○ ×

ON̲PERMISSIVE

× ○ ○ ○

ON × ○ ○ ○

41/69

Page 43: ペパボ de MySQL

GTIDのオンライン有効化

OFFGTIDを振らない。GTIDの振られたイベントが来るとエラる

OFF̲PERMISSIVE (5.7から)GTIDを振らないけど、GTIDの振られたイベントが来ても⽂句を⾔わない

ON̲PERMISSIVE (5.7から)GTIDを振るけど、GTIDの振られてないイベントが来ても⽂句を⾔わない

ONGTIDを振るし、GTIDの振られてないイベントが来るとエラる

42/69

Page 44: ペパボ de MySQL

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

Page 45: ペパボ de MySQL

GTIDで嬉しいこと

CHANGE MASTER TO master_auto_position= 1できるようになる

master_log_file, master_log_posはgtid_executedなどの情報からマスターとスレーブが勝⼿にネゴってくれる

-

さっくり作る、さっくり切り替える、さっくり捨てる に向いてるデータサイズ⼤きくなってくると、「さっくり作る」がアレになってくるかな。。Dockerと相性が良かったDockerはせいぜい開発環境⽤にくらいしか考えてないけど

-

44/69

Page 46: ペパボ de MySQL

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

Page 47: ペパボ de MySQL

続いて雑なやつ

46/69

Page 48: ペパボ de MySQL

FAQ

あの資料雑なの︖雑です雑 #とは

Executorに仕事させないためにインデックス使ってね、だけで、肝⼼のインデックスの貼り⽅書いてないあたりが書けよだってめんどくさいんだもん

47/69

Page 49: ペパボ de MySQL

前回のあらすじ

48/69

Page 50: ペパボ de MySQL

クエリーのライフサイクル

client

connection_handling parser optimizer executor

handler storage_engine

application

mysqld

49/69

Page 51: ペパボ de MySQL

クエリーのライフサイクル

Connection HandlingParserOptimizerExecutorHandlerStorage Engine

50/69

Page 52: ペパボ de MySQL

どこが遅い︖

connection_handling parser optimizer executor

handler storage_engine

mysqld

51/69

Page 53: ペパボ de MySQL

Executorが遅い理由

MySQLの不得意なことをやろうとしている⾊々パターンはあるけど、⼀⾔で⾔うならこれにあたる-

Executorに仕事をさせたら負け

52/69

Page 54: ペパボ de MySQL

MySQLの不得意なこと

複数のインデックスを使いこなす相関サブクエリーこれらはExecutorのお仕事

53/69

Page 55: ペパボ de MySQL

複数のインデックスに弱い理由

MySQLは原則1クエリー内で1つのテーブルあたり1つのインデックスしか使わないインデックスマージという例外が⼀応あるにはあるが、後付け

-

そもそもこういう設計思想なんだと思う-テーブルスキャンに強い/弱いはストレージエンジンのレイヤー

MyISAMはスキャンに強く、InnoDBは弱い-関数演算、(インデックスで解決できない)フィルター、(インデックスで解決できない)ソートはExecutorのお仕事(= 遅い)

54/69

Page 56: ペパボ de MySQL

みんな⼤好きExcel⽅眼紙

55/69

Page 57: ペパボ de MySQL

ルール

右に動いていいです左には動いてはいけません下か上に動いていいですが⼀度上か下に動くと右には進めなくなります

56/69

Page 58: ペパボ de MySQL

ざっくり

AND演算⼦は 右に進むOR演算⼦, IN演算⼦, 不等号演算⼦は 下に進むエグゼキューターが仕事するためには ⼀番右にいないといけない

57/69

Page 59: ペパボ de MySQL

JOIN︖

⽅眼紙作れなかった。。

58/69

Page 60: ペパボ de MySQL

MySQLでも⼈間でも最初に考える(であろう)こと

WHEREにインデックスを利かせる下にしか進めなくなっても困らないくらいの範囲ならエグゼキューターでも戦える

-

59/69

Page 61: ペパボ de MySQL

MySQLでも⼈間でも次に考える(であろう)こと

WHEREにインデックスを利かせてORDER BYにインデックスを利かせる⼗分範囲を狭めてから、「上から下に順番になぞる」-

60/69

Page 62: ペパボ de MySQL

WHERE狙いのキー, ORDER BY狙いのキー

WHEREとORDER BY .. LIMITを同時にカバーできるインデックスが作成できない時に

WHEREをストレージエンジンに任せてフェッチする⾏を減らしてファイルソートをExecutorにやらせるか

-

ORDER BY .. LIMITをストレージエンジンに任せてExecutorにフィルターさせつつLIMITでループを抜けるのを期待するか

-

61/69

Page 63: ペパボ de MySQL

WHERE狙いのキー, ORDER BY狙いのキー

「JOINが遅い」はJOINが遅いんじゃなくてJOINの参照表でソートしようとしてExecutorがソートしちゃってるケースあとはオプティマイザーにバグがあって、駆動表をExecutorがフィルターして参照表の参照回数が減る可能性を考慮していない。

-

MySQLにはヒストグラム統計がないので、WHERE狙いとORDER BY狙いのどちらが本当に速いのかMySQLは知らない(憶えてない)See also, http://www.slideshare.net/yoku0825/whereorder-by

62/69

Page 64: ペパボ de MySQL

⼈間が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

Page 65: ペパボ de MySQL

オプティマイザーの強化

MySQL 5.6Multi-Range Read, Batched Key Access(デフォルトOFF), Semi-JOIN, Materialized

MySQL 5.7ORDER BY .. LIMIT Optimization, Configurable cost Model, JOIN optimizationホントに賢くなってて、「ほらMySQLバカでしょ︖」ってデモがしづらくなった

64/69

Page 66: ペパボ de MySQL

相関サブクエリー

外側のクエリーが内側のサブクエリーの条件になっているケース内側のサブクエリーが外側の条件になるケースはMySQL⽤語では単にサブクエリーと呼ぶ(個⼈的には明確に区別するために「キャッシャブルなサブクエリー」と呼んでる)

-

しかしWHERE INはキャッシャブルなサブクエリーのはずでも相関サブクエリーになるというダメ仕様が5.5とそれ以前にあってだな。。

-

65/69

Page 67: ペパボ de MySQL

シンプルな話、外側のクエリーに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

Page 68: ペパボ de MySQL

相関サブクエリーの書き換え⽅

7割⽅JOINに書き換えられるJOINに落とし込んでUSE INDEXまでつければ5.5とそれ以前でも戦える

-

どうしてもロジックとしてJOINに書き換えられないケースもある

CREATE TEMPORARY TABLEを使って落とし込むのがいいテンポラリーテーブルはクラッシュアンセーフ( 更新ステートメントには使わない︕ )そもそもクラッシュアンセーフだから迷わずMyISAMを選んでいい⼤した量でなくてもインデックス張る。劇的に違う

-

のがいい、というか、SQLだけでやるにはそれしかない- 67/69

Page 69: ペパボ de MySQL

書き換える時に必要なもの

再帰テスト無いと死ぬ-ポジション上、返す結果が変わらないことの担保をしないといけないパラメーター渡して、新旧クエリーの2パターン投げて結果を⽐較するだけでも取り敢えずいいんだ最近はコマンドラインクライアントでREPEATABLE-READでトランザクション内でやればオブジェクト⽐較するだけで済む分布に極端な偏りがあるパラメーターはいくつかバリエーションを⽤意した⽅がいい(同じ形をしていてもパラメーターで実⾏計画は変わる)gender= ʻMʼとgender = ʻFʼみたいなパターン

-

68/69

Page 70: ペパボ de MySQL

Questions and/or

Suggestions?69/69