Upload
yoku0825
View
3.487
Download
9
Embed Size (px)
DESCRIPTION
2013/04/17 MySQL Casual Talks #4の資料です。 当日ぐだぐだにしゃべったのよりもう少し状況の説明をしています。。遅くなりましたorz
Citation preview
I’m yoku0825,
working as DBA
for the company’s web-service.
My wife’s husband, my son’s father,
lovin’ MySQL and Hannari-Tofu too much.
I can’t even log in to PostgreSQL and Oracle but I’m fine! :)
測ってみた
CPU
Xeon L5520 2.27GHz 1P4C8T
Memory
12GiB(don't know any details)
Storage
RAID Controller(RAID5, 8PD)
read 2450MiB/s, 3950IOPS
write 2450MiB/s, 3900IOPS
ext4 on LVM(noatimeしてない)
my.cnf
[mysqld]
query-cache-type = 0
loose-innodb-buffer-pool-size = 1G
loose-innodb-buffer-pool-instances = 1
loose-innodb-log-file-size = 128M
loose-innodb-file-per-table = 1
loose-innodb-file-format = barracuda
測ってみた
+--------------+------------------+------+-----+-------------------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+-------------------+-------+
| num | int(10) unsigned | NO | | 0 | |
| md5 | varchar(32) | YES | | NULL | |
| sha | varchar(40) | YES | | NULL | |
| old_password | varchar(16) | YES | | NULL | |
| password | varchar(41) | YES | | NULL | |
| timeval | timestamp | NO | | CURRENT_TIMESTAMP | |
+--------------+------------------+------+-----+-------------------+-------+
1行あたり140bytes。
テストデータ1000万行をひたすらINSERTする苦行。
データは1.4GiB。
InnoDB(5.5) - INSERT duration
0:00:00
0:07:12
0:14:24
0:21:36
0:28:48
0:36:00
0:43:12
0:50:24
0:57:36
1:04:48
1:12:00
no key pri(4) pri(4) +
1key(32)
pri(4) +
1key(72)
pri(4) +
1key(88)
pri(4) +
1key(129)
pri(4) +
3key(12)
pri(4) +
3key(76)
pri(4) +
3key(144)
pri(4) +
3key(160)
InnoDB(5.5) - size
0
1,000,000,000
2,000,000,000
3,000,000,000
4,000,000,000
5,000,000,000
6,000,000,000
no key pri(4) pri(4) +
1key(32)
pri(4) +
1key(72)
pri(4) +
1key(88)
pri(4) +
1key(129)
pri(4) +
3key(12)
pri(4) +
3key(76)
pri(4) +
3key(144)
pri(4) +
3key(160)
• 72byteのKey1つと合計76byteになるKey3つで
はそんなに変わらなさそう。
• 1行140bytesしかないテーブルなので、イン
デックスの作成がもろにテーブル全体のサイ
ズに影響する
–社内で出会った炸裂インデックスももともとは.ibd
ファイルでっかくね?からだった。
散弾銃索引退治
• 取り敢えず重複インデックスがあったから消した(基本)
• 本当はもうちょっと削りたい。–なんか上手い具合にINDEXの使われ度合いを測
れないもんかな?
– Percona ServerやMariaDBには、そのインデックス
を使って何行フェッチしたか統計を取る
information_schemaが突っ込まれている。
pt-duplicate-key-entry
Percona Toolkitの1つで、重複インデックスを検出してくれる。
$ pt-duplicate-key-checker S=/usr/mysql/5.5.30/data/mysql.sock,u=tpcc,p=xxxx --database=tpcc
# ######################################################################### tpcc.stock# ########################################################################
# s_w_id is a left-prefix of PRIMARY# Key definitions:# KEY `s_w_id` (`s_w_id`),# PRIMARY KEY (`s_w_id`,`s_i_id`),# Column types:# `s_w_id` smallint(6) not null# `s_i_id` int(11) not null# To remove this duplicate index, execute:ALTER TABLE `tpcc`.`stock` DROP INDEX `s_w_id`;
# ######################################################################### Summary of indexes# ########################################################################
# Size Duplicate Indexes 2# Total Duplicate Indexes 1# Total Indexes 25
http://www.percona.com/doc/percona-toolkit/2.1/pt-duplicate-key-checker.html
まずこれを退治。
information_schema.INNODB_BUFFER_PAGE
• 5.6.2から搭載された
– 5.5.28, 5.1.66にもバックポートされてる(5.1.66はplugin-loadで食わせてやる必要がある)
• InnoDB Buffer Poolにどんなページが載ってるのかがinformation_schemaからアクセスできる。
– これで載ってるインデックスページ調べれば幸せになれるんじゃない?とか思った。
information_schema.INNODB_BUFFER_PAGE_LRU
+---------------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+---------------------+------+-----+---------+-------+
| POOL_ID | bigint(21) unsigned | NO | | 0 | |
| LRU_POSITION | bigint(21) unsigned | NO | | 0 | | LRUリストの位置
| SPACE | bigint(21) unsigned | NO | | 0 | |
| PAGE_NUMBER | bigint(21) unsigned | NO | | 0 | |
| PAGE_TYPE | varchar(64) | YES | | NULL | | UNDO_LOG, INDEX, SYSTEMとか
| FLUSH_TYPE | bigint(21) unsigned | NO | | 0 | |
| FIX_COUNT | bigint(21) unsigned | NO | | 0 | |
| IS_HASHED | varchar(3) | YES | | NULL | |
| NEWEST_MODIFICATION | bigint(21) unsigned | NO | | 0 | | このページを最後に更新したLSN
| OLDEST_MODIFICATION | bigint(21) unsigned | NO | | 0 | | このページを最初に更新したLSN
| ACCESS_TIME | bigint(21) unsigned | NO | | 0 | | 初めてバッファプールに載った時間
| TABLE_NAME | varchar(1024) | YES | | NULL | |
| INDEX_NAME | varchar(1024) | YES | | NULL | |
| NUMBER_RECORDS | bigint(21) unsigned | NO | | 0 | | そのページに載っている行
| DATA_SIZE | bigint(21) unsigned | NO | | 0 | | そのページに載っているByte数
| COMPRESSED_SIZE | bigint(21) unsigned | NO | | 0 | |
| COMPRESSED | varchar(3) | YES | | NULL | |
| IO_FIX | varchar(64) | YES | | NULL | |
| IS_OLD | varchar(3) | YES | | NULL | | OLDページかどうか
| FREE_PAGE_CLOCK | bigint(21) unsigned | NO | | 0 | | ページがLRUリストから落ちるとインクリメント
+---------------------+---------------------+------+-----+---------+-------+
information_schema.INNODB_BUFFER_PAGE_LRU
• とりあえず起動直後の状態をチェック。innodb_buffer_pool_size = 5M(=256ページ)
mysql56> SELECT lru_position, table_name, index_name, number_records AS num, data_size, is_old, newest_modification AS LSN, access_time FROM
innodb_buffer_page_lru WHERE page_type = 'INDEX';
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----+-------------+
| lru_position | table_name | index_name | num | data_size | is_old | LSN | access_time |
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----+-------------+
| 210 | `SYS_IBUF_TABLE` | CLUST_IND | 0 | 0 | NO | 0 | 1153626747 |
| 218 | `mysql`.`innodb_index_stats` | PRIMARY | 46 | 4060 | NO | 0 | 1153626732 |
| 219 | `mysql`.`innodb_table_stats` | PRIMARY | 11 | 635 | NO | 0 | 1153626720 |
| 220 | `SYS_TABLES` | ID_IND | 20 | 613 | NO | 0 | 1153626698 |
| 221 | `SYS_TABLES` | CLUST_IND | 20 | 1513 | NO | 0 | 1153626661 |
| 222 | `SYS_COLUMNS` | CLUST_IND | 103 | 6784 | NO | 0 | 1153626696 |
| 223 | `SYS_DATAFILES` | SYS_DATAFILES_SPACE | 16 | 766 | NO | 0 | 1153626748 |
| 224 | `SYS_TABLESPACES` | SYS_TABLESPACES_SPACE | 16 | 750 | NO | 0 | 1153626748 |
| 225 | `SYS_INDEXES` | CLUST_IND | 25 | 1715 | NO | 0 | 1153626695 |
| 240 | `SYS_FIELDS` | CLUST_IND | 30 | 1274 | NO | 0 | 1153626696 |
| 241 | `SYS_FOREIGN` | FOR_IND | 4 | 148 | NO | 0 | 1153626696 |
| 242 | `SYS_FOREIGN` | ID_IND | 4 | 280 | NO | 0 | 1153626768 |
| 243 | `SYS_FOREIGN_COLS` | ID_IND | 4 | 246 | NO | 0 | 1153626768 |
| 244 | `SYS_FOREIGN` | REF_IND | 4 | 152 | NO | 0 | 1153626696 |
| 245 | `test`.`item` | PRIMARY | 17 | 938 | NO | 0 | 1153626777 |
| 247 | `mysql`.`slave_master_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626963 |
| 250 | `mysql`.`slave_worker_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626975 |
| 253 | `mysql`.`slave_relay_log_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626984 |
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----+-------------+
18 rows in set (0.01 sec)
– lru_positionは大きい方が最近ぽい。
– そういえばmysql.slave_*はInnoDBだっけ。
information_schema.INNODB_BUFFER_PAGE_LRU
• テーブル1つ(test.brand)スキャンしてみる。
mysql56> SELECT lru_position, table_name, index_name, number_records AS num, data_size, is_old, newest_modification AS LSN, access_time FROM
innodb_buffer_page_lru WHERE page_type = 'INDEX';
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----+-------------+
| lru_position | table_name | index_name | num | data_size | is_old | LSN | access_time |
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----+-------------+
| 208 | `SYS_IBUF_TABLE` | CLUST_IND | 0 | 0 | NO | 0 | 1153626747 |
| 216 | `mysql`.`innodb_table_stats` | PRIMARY | 11 | 635 | NO | 0 | 1153626720 |
| 217 | `SYS_TABLES` | ID_IND | 20 | 613 | NO | 0 | 1153626698 |
| 218 | `SYS_DATAFILES` | SYS_DATAFILES_SPACE | 16 | 766 | NO | 0 | 1153626748 |
| 219 | `SYS_TABLESPACES` | SYS_TABLESPACES_SPACE | 16 | 750 | NO | 0 | 1153626748 |
| 234 | `test`.`item` | PRIMARY | 17 | 938 | NO | 0 | 1153626777 |
| 236 | `mysql`.`slave_master_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626963 |
| 239 | `mysql`.`slave_worker_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626975 |
| 242 | `mysql`.`slave_relay_log_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626984 |
| 245 | `SYS_TABLES` | CLUST_IND | 20 | 1513 | NO | 0 | 1153626661 |
| 246 | `SYS_COLUMNS` | CLUST_IND | 103 | 6784 | NO | 0 | 1153626696 |
| 247 | `SYS_INDEXES` | CLUST_IND | 25 | 1715 | NO | 0 | 1153626695 |
| 248 | `SYS_FIELDS` | CLUST_IND | 30 | 1274 | NO | 0 | 1153626696 |
| 249 | `SYS_FOREIGN` | FOR_IND | 4 | 148 | NO | 0 | 1153626696 |
| 250 | `SYS_FOREIGN` | ID_IND | 4 | 280 | NO | 0 | 1153626768 |
| 251 | `SYS_FOREIGN_COLS` | ID_IND | 4 | 246 | NO | 0 | 1153626768 |
| 252 | `SYS_FOREIGN` | REF_IND | 4 | 152 | NO | 0 | 1153626696 |
| 253 | `mysql`.`innodb_index_stats` | PRIMARY | 46 | 4060 | NO | 0 | 1153626732 |
| 254 | `test`.`brand` | PRIMARY | 10 | 575 | NO | 0 | 1154017292 |
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----+-------------+
19 rows in set (0.01 sec)
– lru_position 254に載ったから、やっぱりこっちがyoung側の気がする。
– でもミッドポイント挿入戦略とかいうのがなかったっけ。
– SELECTしただけだからnewest_modificationはからっぽ、access_timeは記録される。
information_schema.INNODB_BUFFER_PAGE_LRU
• test.brandに1行INSERTしてみる。
mysql56> SELECT lru_position, table_name, index_name, number_records AS num, data_size, is_old, newest_modification AS LSN, access_time FROM
innodb_buffer_page_lru WHERE page_type = 'INDEX';
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----------+-------------+
| lru_position | table_name | index_name | num | data_size | is_old | LSN | access_time |
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----------+-------------+
| 202 | `SYS_IBUF_TABLE` | CLUST_IND | 0 | 0 | NO | 0 | 1153626747 |
| 210 | `mysql`.`innodb_table_stats` | PRIMARY | 11 | 635 | NO | 0 | 1153626720 |
| 211 | `SYS_TABLES` | ID_IND | 20 | 613 | NO | 0 | 1153626698 |
| 212 | `SYS_DATAFILES` | SYS_DATAFILES_SPACE | 16 | 766 | NO | 0 | 1153626748 |
| 213 | `SYS_TABLESPACES` | SYS_TABLESPACES_SPACE | 16 | 750 | NO | 0 | 1153626748 |
| 228 | `test`.`item` | PRIMARY | 17 | 938 | NO | 0 | 1153626777 |
| 230 | `mysql`.`slave_master_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626963 |
| 233 | `mysql`.`slave_worker_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626975 |
| 236 | `mysql`.`slave_relay_log_info` | PRIMARY | 0 | 0 | NO | 0 | 1153626984 |
| 239 | `SYS_TABLES` | CLUST_IND | 20 | 1513 | NO | 0 | 1153626661 |
| 240 | `SYS_COLUMNS` | CLUST_IND | 103 | 6784 | NO | 0 | 1153626696 |
| 241 | `SYS_INDEXES` | CLUST_IND | 25 | 1715 | NO | 0 | 1153626695 |
| 242 | `SYS_FIELDS` | CLUST_IND | 30 | 1274 | NO | 0 | 1153626696 |
| 243 | `SYS_FOREIGN` | FOR_IND | 4 | 148 | NO | 0 | 1153626696 |
| 244 | `SYS_FOREIGN` | ID_IND | 4 | 280 | NO | 0 | 1153626768 |
| 245 | `SYS_FOREIGN_COLS` | ID_IND | 4 | 246 | NO | 0 | 1153626768 |
| 246 | `SYS_FOREIGN` | REF_IND | 4 | 152 | NO | 0 | 1153626696 |
| 247 | `mysql`.`innodb_index_stats` | PRIMARY | 46 | 4060 | NO | 0 | 1153626732 |
| 248 | `test`.`brand` | PRIMARY | 11 | 614 | NO | 319272605 | 1154017292 |
| 252 | `test`.`factory` | PRIMARY | 4 | 254 | NO | 0 | 1154232813 |
| 254 | `test`.`brand` | maker | 11 | 360 | NO | 319272654 | 1154232814 |
+--------------+--------------------------------+-----------------------+-----+-----------+--------+-----------+-------------+
21 rows in set (0.01 sec)
– test.factoryが読み込まれたのは、FOREIGN KEYを切ってるからっぽい(FOREIGN KEYを消すと載らない)
– PRIMARY INDEXとmaker INDEXがそれぞれLSNが記録されてる。
ということは
• LSNが記録されていない(=0)のレコードはSELECTだけされてるやつだから使われているって根拠になりそう。
– LNS <> 0で検索してみようか。
• SELECTで特定のINDEXを使った場合はそのINDEX(とデータフェッチ用にPRIMARY KEY)だけが載るので、テーブル上の全部のインデックスがリストされている = INSERTで載ったと思えそう。– information_schema.statisticsとJOINして相関サブクエリでゴニョゴニョやろうとしたらものすごく重くなったので断念。。
– information_schemaってテンポラリテーブルみたいに毎回データをストアしてるぽいので、相関サブクエリだと確か
に悲惨になるよね。。
と思ったんですが
• 本番に5.5.28(以降)が2台しかなかった。
• しかもその2台(Master & Slave)はバッファプールがガラガラだった。
• とりあえず、newest_modification = 0のページは無かった。。– 一度LRUリストから落ちて次に読み込まれると、
newest_modificationは0に戻るのね。
• あと、InnoDB Compressed使ってるのですんごくinnodb_buffer_page_lruへのアクセスが重い。
• Master & Slaveで分散させてると、そのチェック結果をマージしないとダメそうだよね。。
• あと、access_timeがミリ秒単位のUNIXTIMEを32bit unsignedで受け取っているので、49.7日くらいでオーバーフローする。。
Percona Serverの
information_schema. INDEX_STATISTICSみたいに
ずぱっとどれくらい使ってるのか判るわけじゃない。
mysql> SET GLOBAL userstat=on;
..
mysql> SELECT * FROM index_statistics;
+--------------+------------+------------+-----------+
| TABLE_SCHEMA | TABLE_NAME | INDEX_NAME | ROWS_READ |
+--------------+------------+------------+-----------+
| tpcc | item | PRIMARY | 100000 |
+--------------+------------+------------+-----------+
1 row in set (0.00 sec)
いやこれもどのインデックス使ってないかっていう銀の弾丸じゃないけど。