GA

2014/07/15

TokuDBで変にパーティションが遅い件の比較用XtraDB

これもメモ調で。
昨日の TokuDBでパーティションが変に遅い件 との比較。

mysql> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `num` int(10) unsigned NOT NULL,
  `val` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.08 sec)

mysql> SHOW CREATE TABLE t2\G
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `num` int(10) unsigned NOT NULL,
  `val` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY HASH (num)
PARTITIONS 10 */
1 row in set (0.05 sec)

mysql> SHOW CREATE TABLE t3\G
*************************** 1. row ***************************
       Table: t3
Create Table: CREATE TABLE `t3` (
  `num` int(10) unsigned NOT NULL,
  `val` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY RANGE (num)
(PARTITION p0 VALUES LESS THAN (100000) ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN (200000) ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN (300000) ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN (400000) ENGINE = InnoDB,
 PARTITION p4 VALUES LESS THAN (500000) ENGINE = InnoDB,
 PARTITION p5 VALUES LESS THAN (600000) ENGINE = InnoDB,
 PARTITION p6 VALUES LESS THAN (700000) ENGINE = InnoDB,
 PARTITION p7 VALUES LESS THAN (800000) ENGINE = InnoDB,
 PARTITION p8 VALUES LESS THAN (900000) ENGINE = InnoDB,
 PARTITION p9 VALUES LESS THAN (1000000) ENGINE = InnoDB,
 PARTITION px VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
1 row in set (0.00 sec)

昨日のテーブルをデータ入ったままALTER TABLE .. Engine= InnoDBしただけ。

mysql> SELECT COUNT(*) FROM t1;
+----------+
| COUNT(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.46 sec)

mysql> SELECT COUNT(*) FROM t2;
+----------+
| COUNT(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.49 sec)

mysql> SELECT COUNT(*) FROM t3;
+----------+
| COUNT(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.49 sec)

同じクエリーを4回投げて一番遅いのを捨てて中央値を取るのはいっしょ。
プルーニングが効かない全スキャンなのでt1が一番速いとはいえ、やっぱりこんなもんだよね。

mysql> SELECT COUNT(*) FROM t1 WHERE num > 900000;
+----------+
| COUNT(*) |
+----------+
|   100000 |
+----------+
1 row in set (0.06 sec)

mysql> SELECT COUNT(*) FROM t2 WHERE num > 900000;
+----------+
| COUNT(*) |
+----------+
|   100000 |
+----------+
1 row in set (0.05 sec)

mysql> SELECT COUNT(*) FROM t3 WHERE num > 900000;
+----------+
| COUNT(*) |
+----------+
|   100000 |
+----------+
1 row in set (0.05 sec)

プルーニング効かないはずのt2のが速い。。

mysql> explain partitions SELECT COUNT(*) FROM t1 WHERE num > 900000;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows   | Extra                    |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL | 214438 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
1 row in set (0.03 sec)

mysql>
mysql> explain partitions SELECT COUNT(*) FROM t2 WHERE num > 900000;
+----+-------------+-------+-------------------------------+-------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | partitions                    | type  | possible_keys | key     | key_len | ref  | rows   | Extra                    |
+----+-------------+-------+-------------------------------+-------+---------------+---------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | t2    | p0,p1,p2,p3,p4,p5,p6,p7,p8,p9 | range | PRIMARY       | PRIMARY | 4       | NULL | 201950 | Using where; Using index |
+----+-------------+-------+-------------------------------+-------+---------------+---------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)

mysql> explain partitions SELECT COUNT(*) FROM t3 WHERE num > 900000;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows  | Extra                    |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+--------------------------+
|  1 | SIMPLE      | t3    | p9,px      | range | PRIMARY       | PRIMARY | 4       | NULL | 49983 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+--------------------------+
1 row in set (0.00 sec)

XtraDBはちゃんとプルーニング効きつつtype: rangeになった。

2014/07/14

TokuDBでパーティションが変に遅い件

とりあえずメモ。

mysql> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `num` int(10) unsigned NOT NULL,
  `val` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`num`)
) ENGINE=TokuDB DEFAULT CHARSET=utf8
1 row in set (0.01 sec)

mysql> SHOW CREATE TABLE t2\G
*************************** 1. row ***************************
       Table: t2
Create Table: CREATE TABLE `t2` (
  `num` int(10) unsigned NOT NULL,
  `val` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`num`)
) ENGINE=TokuDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY HASH (num)
PARTITIONS 10 */
1 row in set (0.00 sec)

