ペパボ de mysql
TRANSCRIPT
あんちぽくんさん「ネタは雑 + 5.7がいいです︕」
いつのまにかあんちぽちゃんさんになっていたことに気付かなかった
2016/02/22
yoku0825の中の⼈Pepabo de MySQL
\こんにちは/
yoku0825@とある企業のDBA の中の⼈オラクれない-ポスグれない-マイエスキューエる-
Twitter: @yoku0825Blog: ⽇々の覚書Oracle ACE DetailsMySQL 5.7 Community Contributor Award
1/69
このスライド書いた中の⼈です
2/69
ちなみに
3/69
というわけで安⼼して 地雷友達 5.7使っていいです
よ :)4/69
なお
ウチは明⽇ 5.7のカットオーバーがあります明⽇からまた5.7についてぐちぐち呟いてたら痛い目⾒たんだと思ってください
5/69
ざっくり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
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
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
General Available #とは
9/69
MySQL 5.7について雑に⾔っておきたいこと
取り敢えず⼀通りなアレは slideshare と ブログ に置いてきました気に⼊ってる機能だけ伝えます
10/69
お気に⼊り
sysスキーマ を 5.6で 使うこと
generated columnGTIDのオンライン有効化
11/69
sysスキーマ
元 ps̲helperリポジトリーの名前はdbahelper。。-
performance̲schema, information̲schemaのビューと、performance̲schema関連の設定をゴニョるプロシージャの詰め合わせなのでperformance_schema= ONが⼤前提-合わせて使いたいinnodb_monitor_enable= all-実は5.6から使える(使ってる。便利)-
12/69
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
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
sys.schema̲unused̲indexes
名前そのものズバリ、使われてない(統計開始から⼀度も使われていない)インデックスを⼀覧してくれる。ただし、performance_schema.setup_actors, performance_schema.setup_objects, performance_schema.setup_instruments= 'wait/io/table/
sql/handler'あたりの設定に依存する。吊るしで使ってるならフツーのテーブルではmysqldの起動時から全部統計が有効になっている。アクセス具合によるので、マスターで使ってなくてスレーブで使ってるインデックスとかあるので注意。
15/69
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
sys.statements̲with̲errors̲or̲warnings
カジュアルに桁切り詰めワーニングを無視してるステートメントとか⼀網打尽にできる。--gtid-enforce-consistency= WARNと組み合わせて使うと夢が広がる。
17/69
sys.innodb̲lock̲waits
SH2さんのステートメントとの別離の時MySQL InnoDBにおけるロック競合の解析⼿順 - SH2の⽇記
-
KILLするためのステートメントも出⼒してくれるあたりがロックしかもKILL QUERYとKILLを両⽅出⼒する芸の細かさ-
18/69
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
危険なヤツら
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
名前で判りそうなやつら
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
sysスキーマというかperformance̲schemaの特徴
コードの中に”instrument”と呼ばれる記録ポイントが埋め込まれていて、そこを通るたびにメモリー上に記録する本質的にプロファイラー-ディスクに落とさない(オンメモリーストレージエンジン)でhistoryを増やせば増やすだけメモリー⾷うメモリーは起動時確保(5.6)5.7で変わったっぽい。メモリー使⽤量が穏やかに。
-
22/69
sysスキーマというかperformance̲schemaの特徴
プロファイラーなので、テーブルにアクセスしても実⾏中の処理には影響が出ない(実⾏済みの処理の統計が記録されているだけ)それに対してi̲sはテーブルアクセス時に各種情報をかき集めるだからinnodb̲buffer̲pageはしこたま重い
-
デフォルトではプロファイラーっぽい表⽰をするためのinstrumentは無効
23/69
sys(というかperformance̲schema)がもたらすもの
ジェネラルログやスローログ(あるいはlong_query_time)が適切に設定されていないと尻尾を掴むことが難しかった クソ ダメなクエリーが
p̲sはダイジェスト単位とはいえ 後追い で検索できる。-テンポラリーテーブルになってるか、ソートした⾏数はいくつだったかはSHOW GLOBAL STATUSしかなかった。
SQLダイジェスト単位 で検索可能。-スローログにも記録されなかった情報がある。今までは記録したかったらPercona ServerかMariaDBを選んでた。
-
24/69
MySQL 5.6 performance̲schema= ON + sysスキーマ
かなり良いMySQL 5.6以降でp̲sが有効なら是非⼊れるべき。-ただしS/N⽐が悪い(ノイズ多い)-
万能ではない検索できるところまでは良くてもノイズをノイズとして⾒分ける能⼒そのクエリーをチューニングする能⼒
-
は相変わらず必要-MySQL Workbenchはsysの結果を⾷ってグラフを作る機能を提供したらもっと流⾏ると思う
25/69
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
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
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
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
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
逆パターン
# 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
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
挙句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
範囲検索で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
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
昇順と降順が混じったソートも
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
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
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
似非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
⾊々できる
クエリーの書き換えは必要になるカラム指定せずにINSERTしようとするお⾏儀の悪いクエリー
-
generatedなカラムとフツーのカラムを⾒分けるにはSHOW
FULL COLUMNSのExtra列をちゃんとパースしないとダメmysqldumpとの相性が悪い。。-ORMとの相性も悪い。。
ActiveRecord︕︕1 @kamipo++ => ActiveRecordとMySQL 5.7
-
開発者との相性も(今のところ)悪い。。もっと広く知られるようになれば問題はなくなると思う。
-
40/69
GTIDのオンライン有効化
masterslave OFF OFF̲PERMISSIVE ON̲PERMISSIVE ON
OFF ○ ○ ○ ×
OFF̲PERMISSIVE
○ ○ ○ ×
ON̲PERMISSIVE
× ○ ○ ○
ON × ○ ○ ○
41/69
GTIDのオンライン有効化
OFFGTIDを振らない。GTIDの振られたイベントが来るとエラる
OFF̲PERMISSIVE (5.7から)GTIDを振らないけど、GTIDの振られたイベントが来ても⽂句を⾔わない
ON̲PERMISSIVE (5.7から)GTIDを振るけど、GTIDの振られてないイベントが来ても⽂句を⾔わない
ONGTIDを振るし、GTIDの振られてないイベントが来るとエラる
42/69
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
GTIDで嬉しいこと
CHANGE MASTER TO master_auto_position= 1できるようになる
master_log_file, master_log_posはgtid_executedなどの情報からマスターとスレーブが勝⼿にネゴってくれる
-
さっくり作る、さっくり切り替える、さっくり捨てる に向いてるデータサイズ⼤きくなってくると、「さっくり作る」がアレになってくるかな。。Dockerと相性が良かったDockerはせいぜい開発環境⽤にくらいしか考えてないけど
-
44/69
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
続いて雑なやつ
46/69
FAQ
あの資料雑なの︖雑です雑 #とは
Executorに仕事させないためにインデックス使ってね、だけで、肝⼼のインデックスの貼り⽅書いてないあたりが書けよだってめんどくさいんだもん
47/69
前回のあらすじ
48/69
クエリーのライフサイクル
client
connection_handling parser optimizer executor
handler storage_engine
application
mysqld
49/69
クエリーのライフサイクル
Connection HandlingParserOptimizerExecutorHandlerStorage Engine
50/69
どこが遅い︖
connection_handling parser optimizer executor
handler storage_engine
mysqld
51/69
Executorが遅い理由
MySQLの不得意なことをやろうとしている⾊々パターンはあるけど、⼀⾔で⾔うならこれにあたる-
Executorに仕事をさせたら負け
52/69
MySQLの不得意なこと
複数のインデックスを使いこなす相関サブクエリーこれらはExecutorのお仕事
53/69
複数のインデックスに弱い理由
MySQLは原則1クエリー内で1つのテーブルあたり1つのインデックスしか使わないインデックスマージという例外が⼀応あるにはあるが、後付け
-
そもそもこういう設計思想なんだと思う-テーブルスキャンに強い/弱いはストレージエンジンのレイヤー
MyISAMはスキャンに強く、InnoDBは弱い-関数演算、(インデックスで解決できない)フィルター、(インデックスで解決できない)ソートはExecutorのお仕事(= 遅い)
54/69
みんな⼤好きExcel⽅眼紙
55/69
ルール
右に動いていいです左には動いてはいけません下か上に動いていいですが⼀度上か下に動くと右には進めなくなります
56/69
ざっくり
AND演算⼦は 右に進むOR演算⼦, IN演算⼦, 不等号演算⼦は 下に進むエグゼキューターが仕事するためには ⼀番右にいないといけない
57/69
JOIN︖
⽅眼紙作れなかった。。
58/69
MySQLでも⼈間でも最初に考える(であろう)こと
WHEREにインデックスを利かせる下にしか進めなくなっても困らないくらいの範囲ならエグゼキューターでも戦える
-
59/69
MySQLでも⼈間でも次に考える(であろう)こと
WHEREにインデックスを利かせてORDER BYにインデックスを利かせる⼗分範囲を狭めてから、「上から下に順番になぞる」-
60/69
WHERE狙いのキー, ORDER BY狙いのキー
WHEREとORDER BY .. LIMITを同時にカバーできるインデックスが作成できない時に
WHEREをストレージエンジンに任せてフェッチする⾏を減らしてファイルソートをExecutorにやらせるか
-
ORDER BY .. LIMITをストレージエンジンに任せてExecutorにフィルターさせつつLIMITでループを抜けるのを期待するか
-
61/69
WHERE狙いのキー, ORDER BY狙いのキー
「JOINが遅い」はJOINが遅いんじゃなくてJOINの参照表でソートしようとしてExecutorがソートしちゃってるケースあとはオプティマイザーにバグがあって、駆動表をExecutorがフィルターして参照表の参照回数が減る可能性を考慮していない。
-
MySQLにはヒストグラム統計がないので、WHERE狙いとORDER BY狙いのどちらが本当に速いのかMySQLは知らない(憶えてない)See also, http://www.slideshare.net/yoku0825/whereorder-by
62/69
⼈間が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
オプティマイザーの強化
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
相関サブクエリー
外側のクエリーが内側のサブクエリーの条件になっているケース内側のサブクエリーが外側の条件になるケースはMySQL⽤語では単にサブクエリーと呼ぶ(個⼈的には明確に区別するために「キャッシャブルなサブクエリー」と呼んでる)
-
しかしWHERE INはキャッシャブルなサブクエリーのはずでも相関サブクエリーになるというダメ仕様が5.5とそれ以前にあってだな。。
-
65/69
シンプルな話、外側のクエリーに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
相関サブクエリーの書き換え⽅
7割⽅JOINに書き換えられるJOINに落とし込んでUSE INDEXまでつければ5.5とそれ以前でも戦える
-
どうしてもロジックとしてJOINに書き換えられないケースもある
CREATE TEMPORARY TABLEを使って落とし込むのがいいテンポラリーテーブルはクラッシュアンセーフ( 更新ステートメントには使わない︕ )そもそもクラッシュアンセーフだから迷わずMyISAMを選んでいい⼤した量でなくてもインデックス張る。劇的に違う
-
のがいい、というか、SQLだけでやるにはそれしかない- 67/69
書き換える時に必要なもの
再帰テスト無いと死ぬ-ポジション上、返す結果が変わらないことの担保をしないといけないパラメーター渡して、新旧クエリーの2パターン投げて結果を⽐較するだけでも取り敢えずいいんだ最近はコマンドラインクライアントでREPEATABLE-READでトランザクション内でやればオブジェクト⽐較するだけで済む分布に極端な偏りがあるパラメーターはいくつかバリエーションを⽤意した⽅がいい(同じ形をしていてもパラメーターで実⾏計画は変わる)gender= ʻMʼとgender = ʻFʼみたいなパターン
-
68/69
Questions and/or
Suggestions?69/69