mysql> SHOW CREATE TABLE t3\G
*************************** 1. row ***************************
       Table: t3
Create Table: CREATE TABLE `t3` (
  `num` int(10) unsigned NOT NULL,
  `val` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`num`)
) ENGINE=TokuDB DEFAULT CHARSET=utf8
/*!50100 PARTITION BY RANGE (num)
(PARTITION p0 VALUES LESS THAN (100000) ENGINE = TokuDB,
 PARTITION p1 VALUES LESS THAN (200000) ENGINE = TokuDB,
 PARTITION p2 VALUES LESS THAN (300000) ENGINE = TokuDB,
 PARTITION p3 VALUES LESS THAN (400000) ENGINE = TokuDB,
 PARTITION p4 VALUES LESS THAN (500000) ENGINE = TokuDB,
 PARTITION p5 VALUES LESS THAN (600000) ENGINE = TokuDB,
 PARTITION p6 VALUES LESS THAN (700000) ENGINE = TokuDB,
 PARTITION p7 VALUES LESS THAN (800000) ENGINE = TokuDB,
 PARTITION p8 VALUES LESS THAN (900000) ENGINE = TokuDB,
 PARTITION p9 VALUES LESS THAN (1000000) ENGINE = TokuDB,
 PARTITION px VALUES LESS THAN MAXVALUE ENGINE = TokuDB) */
1 row in set (0.00 sec)

mysql> LOAD DATA INFILE '/tmp/md5' INTO TABLE t1;
Query OK, 1000000 rows affected (12.70 sec)
Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0

mysql> LOAD DATA INFILE '/tmp/md5' INTO TABLE t2;
Query OK, 1000000 rows affected (13.12 sec)
Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0

mysql> LOAD DATA INFILE '/tmp/md5' INTO TABLE t3;
Query OK, 1000000 rows affected (13.00 sec)
Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0

パーティショニングなし, HASHパーティショニング, RANGEパーティショニングの3つに同じデータを詰める。

mysql> SELECT COUNT(*) FROM t1;
+----------+
| COUNT(*) |
+----------+
|  1000000 |
+----------+
1 row in set (0.54 sec)

mysql> SELECT COUNT(*) FROM t2;
+----------+
| COUNT(*) |
+----------+
|  1000000 |
+----------+
1 row in set (3.39 sec)

mysql> SELECT COUNT(*) FROM t3;
+----------+
| COUNT(*) |
+----------+
|  1000000 |
+----------+
1 row in set (3.51 sec)

同じクエリーを4回投げて、一番遅いものを捨てて(キャッシュがあったまってないやつ)、3回の中央値で比較。
WHERE句なし。パーティション2つが異様に重い。

mysql> SELECT COUNT(*) FROM t1 WHERE num > 900000;
+----------+
| COUNT(*) |
+----------+
|   100000 |
+----------+
1 row in set (0.06 sec)

mysql> SELECT COUNT(*) FROM t2 WHERE num > 900000;
+----------+
| COUNT(*) |
+----------+
|   100000 |
+----------+
1 row in set (0.05 sec)

mysql> SELECT COUNT(*) FROM t3 WHERE num > 900000;
+----------+
| COUNT(*) |
+----------+
|   100000 |
+----------+
1 row in set (0.36 sec)

…あれ、パーティションの刈り込みが効くはずのt3がものすごく遅い。。

mysql> explain partitions SELECT COUNT(*) FROM t1 WHERE num > 900000;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows   | Extra                    |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | PRIMARY       | PRIMARY | 4       | NULL | 114200 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
1 row in set (0.02 sec)

mysql> explain partitions SELECT COUNT(*) FROM t2 WHERE num > 900000;
+----+-------------+-------+-------------------------------+-------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | partitions                    | type  | possible_keys | key     | key_len | ref  | rows   | Extra                    |
+----+-------------+-------+-------------------------------+-------+---------------+---------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | t2    | p0,p1,p2,p3,p4,p5,p6,p7,p8,p9 | range | PRIMARY       | PRIMARY | 4       | NULL | 103200 | Using where; Using index |
+----+-------------+-------+-------------------------------+-------+---------------+---------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)

mysql> explain partitions SELECT COUNT(*) FROM t3 WHERE num > 900000;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows   | Extra                    |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
|  1 | SIMPLE      | t3    | p9,px      | index | PRIMARY       | PRIMARY | 4       | NULL | 100001 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+--------------------------+
1 row in set (0.00 sec)

ちゃんと刈り込めてはいるんだけどなーてか、t3のときだけインデックススキャンになってる。なんでだ。


【2014/07/15 11:55】
XtraDBでも調べた => TokuDBで変にパーティションが遅い件の比較用XtraDB


【2014/07/16 18:09】
バグらしい。中の人がMLにリプライくれた。=> https://groups.google.com/d/msg/tokudb-user/EjKSr_QuLYI/DSBH7PZ3v7AJ

MySQL Casual Talks vol.6でTokuDBについて話してきた

去る 7/11(金) ("セブンイレブンの日とおぼえてください" (c) RKajiyama) にMySQL Casual Talks vol.6がありました。 http://www.zusaar.com/event/11507003
とぅぎゃったまとめ http://togetter.com/li/691589


↓わたしの発表スライドです。





( ´-`).oO(これ、レイテンシーのグラフとかスループットのグラフとかかなり読み飛ばしながらだったので、発表の前にslideshare公開しておくべきだったな。。

今回のネタはTokuDBという、最近(?) PerconaやMariaDBにも組み込まれているストレージエンジンのおはなし。2ヶ月くらい調べてみた感想だけ言うと、「圧縮ウマー」「一応MVCC対応してるしレイテンシーもInnoDBに比べて(ケースにはよれど) *そこまで悪くない*」「たまに静寂に陥るのがマジ怖い」「ロック粒度本当に謎」という感じで、「アプリケーションが叩いてくる本番にはまだ入れたくないけどバッチ用サーバーとかに入れて経験値をためたい」が一番素直な感想でしょうか。

公式MLが過疎ってる(中の人が返信してくれたらいいのに。。)のはちょっと問題な気がするけれど、公式Twitterはちょくちょく話しかけてくれるので、なんかこうその辺りは探りつつなんとかなりそうな予感。



いずれにせよ、誰か(いや俺じゃなくてもいいんだ)が日本語で積極的に良いところ悪いところ発信していって、
 み ん な で 地 雷 除 去
していけばいいんじゃないですかね。

ところでいまさら気付いたんですが、○racleのオフィスでPerconaとMariaDBにだけ搭載されてるストレージエンジンの話とかしてしまったので、俺が連絡を絶ったら赤い会社に消されあれ誰か来た、ちょっと待ってt

2014/07/09

Percona XtraDB Clusterでいつの間にかPITRできるようになってた

以前の覚書 日々の覚書: Percona XtraDB Cluster設計の迷いどころ(現在進行形) で迷ってたんですが、気付いたらPITRフツーにできたのでメモ。

あの記事を書いた時点で検証していたのはwsrep 25.2.xだったんですが、wsrepも気付けば25.5.x (MySQLと同じで、25. x . y のうち25はAPIバージョンなので、wsrepのバージョンは2.x系から5.x系なのでメジャー2世代くらい?)、25.2.xと25.3.xの間で結構インプリメントされたような話は聞いてたんですが、そのせいなのかどうかはよく判らず。

( ´-`).oO(なんか25.2.xの時のcodershipのMLで、25.3.x(の初期)ではマルチマスターでPITRまだできないねー、みたいなのを読んだ気がしたんですがソース見つからず。

このディスカッション見ると、25.3.xと5.6の組み合わせで実装される予定(当時からすれば未来)だったのかなと思いますが。
http://www.linkedin.com/groups/How-do-pointintime-recovery-in-3973740.S.5802907499266850819


で、まあ、できました。
必要なのはmy.cnfにlog-slave-updates、これだけ。
これで、全てのインスタンスのバイナリーログに(ROWモードでだけど)記録されるようになる。
(前はこれができなかった。log-slave-updatesしても、実際にステートメントを実行したインスタンスのバイナリーログのみ)

mysql> use d1
Database changed

mysql> INSERT INTO t1 VALUES (1, 'one'), (2, 'two');
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> DELETE FROM t1 WHERE num= 1;
Query OK, 1 row affected (0.00 sec)

$ ll pxc*/bin*
-rw-rw---- 1 mysql mysql 501 Jul  9 08:28 pxc1/bin.000001
-rw-rw---- 1 mysql mysql  13 Jul  9 08:28 pxc1/bin.index
-rw-rw---- 1 mysql mysql 487 Jul  9 08:28 pxc2/bin.000001
-rw-rw---- 1 mysql mysql  13 Jul  9 08:28 pxc2/bin.index
-rw-rw---- 1 mysql mysql 487 Jul  9 08:28 pxc3/bin.000001
-rw-rw---- 1 mysql mysql  13 Jul  9 08:28 pxc3/bin.index

$ mysqlbinlog -vv pxc*/bin.000001 | grep "^#"
Warning: mysqlbinlog: unknown variable 'loose-default-character-set=utf8'
# at 4
#140709  8:28:26 server id 1  end_log_pos 120   Start: binlog v 4, server v 5.6.15-56-log created 140709  8:28:26 at startup
# Warning: this binlog is either in use or was not closed properly.
# at 120
#140709  8:28:42 server id 1  end_log_pos 191   Query   thread_id=4     exec_time=0     error_code=0
# at 191
#140709  8:28:42 server id 1  end_log_pos 233   Table_map: `d1`.`t1` mapped to number 70
# at 233
#140709  8:28:42 server id 1  end_log_pos 290   Write_rows: table id 70 flags: STMT_END_F
### INSERT INTO `d1`.`t1`
### SET
###   @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='one' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
### INSERT INTO `d1`.`t1`
### SET
###   @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='two' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 290
#140709  8:28:42 server id 1  end_log_pos 317   Xid = 12
# at 317
#140709  8:28:48 server id 1  end_log_pos 388   Query   thread_id=4     exec_time=0     error_code=0
# at 388
#140709  8:28:48 server id 1  end_log_pos 430   Table_map: `d1`.`t1` mapped to number 70
# at 430
#140709  8:28:48 server id 1  end_log_pos 474   Delete_rows: table id 70 flags: STMT_END_F
### DELETE FROM `d1`.`t1`
### WHERE
###   @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='one' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 474
#140709  8:28:48 server id 1  end_log_pos 501   Xid = 13
# at 4
#140709  8:28:19 server id 1  end_log_pos 120   Start: binlog v 4, server v 5.6.15-56-log created 140709  8:28:19 at startup
# Warning: this binlog is either in use or was not closed properly.
# at 120
#140709  8:28:42 server id 1  end_log_pos 184   Query   thread_id=4     exec_time=0     error_code=0
# at 184
#140709  8:28:42 server id 1  end_log_pos 226   Table_map: `d1`.`t1` mapped to number 70
# at 226
#140709  8:28:42 server id 1  end_log_pos 283   Write_rows: table id 70 flags: STMT_END_F
### INSERT INTO `d1`.`t1`
### SET
###   @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='one' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
### INSERT INTO `d1`.`t1`
### SET
###   @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='two' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 283
#140709  8:28:42 server id 1  end_log_pos 310   Xid = 12
# at 310
#140709  8:28:48 server id 1  end_log_pos 374   Query   thread_id=4     exec_time=0     error_code=0
# at 374
#140709  8:28:48 server id 1  end_log_pos 416   Table_map: `d1`.`t1` mapped to number 70
# at 416
#140709  8:28:48 server id 1  end_log_pos 460   Delete_rows: table id 70 flags: STMT_END_F
### DELETE FROM `d1`.`t1`
### WHERE
###   @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='one' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 460
#140709  8:28:48 server id 1  end_log_pos 487   Xid = 13
# at 4
#140709  8:28:22 server id 1  end_log_pos 120   Start: binlog v 4, server v 5.6.15-56-log created 140709  8:28:22 at startup
# Warning: this binlog is either in use or was not closed properly.
# at 120
#140709  8:28:42 server id 1  end_log_pos 184   Query   thread_id=4     exec_time=0     error_code=0
# at 184
#140709  8:28:42 server id 1  end_log_pos 226   Table_map: `d1`.`t1` mapped to number 70
# at 226
#140709  8:28:42 server id 1  end_log_pos 283   Write_rows: table id 70 flags: STMT_END_F
### INSERT INTO `d1`.`t1`
### SET
###   @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='one' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
### INSERT INTO `d1`.`t1`
### SET
###   @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='two' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 283
#140709  8:28:42 server id 1  end_log_pos 310   Xid = 12
# at 310
#140709  8:28:48 server id 1  end_log_pos 374   Query   thread_id=4     exec_time=0     error_code=0
# at 374
#140709  8:28:48 server id 1  end_log_pos 416   Table_map: `d1`.`t1` mapped to number 70
# at 416
#140709  8:28:48 server id 1  end_log_pos 460   Delete_rows: table id 70 flags: STMT_END_F
### DELETE FROM `d1`.`t1`
### WHERE
###   @1=1 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='one' /* VARSTRING(96) meta=96 nullable=1 is_null=0 */
# at 460
#140709  8:28:48 server id 1  end_log_pos 487   Xid = 13
# End of log file

というわけで、どれか1台以上のmysqldにmysqlbinlog -R --raw --stop-neverでつないでおけば、フツーのMySQLと同じようにPITRできそう。ビバ。

2014/07/03

MySQL 5.6のオンラインALTER TABLEとinnodb-sort-buffer-sizeに関する考察

つらつらと覚書。

サンプルテーブルは以下。

mysql> CREATE TABLE `t1` (
    ->   `id` int(10) unsigned NOT NULL,
    ->   `c1` varchar(32) NOT NULL,
    ->   `c2` varchar(32) NOT NULL,
    ->   PRIMARY KEY (`id`));
Query OK, 0 rows affected (0.32 sec)

mysql> INSERT INTO t1 VALUES (1, 'one', 'eins'), (2, 'two', 'zwei'), (3, 'three', 'drei');
Query OK, 3 rows affected (0.13 sec)
Records: 3  Duplicates: 0  Warnings: 0


まずは innodb-sort-buffer-size= 1M で単一カラムに。performance-schemaはOFFにしておかないとメモリー割り当ての上位がほとんどp_s関連で占められて表示が追い出されてしまう。

$ valgrind --tool=massif bin/mysqld --no-defaults --datadir=./data --performance-schema=0 --innodb-sort-buffer-size=1M

mysql> ALTER TABLE t1 ADD KEY (c1);
Query OK, 0 rows affected (0.66 sec)
Records: 0  Duplicates: 0  Warnings: 0

$ ms_print massif.out.30840 | grep alter

ALTER TABLEに関するメモリ割り当ては下位にあるっぽいので出てこない。


innodb-sort-buffer-size= 8M で単一カラム。

$ valgrind --tool=massif bin/mysqld --no-defaults --datadir=./data --performance-schema=0 --innodb-sort-buffer-size=8M

mysql> ALTER TABLE t1 ADD KEY (c1);
Query OK, 0 rows affected (0.72 sec)
Records: 0  Duplicates: 0  Warnings: 0

$ ms_print massif.out.30908 | grep alter
| |     ->49.78% (33,554,432B) 0x99EFC0: ha_innobase::inplace_alter_table(TABLE*, Alter_inplace_info*) (handler0alter.cc:3930)
| |       ->49.78% (33,554,432B) 0x7280AB: mysql_inplace_alter_table(THD*, TABLE_LIST*, TABLE*, TABLE*, Alter_inplace_info*, enum_alter_inplace_result, MDL_request*, Alter_table_ctx*) (handler.h:2868)
| |         ->49.78% (33,554,432B) 0x72B70B: mysql_alter_table(THD*, char*, char*, st_ha_create_information*, TABLE_LIST*, Alter_info*, unsigned int, st_order*, bool) (sql_table.cc:8319)
| |           ->49.78% (33,554,432B) 0x81EE4A: Sql_cmd_alter_table::execute(THD*) (sql_alter.cc:313)

出てきた。32MB。


innodb-sort-buffer-size= 64M

$ valgrind --tool=massif bin/mysqld --no-defaults --datadir=./data --performance-schema=0 --innodb-sort-buffer-size=64M

mysql> ALTER TABLE t1 ADD KEY (c1);
Query OK, 0 rows affected (1.40 sec)
Records: 0  Duplicates: 0  Warnings: 0

$ ms_print massif.out.30969 | grep alter
| |     ->88.81% (268,435,456B) 0x99EFC0: ha_innobase::inplace_alter_table(TABLE*, Alter_inplace_info*) (handler0alter.cc:3930)
| |       ->88.81% (268,435,456B) 0x7280AB: mysql_inplace_alter_table(THD*, TABLE_LIST*, TABLE*, TABLE*, Alter_inplace_info*, enum_alter_inplace_result, MDL_request*, Alter_table_ctx*) (handler.h:2868)
| |         ->88.81% (268,435,456B) 0x72B70B: mysql_alter_table(THD*, char*, char*, st_ha_create_information*, TABLE_LIST*, Alter_info*, unsigned int, st_order*, bool) (sql_table.cc:8319)
| |           ->88.81% (268,435,456B) 0x81EE4A: Sql_cmd_alter_table::execute(THD*) (sql_alter.cc:313)

256MB。innodb-sort-buffer-size= 8Mのときの8倍になっているのは良いとして、innodb-sort-buffer-sizeそのものの4倍確保してる?


innodb-sort-buffer-size= 8Mにして、2カラムの複合インデックス。

$ valgrind --tool=massif bin/mysqld --no-defaults --datadir=./data --performance-schema=0 --innodb-sort-buffer-size=8M

mysql> ALTER TABLE t1 ADD KEY (c1, c2);
Query OK, 0 rows affected (0.71 sec)
Records: 0  Duplicates: 0  Warnings: 0

$ ms_print massif.out.31031 | grep alter
| |     ->49.78% (33,554,432B) 0x99EFC0: ha_innobase::inplace_alter_table(TABLE*, Alter_inplace_info*) (handler0alter.cc:3930)
| |       ->49.78% (33,554,432B) 0x7280AB: mysql_inplace_alter_table(THD*, TABLE_LIST*, TABLE*, TABLE*, Alter_inplace_info*, enum_alter_inplace_result, MDL_request*, Alter_table_ctx*) (handler.h:2868)
| |         ->49.78% (33,554,432B) 0x72B70B: mysql_alter_table(THD*, char*, char*, st_ha_create_information*, TABLE_LIST*, Alter_info*, unsigned int, st_order*, bool) (sql_table.cc:8319)
| |           ->49.78% (33,554,432B) 0x81EE4A: Sql_cmd_alter_table::execute(THD*) (sql_alter.cc:313)

複合インデックスにしても変わらないぽい。


同じく、単一カラムのインデックスを一気に2つ作成。

$ valgrind --tool=massif bin/mysqld --no-defaults --datadir=./data --performance-schema=0 --innodb-sort-buffer-size=8M

mysql> ALTER TABLE t1 ADD KEY (c1), ADD KEY (c2);
Query OK, 0 rows affected (0.80 sec)
Records: 0  Duplicates: 0  Warnings: 0

$ ms_print massif.out.31095 | grep alter
| |     ->66.45% (67,108,864B) 0x99EFC0: ha_innobase::inplace_alter_table(TABLE*, Alter_inplace_info*) (handler0alter.cc:3930)
| |       ->66.45% (67,108,864B) 0x7280AB: mysql_inplace_alter_table(THD*, TABLE_LIST*, TABLE*, TABLE*, Alter_inplace_info*, enum_alter_inplace_result, MDL_request*, Alter_table_ctx*) (handler.h:2868)
| |         ->66.45% (67,108,864B) 0x72B70B: mysql_alter_table(THD*, char*, char*, st_ha_create_information*, TABLE_LIST*, Alter_info*, unsigned int, st_order*, bool) (sql_table.cc:8319)
| |           ->66.45% (67,108,864B) 0x81EE4A: Sql_cmd_alter_table::execute(THD*) (sql_alter.cc:313)

これはシンプルに倍になった。

マニュアルをよく読んでみたら、
(6 /*FTS_NUM_AUX_INDEX*/ *
(3*@@global.innodb_sort_buffer_size) + 2 * (
@@global.innodb_sort_buffer_size/dict_index_get_min_size(index)*/)
* 8 /*64-bit sizeof *buf->tuples*/")

http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_sort_buffer_size
http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_sort_buffer_size

まあなんとなく4倍くらいでおかしくなさそうなアレだった。納得。

2014/07/02

MariaDB 10.0のSEQUENCEストレージエンジンを試してみる

というか、MariaDB 10.1.0がAlphaリリースされたのでそれを試したついでなので、実際にはMariaDB 10.1のSEQUENCEストレージエンジンを使ってみました。


MariaDB [d1]> SELECT @@version;
+--------------------+
| @@version          |
+--------------------+
| 10.1.0-MariaDB-log |
+--------------------+
1 row in set (0.00 sec)

MariaDB [d1]> SHOW ENGINES;
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                                    | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
| MyISAM             | YES     | MyISAM storage engine                                                      | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                                         | NO           | NO   | NO         |
| MRG_MyISAM         | YES     | Collection of identical MyISAM tables                                      | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears)             | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables                  | NO           | NO   | NO         |
| Aria               | YES     | Crash-safe tables with MyISAM heritage                                     | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                                     | NO           | NO   | NO         |
| TEST_SQL_DISCOVERY | YES     | Minimal engine to test table discovery via sql statements                  | NO           | NO   | NO         |
| InnoDB             | DEFAULT | Percona-XtraDB, Supports transactions, row-level locking, and foreign keys | YES          | YES  | YES        |
| FEDERATED          | YES     | FederatedX pluggable storage engine                                        | YES          | NO   | YES        |
| CONNECT            | YES     | Management of External Data (SQL/MED), including many file formats         | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                                         | NO           | NO   | NO         |
| SPHINX             | YES     | Sphinx storage engine 2.1.5-release                                        | NO           | NO   | NO         |
| SEQUENCE           | YES     | Generated tables filled with sequential values                             | YES          | YES  | YES        |
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
14 rows in set (0.00 sec)

うぇーい(^ω^)


MariaDB [d1]> CREATE TABLE t1 (num int) Engine= SEQUENCE;
ERROR 1005 (HY000): Can't create table `d1`.`t1` (errno: 131 "Command not supported by database")

あれ、なんか違うらしい。
確かに前(10.0に入るって時に)なんかちょっと見た気がするけど、Oracleのシーケンステーブル(?)とかとは根本的に違う何からしいんだよね確か。

おとなしくマニュアル読む。 https://mariadb.com/kb/en/mariadb/mariadb-documentation/mariadb-storage-engines/sequence/


使い方はこうらしい。


MariaDB [d1]> SELECT * FROM seq_1_to_12;
+-----+
| seq |
+-----+
|   1 |
|   2 |
|   3 |
|   4 |
|   5 |
|   6 |
|   7 |
|   8 |
|   9 |
|  10 |
|  11 |
|  12 |
+-----+
12 rows in set (0.00 sec)

MariaDB [d1]> SELECT seq FROM seq_10_to_20_step_2;
+-----+
| seq |
+-----+
|  10 |
|  12 |
|  14 |
|  16 |
|  18 |
|  20 |
+-----+
6 rows in set (0.00 sec)

お、おう。連番が欲しい時に便利そうだねってか、偶数だけ欲しい時にWHERE MOD(c1, 2) = 0とかしなくてもJOINすれば良いように…とかそんなかんじ?(思い付かない


MariaDB [d1]> SELECT seq FROM seq_1_to_99999999999999999999999999999999 ORDER BY seq DESC LIMIT 3;
+----------------------+
| seq                  |
+----------------------+
| 18446744073709551615 |
| 18446744073709551614 |
| 18446744073709551613 |
+----------------------+
3 rows in set (0.00 sec)

2 ^ 64 - 1までいけるっぽい。負値はダメ。


MariaDB [d1]> SELECT * FROM seq_10_to_1;
+-----+
| seq |
+-----+
|  10 |
|   9 |
|   8 |
|   7 |
|   6 |
|   5 |
|   4 |
|   3 |
|   2 |
|   1 |
+-----+
10 rows in set (0.00 sec)

MariaDB [d1]> SELECT * FROM seq_10_to_1_step_2;
+-----+
| seq |
+-----+
|   9 |
|   7 |
|   5 |
|   3 |
|   1 |
+-----+
5 rows in set (0.00 sec)

頭の方が数字が大きければ降順っぽく。
ただし、seq_1_to_10_step_2 は 「先の値」の1から2ずつインクリメントするのに対して、seq_10_to_1_step_2は「先の値」の10からデクリメントしてるわけではないので、たぶん単にseq_1_to_10_step_2をひっくり返してるだけ。


MariaDB [d1]> explain SELECT * FROM seq_1_to_10_step_2;
+------+-------------+--------------------+-------+---------------+---------+---------+------+------+-------------+
| id   | select_type | table              | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+------+-------------+--------------------+-------+---------------+---------+---------+------+------+-------------+
|    1 | SIMPLE      | seq_1_to_10_step_2 | index | NULL          | PRIMARY | 8       | NULL |    5 | Using index |
+------+-------------+--------------------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.00 sec)

MariaDB [d1]> explain SELECT * FROM seq_10_to_1_step_2;
+------+-------------+--------------------+------+---------------+------+---------+------+------+-------+
| id   | select_type | table              | type | possible_keys | key  | key_len | ref  | rows | Extra |
+------+-------------+--------------------+------+---------------+------+---------+------+------+-------+
|    1 | SIMPLE      | seq_10_to_1_step_2 | ALL  | NULL          | NULL | NULL    | NULL |    5 |       |
+------+-------------+--------------------+------+---------------+------+---------+------+------+-------+
1 row in set (0.00 sec)


ちゃんとEXPLAIN取れるところは芸が細かいというかなんと言うか。


MariaDB [d1]> SELECT seq, CONCAT(IF(fizz.seq IS NULL, '', 'fizz'), IF(buzz.seq IS NULL, '', 'buzz')) AS str FROM seq_1_to_100 AS main LEFT JOIN seq_0_to_100_step_3 AS fizz USING(seq) LEFT JOIN seq_0_to_100_step_5 AS buzz USING(seq);
+-----+----------+
| seq | str      |
+-----+----------+
|   1 |          |
|   2 |          |
|   3 | fizz     |
|   4 |          |
|   5 | buzz     |
|   6 | fizz     |
|   7 |          |
|   8 |          |
|   9 | fizz     |
|  10 | buzz     |
|  11 |          |
|  12 | fizz     |
|  13 |          |
|  14 |          |
|  15 | fizzbuzz |
..
| 100 | buzz     |
+-----+----------+
100 rows in set (0.00 sec)

FIZZBUZZしてみたけど、IF演算子使うならなんでもいいやね。。

2014/06/03

'Client requested master to start replication from impossible position'の原因

たとえばこんなエラーログ。

140603 10:05:58 [Note] Slave SQL thread initialized, starting replication in log 'mysql-bin.000032' at position 61352894, relay log './mysql-relay.000787' position: 21485832
140603 10:05:58 [Note] Slave I/O thread: connected to master 'replicator@xxx.xxx.xxx.xxx:3306',  replication started in log 'mysql-bin.000032' at position 61352894
140603 10:05:58 [ERROR] Error reading packet from server: Client requested master to start replication from impossible position ( server_errno=1236)
140603 10:05:58 [ERROR] Got fatal error 1236: 'Client requested master to start replication from impossible position' from master when reading data from binary log
140603 10:05:58 [Note] Slave I/O thread exiting, read up to log 'mysql-bin.000032', position 61352894

START SLAVEしたタイミングでこんなんが出ることがあります。
そしてI/Oスレッドのみが止まる。SHOW SLAVE STATUSで見るとSlave_IO_Running: No, Slave_SQL_Running: Yes な状態です。

ゆえあってMySQL 5.0なので(というか2014年にもなってTritonnです)、SHOW SLAVE STATUSには何も表示されてないですが、5.1 以降なら↑のログの内容がLast_IO_Errorに表示されるんじゃないかと。

上から読んでいくと、

  1. SQLスレッドが初期化された。マスターのログファイルはmysql-bin.000032でログポジションは61352894、リレーログファイルはmysql-relay.000787でログポジションが21485832。
  2. I/Oスレッドがマスターに接続して、mysql-bin.000032のポジション61352894のログを要求した。
  3. サーバーからerrno= 1236 を受け取った。メッセージは「クライアントのリクエストしたレプリケーションの開始位置がアリエナイ!」
  4. 大事なので(かどうかは知らんけど)2回言った
  5. I/Oスレッドが動作を停止した。最後に読んだマスターのログファイルは..(略)

errno= 1236は「バイナリーログをもらおうとしたら致命的な(=リトライしても成功する見込みのない)エラーが発生した」というI/Oスレッドの(わりと)汎用エラーなので、大事なのはエラーメッセージの方。

$ perror 1236
MySQL error code 1236 (ER_MASTER_FATAL_ERROR_READING_BINLOG): Got fatal error %d from master when reading data from binary log: '%-.320s'

そしてマスター上のバイナリーログファイルはこんなんになっており、

$ ll mysql-bin.*
-rw-rw---- 1 mysql mysql 268436192 May 31 21:26 mysql-bin.000031
-rw-rw---- 1 mysql mysql  61341696 Jun  3 05:35 mysql-bin.000032
-rw-rw---- 1 mysql mysql        98 Jun  3 05:53 mysql-bin.000033
-rw-rw---- 1 mysql mysql       117 Jun  3 06:15 mysql-bin.000034
-rw-rw---- 1 mysql mysql   4630804 Jun  3 11:35 mysql-bin.000035
-rw-rw---- 1 mysql mysql        95 Jun  3 06:29 mysql-bin.index

確かにmysql-bin.000032に61352894バイト目は存在しない(master_log_posはバイナリーログファイルの先頭からのバイトオフセット)
だけど、スレーブは61352894バイト目までは終わってるから次をよこせと言っている状況。

マスターがsync-binlog= 0 でOSごとクラッシュした時にこんな状況が起こりがちで、

  1. マスターがバイナリーログをwrite
  2. Binlog dump threadがwriteされたバイナリーログを読み出してスレーブに送信
  3. マスターのバイナリーログがフラッシュされる前にOSクラッシュ
とまあ、書き込みワークロードをかけながらOSをクラッシュさせるなり電源を落とすなりで比較的簡単に再現できる。

CHANGE MASTER TO master_log_file= 'mysql-bin.000033', master_log_pos= 1; で仮復旧はできるものの、その後マスター/スレーブのデータの整合性確認しないといけないのが面倒とか、バイナリーログの保全ができてない(log-slave-updatesしているならそっちから抽出すれば大丈夫だろうけど)ので、この時間近辺のPITRは危ないとか色々あるので、5.6のmysqlbinlog使ったリアルタイムバックアップももう一度考えようかなぁ。。