Quantcast
Channel: 日々の覚書
Viewing all 581 articles
Browse latest View live

MySQLサーバーのディスク容量減少アラートが飛んでくる前に

$
0
0
このビッグウェーブにいつ乗るの! 今でしょ!


元RX-7乗りの適当な日々
Linuxサーバがディスク容量不足になった!何か消さねば!ってなった時にどう対処するか

tagomorisのメモ置き場
Linuxサーバのディスク容量減少アラートが飛んできた!ってときにどう対処するか

iをgに変えるとorangeになることに気づいたoranieの日記
Cassandraサーバのディスク容量減少アラートが飛んできた!ってときにどう対処するか


…と思ってたらMySQLバージョンも既に出た。けどめげない。かぶらないように書きます。というかタイトルはかぶらないように変えました(つд`)

Y-Ken Studio
MySQLサーバのディスク容量減少アラートが飛んできた!ってときにどう対処するか


さてまず、MySQLで勝手に(?)増えるものといえば。

  1. データファイル(.MYD, .MYI, .ibd, ibdata1)
  2. バイナリーログファイル, リレーログファイル
  3. スローログファイル
  4. エラーログファイル
  5. テンポラリーファイル, テンポラリーテーブル
くらいかなあ。少なくとも俺が過去にDISKをあふれさせたことがあるのはこいつらくらいです。


1. データファイル

ウチの場合、取り敢えず一番増えるのは ほげほげ_history とか なんちゃら_logs とかそんな名前のテーブルです。最低限、1行あたりのサイズを押さえておく必要があります。

mysql> SHOW TABLE STATU LIKE 'hogehoge_logs'\G
*************************** 1. row ***************************
           Name: hogehoge_logs
         Engine: InnoDB
        Version: 10
     Row_format: Compressed
           Rows: 18
 Avg_row_length: 455
    Data_length: 8192
Max_data_length: 0
   Index_length: 32768
      Data_free: 0
 Auto_increment: 19
    Create_time: 2013-07-22 23:55:13
    Update_time: NULL
     Check_time: NULL
      Collation: ujis_japanese_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

Avg_row_lengthはデータ部分だけのサイズなので、(Data_length + Index_length) / Rowsでインデックス込みの1行あたりの値をとっときます。意外と大きいケースが多かったりするので。するので。

↑のテーブルだと(ちょっとデータ量が少なすぎて全くアテにならないとはいえ)1行当たり2.2KiBくらい、Create_timeから察するに1日の増加量は2.5行、万一(ひどい)サービスがバズってトラフィックが10倍になったとしても1日50KiBくらいですね。


というのを、information_schemaから全テーブル分ばっこ抜きます。

mysql> SELECT
    ->  TABLE_SCHEMA,
    ->  TABLE_NAME,
    ->  ENGINE,
    ->  SUM(((DATA_LENGTH + INDEX_LENGTH) / TABLE_ROWS) / TIMESTAMPDIFF(DAY,  CREATE_TIME, NOW())) AS 1d_size
    -> FROM
    ->  information_schema.tables
    -> WHERE
    ->         ENGINE IN ('InnoDB', 'MyISAM')
    ->  AND TABLE_SCHEMA NOT IN ('information_schema', 'mysql')
    -> GROUP BY
    ->  TABLE_SCHEMA, TABLE_NAME
    -> WITH ROLLUP;
+--------------+-------------------------+--------+----------------+
| TABLE_SCHEMA | TABLE_NAME              | ENGINE | 1d_size        |
+--------------+-------------------------+--------+----------------+
| xxxx  | hogehoge_logs        | InnoDB |   537.18032787 |
..
| xxxx  | NULL                    | InnoDB | 61917.19581370 |
| NULL         | NULL                    | InnoDB | 61917.19581370 |
+--------------+-------------------------+--------+----------------+

SUMもへったくれもない1d_sizeカラムにSUM()をつけているのは、WITH ROLLUPで最後に合計値を出すためです。
便利ですよWITH ROLLUP。滅多に使わないけど。
http://dev.mysql.com/doc/refman/5.6/en/group-by-modifiers.html

というわけで、このMySQL(検証用DBだけど)は1日あたり60KiBちょっと、バズって100倍来ても大丈夫ですね(にっこり)

information_schemaだし、飽くまで参考値にしかなりませんが、定常状態を知っておくのは大事だと思います。


2. バイナリーログファイル, リレーログファイル

データファイルより更に手抜きで引っこ抜きます。
大概の場合、--expire-logs-daysで過去何日分かに絞っていると思うので、それで割り算しちゃいます。

$ ls -l mysql-bin.* | awk '{i+=$5}END{print i}'
356928028

7日間で350MiBくらいなので50MiB/dayくらいですね。100倍バズるとちょっと大変かも。
リレーログも余裕を見てこれと同じだけ見込むことにしてます。
というわけで--log-slave-updatesつきの中間スレーブで100MiB/day。

もしイベントなんかの期間要因がバイナリーログの保存期間の中に含まれていて、そこは除外したいというのがあれば、mysqlbinlogでテキトーに引っこ抜きます。SQLデコードされる分容量が0%(増えない)~100%(2倍)くらいになっちゃうんですが、多少安全側に倒していると思って諦めます。

$ mysqlbinlog mysql-bin.* --start-datetime="2013-07-21 00:00:00" --stop-datetime="2013-07-22 00:00:00" | wc
  12213   59409  543939

540KiB/day。バラつきがあったんだなぁ。。どっちを採用するか却って悩むかも知れません。

【2013/07/30 13:24】
ちなみに、--binlog_format=mixedだと、LOAD DATA INFILEがROWモードで1行ずつロギングされるので--expire-logs-daysに関係なく瞬間的に結構な量になります。↑のばらつきはそうだったっぽいです。


3. スローログファイル

まあ、増える時は増えますよね! たまに気にしてあげてください。
あと、クエリーをまるっと書き出す性質上、DWH的な使い方をしててスローログ出てもいいからサブクエリーやJOINし放題、WHERE .. IN (..)に3000くらい定数が列挙してあるぜはははーなやつがあったりするとあっという間に太ります。

つーか、出ても気にしないんだったら切っとけ。。


4. エラーログファイル

意外と肥大化の原因になってるのが[Warning]で、コイツ、当然ながらエラーは返さないなれどエラーログには記録されるんですよね(--log-warnings=n でレベル指定。暗黙のデフォルトは1)

で、アプリから投げたSQLのWarningって意外と気にしてくれない人が多いので気が付くとぼこぼこぼこぼこぼこぼこぼこぼこ。。

しかも俺が最近よく見るWarningレベルのログって、ご丁寧にワーニングになったクエリーを書き出してくれる親切設計なので、 1 ク エ リ ー 8 K i B も あ る いじめのようなクエリーがごりごり書き出されてあっという間にエラーログが太ったことがありました。

このクエリーはスローログファイルもいじめました。もうやめて! MySQLのライフはゼロよ!


5. テンポラリーファイル, テンポラリーテーブル

これもうちょっと細分化すればよかったかも知れない。
Using filesortの時に使うテンポラリーファイル, Using temporaryの時に使う(かつ、Diskに固定化された)テンポラリーテーブル, 5.6のInnoDB Online Alter Table(--innodb_online_alter_log_max_size=nで制御、暗黙のデフォルトは128M)で使う更新ログっぽいやつは軒並みtmpdirに吐く。

特に前2つはtmpdirが/tmpのままだったりすると意外とあっさり食いきられてError:28(=No space left on device)をエラーログに吐いてSort Abortしたり。
クエリーがAbortされたあとは(一応)自動で消えはするものの、そのクエリーはエラーで返ることになる。で、大概テンポラリーテーブルのサイズがそもそもtmpdirにおさまってないのが問題なので、同じクエリーを投げればまあ再現する羽目に。。

あとはいわゆるフツーのALTER TABLEの時はdatadir(ALTER対象のテーブルのまどなり)にテンポラリーテーブルを作る。インデックスやカラムを追加する時は今のサイズにその新しい容量が乗っかるので、ALTER対象テーブルサイズよりちょっと大きめの量が余計に必要になるので注意。これがあふれてALTERがAbortされると、「俺の3時間を返せええええええ」ってなったりならなかったり。。


なんか終盤ぐだぐだになってますが、体感的にはMySQLのデータを破損させる一番の原因はDisk Fullなので、「そもそもアラートが㌧でこないように! ギリギリの運用イクナイ!」ということを主張するものであります。

おらにえさん、こんな感じでいいですかー?w

MyNA会2013年7月に行ってきました

$
0
0
行ってきました。
昨日は無事でした。

http://atnd.org/events/40892

無事というのはアレです。
今年に入ってからジンクス的なものがあってですね、

  • MySQL Tech Tour Tokyoでオラクル青山へ行く
    • MySQLがサーバーごと壊れてアラートで帰る
  • MyNA会2013年3月でオラクル青山へ行く
    • 朝一でデータがぶっ壊れて午前中ずっとリカバリー
    • 38度近く熱が出てオーバーヒートしながら何かしゃべった
  • MySQL勉強会 in 大阪(第4回)
    • Ustで見てただけなのに発熱Again
オラクル青山に行くたびに何かアラートが、勉強会のたびに発熱が襲ってくるわけですが、今年も半分以上終わったところで初めて平穏でした。

代わりに今日、バックアップサーバーが壊れてリブートを繰り返し、俺のお腹も急降下ですけどな!orz

やっぱ呪われてるんだと思います。


で、やってきたネタLTなんですが、



チューニンガソン5の復讐 from yoku0825


ネタLTのくせに間が悪くてスベりまくり、その後のごはんで坂井さんにしんみり諭されました。精進します。。


中身の話はsmallpalaceさんがまとめてくれてました。すてき。
smallpalace's blog - myna会20130729にいってきた記録

performance_schemaすごく面白かったです。英語で話しかけられない自分のへたれっぷりにへこみました。奥野さんがナチュラルに「質問、日本語でも大丈夫ですよ」とか誘導しててカコヨカッタです。というかかじやまさんが始まってすぐどこかに飛び立って行ったのにすごくやられた感が。。リアルタイム通訳を期待していたのは俺だけではあるまい。。
きっといとーさんのベンチマークが火を噴いて(全てのenabled='No'だったら--performance-schema=0とそんなに変わらない? とか)、スパスパMySQLの席で発表されるんじゃないかと思います。煙草吸いながら聞きたいなー(棒

MySQLプロトコルは超俺得で俺は何も作りませんが色々遊ぼうと思います。OSC 2013 Tokyo/Fallで再演されるという話が出ていましたが定かではありません。

あと、SH2さんにアイスをおごることができたので、俺の夏はもう終わりかなと思います。


後は年を食うだけかな。。

Amazon RDSのリードレプリカでClient requested master to start replication from impossible positionに出会う

$
0
0
マスターがMulti-AZ構成で、フェイルオーバーした時に、リードレプリカのI/Oスレッドが止まっちゃいました。

mysql56> SHOW SLAVE STATUS\G
..
                Last_IO_Errno: 1236
                Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Client requested master to start replication from impossible position; the first event 'bin.000001' at 64056, the last event read from './bin.000001' at 4, the last byte read from './bin.000001' at 4.'
..

ログ出力はこんな感じ。別のサーバーで再現させただけなので数字はテキトーです。


Error:1236自体は、「I/Oスレッドがマスターのbinlogを読み込もうとしたら、致命的な(リトライしても決して成功しない)エラーになった」という意味で、後続のメッセージが重要になったりします。

この場合は、"Client requested master to start replication from impossible position;"がそれですね。スレーブのI/OスレッドはMASTER_LOG_FILE= 'bin.000001', MASTER_LOG_POS= 64056をマスターに要求したけれど、マスターのbin.000001ファイル上にはPosition 64056のイベントは存在しない、というエラーです。

たぶん、マスターが元のAZにいたときに、スレーブがぴったり遅延なくbinlogを受信していた && sync_binlog= 0だったので元のマスターがwrite()したbinlogがfsync()される前に切り替わってしまったのが原因でせう。

MySQL on EC2などであればCHANGE MASTER TOで直したりもできるんですが(マスターに存在しないトランザクションがスレーブに受信されちゃってる状態なので、ある程度は調整しないとではあるんですが)そこはRDS、それは無理。

https://forums.aws.amazon.com/thread.jspa?messageID=452065&#452065

"you should create a new read replica to replace the broken one."だそうで。
まあ、マスターに無いbinlogがスレーブに行っちゃっている時点でデータの整合性も保証できなくなってるわけで、おとなしく作り直すのがいいんでしょうね。


ちなみにですが、マスター上に存在しないバイナリーログファイルを指定するとこんな感じに。

mysql56> SHOW SLAVE STATUS\G
..
                Last_IO_Errno: 1236
                Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Could not find first log file name in binary log index file'
..

そんなバイナリーログファイル持ってないぜって感じですね。


mysql.rds_skip_repl_errorで直るかなぁって相談受けたんだけど、無理です。
CALLステートメントでよく呼ぶストアドルーチンの中身の見方は↓こんな感じ。

http://yoku0825.blogspot.jp/2013/08/amazon-rds.html

Amazon RDSの各種プロシージャの中身の見方

$
0
0
http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.MySQL.CommonDBATasks.html

↑に記載されてるようなやつ。

mysql> SELECT * FROM information_schema.routines\G
..

でルーチンボディやその他属性関連色々見られる。


mysql.rds_skip_repl_errorとか見ていて判りやすいですね。

mysql> SELECT routine_body FROM information_schema.routines WHERE specific_name= 'rds_skip_repl_error'\G
..

ルーチンボディはライセンスがよく判らないので貼らないでおこうそうしよう。。

mroongaのインデックスサイズをなんとなく

$
0
0
ウチではかつてはTritonn全盛、今はmroonga全盛な「MySQL de FullTextSearch」が大好きな感じでやっています。Sphinxとか誰か手を出さないかなぁと思いつつ、いい加減MySQL de FTSから足を洗って全文検索ライブラリはMySQLとは連携させないでやろうぜ、というのもあったりします。

そこで取り敢えず現状の洗い出し(?)の一環として、mroongaのストレージサイズをぼんやりと考えてみることにします。
というか運用している分にはこれがキツい。
インデックス用のmrnファイルは(tokenBigramなせいもあって)データサイズに対して線形に増えるので、ストレージ容量と、あとそれをキャッシュするだけのメモリ増設がもうままならない。


実際どれくらい食うのかを取り敢えず測ってみる。

サンプルデータはTwitterのツイート履歴。
ただし全文検索が試したいだけだったり、カラムをもろもろ列挙したりするのがものすごく面倒だったのでテーブル構成はすごく手抜きな感じで。

mysql55> CREATE TABLE myisam.tweet ( tweet_id BIGINT UNSIGNED PRIMARY KEY, timestamp TIMESTAMP NOT NULL DEFAULT 0, text TEXT) Engine = MyISAM;
Query OK, 0 rows affected (0.03 sec)

mysql55> CREATE TABLE mroonga_storage.tweet ( tweet_id BIGINT UNSIGNED PRIMARY KEY, timestamp TIMESTAMP NOT NULL DEFAULT 0, text TEXT) Engine = mroonga;
Query OK, 0 rows affected (0.07 sec)

mysql55> CREATE TABLE mroonga_wrapper.tweet ( tweet_id BIGINT UNSIGNED PRIMARY KEY, timestamp TIMESTAMP NOT NULL DEFAULT 0, text TEXT) Engine = mroonga COMMENT = 'Engine "MyISAM"';
Query OK, 0 rows affected (0.09 sec)

MyISAM、ストレージモード、ラッパーモードでそれぞれ作る。
mroongaはデータベース単位でmrnファイルを作ってしまうのでデータベースも分けておく。

mysql55> SHOW TABLE STATUS FROM myisam\G
*************************** 1. row ***************************
Name: tweet
Engine: MyISAM
Version: 10
Row_format: Dynamic
Rows: 0
Avg_row_length: 0
Data_length: 0
Max_data_length: 281474976710655
Index_length: 1024
Data_free: 0
Auto_increment: NULL
Create_time: 2013-08-19 19:39:02
Update_time: 2013-08-19 19:39:02
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.01 sec)

mysql55> SHOW TABLE STATUS FROM mroonga_storage\G
*************************** 1. row ***************************
Name: tweet
Engine: mroonga
Version: 10
Row_format: Dynamic
Rows: 0
Avg_row_length: 0
Data_length: 4526080
Max_data_length: 0
Index_length: 0
Data_free: 0
Auto_increment: NULL
Create_time: NULL
Update_time: NULL
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.01 sec)

mysql55> SHOW TABLE STATUS FROM mroonga_wrapper\G
*************************** 1. row ***************************
Name: tweet
Engine: mroonga
Version: 10
Row_format: Dynamic
Rows: 0
Avg_row_length: 0
Data_length: 0
Max_data_length: 0
Index_length: 1024
Data_free: 0
Auto_increment: NULL
Create_time: NULL
Update_time: 2013-08-19 19:39:09
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment: Engine "MyISAM"
1 row in set (0.01 sec)

$ ls -l myisam/* | tee >(awk '{i+=$5}END{print i}')
-rw-rw---- 1 mysql mysql 61 8月 19 19:38 2013 myisam/db.opt
-rw-rw---- 1 mysql mysql 0 8月 19 19:39 2013 myisam/tweet.MYD
-rw-rw---- 1 mysql mysql 1024 8月 19 19:39 2013 myisam/tweet.MYI
-rw-rw---- 1 mysql mysql 8638 8月 19 19:39 2013 myisam/tweet.frm
9723

$ ls -l mroonga_storage* | tee >(awk '{i+=$5}END{print i}')
-rw-rw---- 1 mysql mysql 4096 8月 19 19:39 2013 mroonga_storage.mrn
-rw-rw---- 1 mysql mysql 12857344 8月 19 19:39 2013 mroonga_storage.mrn.0000000
-rw-rw---- 1 mysql mysql 4243456 8月 19 19:39 2013 mroonga_storage.mrn.0000103
-rw-rw---- 1 mysql mysql 4096 8月 19 19:39 2013 mroonga_storage.mrn.0000104
-rw-rw---- 1 mysql mysql 4096 8月 19 19:39 2013 mroonga_storage.mrn.0000105
-rw-rw---- 1 mysql mysql 274432 8月 19 19:39 2013 mroonga_storage.mrn.0000106
-rw-rw---- 1 mysql mysql 1048576 8月 19 19:39 2013 mroonga_storage.mrn.001

mroonga_storage:
合計 16
-rw-rw---- 1 mysql mysql 61 8月 19 19:38 2013 db.opt
-rw-rw---- 1 mysql mysql 8638 8月 19 19:39 2013 tweet.frm
18444795

$ ls -l mroonga_wrapper* | tee >(awk '{i+=$5}END{print i}')
-rw-rw---- 1 mysql mysql 4096 8月 19 19:39 2013 mroonga_wrapper.mrn
-rw-rw---- 1 mysql mysql 12857344 8月 19 19:39 2013 mroonga_wrapper.mrn.0000000
-rw-rw---- 1 mysql mysql 65536 8月 19 19:39 2013 mroonga_wrapper.mrn.0000103
-rw-rw---- 1 mysql mysql 1048576 8月 19 19:39 2013 mroonga_wrapper.mrn.001

mroonga_wrapper:
合計 20
-rw-rw---- 1 mysql mysql 61 8月 19 19:38 2013 db.opt
-rw-rw---- 1 mysql mysql 0 8月 19 19:39 2013 tweet.MYD
-rw-rw---- 1 mysql mysql 1024 8月 19 19:39 2013 tweet.MYI
-rw-rw---- 1 mysql mysql 8638 8月 19 19:39 2013 tweet.frm
13985275
mroonga_storage, mroonga_wrapperの差とmroonga_storageのSHOW TABLE STATUSの結果から察するに、.mrn.0000103が.MYDファイルみたいな扱いになるんだろうか。

LOAD DATA INFILEで1万ツイートほど流し込んでみる。

mysql55> LOAD DATA INFILE '/tmp/tweets.csv' INTO TABLE tweet FIELDS TERMINATED BY ',' ENCLOSED BY  '"' IGNORE 1 LINES (tweet_id, @dummy, @dummy, @dummy, @dummy, timestamp, @dummy, text, @dummy);
Query OK, 10045 rows affected, 10089 warnings (0.25 sec)
Records: 10045 Deleted: 0 Skipped: 0 Warnings: 10089
timestampカラムのデータを切り詰めたとかフィールドの数が合わないってワーニングが出てたけど大勢に影響がないので気にしない。

mysql55> SHOW TABLE STATUS FROM myisam\G
*************************** 1. row ***************************
Name: tweet
Engine: MyISAM
Version: 10
Row_format: Dynamic
Rows: 10045
Avg_row_length: 125
Data_length: 1263404
Max_data_length: 281474976710655
Index_length: 217088
Data_free: 0
Auto_increment: NULL
Create_time: 2013-08-19 19:39:02
Update_time: 2013-08-19 19:41:06
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)

mysql55> SHOW TABLE STATUS FROM mroonga_storage\G
*************************** 1. row ***************************
Name: tweet
Engine: mroonga
Version: 10
Row_format: Dynamic
Rows: 10045
Avg_row_length: 0
Data_length: 46469120
Max_data_length: 0
Index_length: 0
Data_free: 0
Auto_increment: NULL
Create_time: NULL
Update_time: NULL
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)

mysql55> SHOW TABLE STATUS FROM mroonga_wrapper\G
*************************** 1. row ***************************
Name: tweet
Engine: mroonga
Version: 10
Row_format: Dynamic
Rows: 10045
Avg_row_length: 125
Data_length: 1263404
Max_data_length: 0
Index_length: 217088
Data_free: 0
Auto_increment: NULL
Create_time: NULL
Update_time: 2013-08-19 19:41:19
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment: Engine "MyISAM"
1 row in set (0.00 sec)

$ ls -l myisam/* | tee >(awk '{i+=$5}END{print i}')
-rw-rw---- 1 mysql mysql 61 8月 19 19:38 2013 myisam/db.opt
-rw-rw---- 1 mysql mysql 1263404 8月 19 19:41 2013 myisam/tweet.MYD
-rw-rw---- 1 mysql mysql 217088 8月 19 19:41 2013 myisam/tweet.MYI
-rw-rw---- 1 mysql mysql 8638 8月 19 19:39 2013 myisam/tweet.frm
1489191

$ ls -l mroonga_storage* | tee >(awk '{i+=$5}END{print i}')
-rw-rw---- 1 mysql mysql 4096 8月 19 19:41 2013 mroonga_storage.mrn
-rw-rw---- 1 mysql mysql 12857344 8月 19 19:39 2013 mroonga_storage.mrn.0000000
-rw-rw---- 1 mysql mysql 8437760 8月 19 19:41 2013 mroonga_storage.mrn.0000103
-rw-rw---- 1 mysql mysql 4198400 8月 19 19:41 2013 mroonga_storage.mrn.0000104
-rw-rw---- 1 mysql mysql 4198400 8月 19 19:41 2013 mroonga_storage.mrn.0000105
-rw-rw---- 1 mysql mysql 29634560 8月 19 19:41 2013 mroonga_storage.mrn.0000106
-rw-rw---- 1 mysql mysql 1048576 8月 19 19:39 2013 mroonga_storage.mrn.001

mroonga_storage:
合計 16
-rw-rw---- 1 mysql mysql 61 8月 19 19:38 2013 db.opt
-rw-rw---- 1 mysql mysql 8638 8月 19 19:39 2013 tweet.frm
60387835

$ ls -l mroonga_wrapper* | tee >(awk '{i+=$5}END{print i}')
-rw-rw---- 1 mysql mysql 4096 8月 19 19:39 2013 mroonga_wrapper.mrn
-rw-rw---- 1 mysql mysql 12857344 8月 19 19:39 2013 mroonga_wrapper.mrn.0000000
-rw-rw---- 1 mysql mysql 65536 8月 19 19:39 2013 mroonga_wrapper.mrn.0000103
-rw-rw---- 1 mysql mysql 1048576 8月 19 19:39 2013 mroonga_wrapper.mrn.001

mroonga_wrapper:
合計 1464
-rw-rw---- 1 mysql mysql 61 8月 19 19:38 2013 db.opt
-rw-rw---- 1 mysql mysql 1263404 8月 19 19:41 2013 tweet.MYD
-rw-rw---- 1 mysql mysql 217088 8月 19 19:41 2013 tweet.MYI
-rw-rw---- 1 mysql mysql 8638 8月 19 19:39 2013 tweet.frm
15464743

更新時刻やファイルサイズからして、.mrnと.mrn.000000はなんかテーブル情報持ってて、.mrn.00010x(から連番になるんだろう)ファイルがデータ本体ぽい。足すとData_lengthっぽくなるし。
MyISAMのテーブルはフルテキストインデックス作る気がないのでここまでとして、mroonga_storageとmroonga_wrapperでそれぞれインデックスを作ってみる。

mysql55> ALTER TABLE mroonga_storage.tweet ADD FULLTEXT KEY (text) COMMENT 'parser "TokenBigram"';
Query OK, 0 rows affected (0.48 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql55> ALTER TABLE mroonga_wrapper.tweet ADD FULLTEXT KEY (text) COMMENT 'parser "TokenBigram"';
Query OK, 10045 rows affected (1.18 sec)
Records: 10045 Duplicates: 0 Warnings: 0

mysql55> SHOW TABLE STATUS FROM mroonga_storage\G
*************************** 1. row ***************************
Name: tweet
Engine: mroonga
Version: 10
Row_format: Dynamic
Rows: 10045
Avg_row_length: 0
Data_length: 46469120
Max_data_length: 0
Index_length: 0
Data_free: 0
Auto_increment: NULL
Create_time: NULL
Update_time: NULL
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)

mysql55> SHOW TABLE STATUS FROM mroonga_wrapper\G
*************************** 1. row ***************************
Name: tweet
Engine: mroonga
Version: 10
Row_format: Dynamic
Rows: 10045
Avg_row_length: 125
Data_length: 1263404
Max_data_length: 0
Index_length: 217088
Data_free: 0
Auto_increment: NULL
Create_time: NULL
Update_time: 2013-08-19 19:45:54
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment: Engine "MyISAM"
1 row in set (0.00 sec)

$ ls -l mroonga_storage* | tee >(awk '{i+=$5}END{print i}')
-rw-rw---- 1 mysql mysql 4096 8月 19 19:45 2013 mroonga_storage.mrn
-rw-rw---- 1 mysql mysql 17051648 8月 19 19:45 2013 mroonga_storage.mrn.0000000
-rw-rw---- 1 mysql mysql 8437760 8月 19 19:41 2013 mroonga_storage.mrn.0000103
-rw-rw---- 1 mysql mysql 4198400 8月 19 19:41 2013 mroonga_storage.mrn.0000104
-rw-rw---- 1 mysql mysql 4198400 8月 19 19:41 2013 mroonga_storage.mrn.0000105
-rw-rw---- 1 mysql mysql 29634560 8月 19 19:41 2013 mroonga_storage.mrn.0000106
-rw-rw---- 1 mysql mysql 8437760 8月 19 19:45 2013 mroonga_storage.mrn.0000107
-rw-rw---- 1 mysql mysql 2134016 8月 19 19:45 2013 mroonga_storage.mrn.0000108
-rw-rw---- 1 mysql mysql 8392704 8月 19 19:45 2013 mroonga_storage.mrn.0000108.c
-rw-rw---- 1 mysql mysql 1048576 8月 19 19:45 2013 mroonga_storage.mrn.001

mroonga_storage:
合計 16
-rw-rw---- 1 mysql mysql 61 8月 19 19:38 2013 db.opt
-rw-rw---- 1 mysql mysql 8638 8月 19 19:45 2013 tweet.frm
83546619

$ ls -l mroonga_wrapper* | tee >(awk '{i+=$5}END{print i}')
-rw-rw---- 1 mysql mysql 4096 8月 19 19:45 2013 mroonga_wrapper.mrn
-rw-rw---- 1 mysql mysql 17051648 8月 19 19:45 2013 mroonga_wrapper.mrn.0000000
-rw-rw---- 1 mysql mysql 16842752 8月 19 19:45 2013 mroonga_wrapper.mrn.0000104
-rw-rw---- 1 mysql mysql 8437760 8月 19 19:45 2013 mroonga_wrapper.mrn.0000105
-rw-rw---- 1 mysql mysql 7639040 8月 19 19:45 2013 mroonga_wrapper.mrn.0000106
-rw-rw---- 1 mysql mysql 12587008 8月 19 19:45 2013 mroonga_wrapper.mrn.0000106.c
-rw-rw---- 1 mysql mysql 1048576 8月 19 19:45 2013 mroonga_wrapper.mrn.001

mroonga_wrapper:
合計 1464
-rw-rw---- 1 mysql mysql 61 8月 19 19:38 2013 db.opt
-rw-rw---- 1 mysql mysql 1263404 8月 19 19:45 2013 tweet.MYD
-rw-rw---- 1 mysql mysql 217088 8月 19 19:45 2013 tweet.MYI
-rw-rw---- 1 mysql mysql 8638 8月 19 19:45 2013 tweet.frm
65100071
SHOW TABLE STATUSには計上されないんだね。。容量の増加量はストレージモードで23MiB, ラッパーモードで48MiBくらい。

TokenBigramのトークン数はフルテキスト文字数とイコールで、SUM(CHARACTER_LENGTH(text))で取ったら504972だったので、トークンは概算で50万とする。アルファベットはまとめて1つのトークンにするとかそういうのは取り敢えず今は考えない。
⇒本当はSUM(CHARACTER_LENGTH(text) - 1)になるかと思ったんだけど、ならないみたい。
http://groonga.org/docs/spec/search.html

1トークンあたりのサイズは(2 * charset + PRIMARY KEY SIZE)くらいかなと期待すると、utf8は1文字3バイトでPRIMARY KEYがBIGINTの8バイトで16バイト。

単純に掛け算すると8MiBくらいが期待だったんだけど、とてもそんなもんじゃ済んでない。単純にLOAD DATAしただけでも60MiB食うような感じだから、内部的に色々情報を持っててこれじゃ済まないってことなんだろうけど。

取り敢えず思ったより行くよね、というところに止めておいて他の何かで追試したい。というか、してくる。


TokenMecabでどのくらい減らせるかも調べたい。。

MySQLをWindowsサービスとして動かしている時のコマンドラインオプションとか

$
0
0
特にMySQL Installerや.msiで突っ込んだとき。

ファイル名を指定して実行

services.msc

名前= MySQL

  • --installで自分で登録した時はそのサービス名
  • MySQL5.6のInstallerからはMySQL56ってサービス名にしようとする?


プロパティ

実行ファイルのパスに実際に渡されている引数が見える


こんなかんじで。


懐かしい。。

MySQL Clusterのmod_ndbが色々残念

$
0
0
本家のNDBCLUSTER関連のセミナー行ったりすると、

 Apacheのmod_ndbで直接NDB APIが叩けるんですよ!

とかよく言われるので試してみたメモ。


NDB memcached Engine の時と違ってNDBCLUSTER本体には同梱されていないので、Google Codeからダウンロード。
⇒ https://code.google.com/p/mod-ndb/downloads/list

最新版のアップロードが2009/11な時点でちょっと危なげな気配がする。


取り敢えずhttpd-develを突っ込んでconfigure。

$ ./configure --mysql=/usr/ndb/7.3.1/bin/mysql_config
Server version: Apache/2.4.6 (Unix)
Server built:   Jul 24 2013 11:06:47
Using Apache 2.0
/usr/bin/apu-1-config

Configuring with
    mysql_config =  /usr/ndb/7.3.1/bin/mysql_config
    apxs         =  /usr/sbin/apxs
    apr-config   =  /usr/bin/apr-1-config
    apu-config   =  /usr/bin/apu-1-config

Linking with -lmysys and -lmystrings

Created Makefile.

Removing out-of-date files.

Hello!

   make            : build mod_ndb
   make install    : install mod_ndb.so
   make configtest : test configuration
   make start      : start a test server on port 3080 (see test.conf)
   make stop       : stop the test server
   make restart    : restart the test server

Thanks for trying mod_ndb!


configureで指定必須なのは--apxs=/path/to/apxsと--mysql=/path/to/mysql_configだけど、パスが通ってれば勝手に拾ってきてくれる。
テストサーバーなんて同梱されてるんだ。すてき。

$ make
g++ -c -I/usr/include/httpd -I/usr/include/apr-1 -I/usr/ndb/7.3.1/include -I/usr/ndb/7.3.1/include/storage/ndb -I/usr/ndb/7.3.1/include/storage/ndb/ndbapi -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -fPIC -Wall -O3 -o mod_ndb.o mod_ndb_ap20.cc
mod_ndb_ap20.cc: In function ‘int mod_ndb_post_config(apr_pool_t*, apr_pool_t*, apr_pool_t*, server_rec*)’:
mod_ndb_ap20.cc:53: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc:60: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc: In function ‘void mod_ndb_child_init(ap_pool*, server_rec*)’:
mod_ndb_ap20.cc:110: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc:118: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc: In function ‘apr_status_t mod_ndb_child_exit(void*)’:
mod_ndb_ap20.cc:156: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc: In function ‘void connect_to_cluster(ndb_connection*, server_rec*, config::srv*, ap_pool*, bool)’:
mod_ndb_ap20.cc:190: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc:208: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc:215: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc: In function ‘Ndb* init_instance(ndb_connection*, ndb_instance*, server_rec*, config::srv*, ap_pool*)’:
mod_ndb_ap20.cc:239: error: ‘aplog_module_index’ was not declared in this scope
mod_ndb_ap20.cc: In function ‘ndb_instance* my_instance(request_rec*)’:
mod_ndb_ap20.cc:283: error: ‘aplog_module_index’ was not declared in this scope
make: *** [mod_ndb.o] エラー 1

いきなり1個目でこけてるすてきじゃなーいorz

aplog_module_indexというのを調べてみるとなんかよく判らないけれどApache 2.2と2.4の間でAPIの呼び出しが変わったあたりに原因がありそうな感じ。
試しにhttpdを一度消して2.2のhttpd, httpd-develを突っ込む。

$ make
g++ -c -I/usr/include/httpd -I/usr/include/apr-1 -I/usr/ndb/7.3.1/include -I/usr/ndb/7.3.1/include/storage/ndb -I/usr/ndb/7.3.1/include/storage/ndb/ndbapi -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -fPIC -Wall -O3 -o mod_ndb.o mod_ndb_ap20.cc
g++ -c -I/usr/include/httpd -I/usr/include/apr-1 -I/usr/ndb/7.3.1/include -I/usr/ndb/7.3.1/include/storage/ndb -I/usr/ndb/7.3.1/include/storage/ndb/ndbapi -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -fPIC -Wall -O3 -o Query.o Query.cc
g++ -c -I/usr/include/httpd -I/usr/include/apr-1 -I/usr/ndb/7.3.1/include -I/usr/ndb/7.3.1/include/storage/ndb -I/usr/ndb/7.3.1/include/storage/ndb/ndbapi -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -fPIC -Wall -O3 -o Execute.o Execute.cc
g++ -c -I/usr/include/httpd -I/usr/include/apr-1 -I/usr/ndb/7.3.1/include -I/usr/ndb/7.3.1/include/storage/ndb -I/usr/ndb/7.3.1/include/storage/ndb/ndbapi -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -fPIC -Wall -O3 -o MySQL_value.o MySQL_value.cc
..
LD_RUN_PATH=/usr/ndb/7.3.1/lib g++ -shared -o mod_ndb.so mod_ndb.o Query.o Execute.o MySQL_value.o MySQL_result.o config.o request_body.o handlers.o result_buffer.o output_format.o format_compiler.o format_dumper.o query_source.o JSON_encoding.o NSQL_Parser.o NSQL_Scanner.o JSON_Parser.o JSON_Scanner.o -lndbclient -lmystrings -L/usr/ndb/7.3.1/lib -lmysqlclient -lpthread -lm -lrt -lssl -lcrypto -ldl -lmysys -lstdc++ -lrt
/usr/bin/ld: cannot find -lmystrings
collect2: ld はステータス 1 で終了しました
make: *** [mod_ndb.so] エラー 1


取り敢えずいきなりダメぽだったところは乗り越えたヾ( ´∀`)ノ
が、今度はlibmystringsが無いって怒られてる。

NDB API関連のドキュメントを見てもLDFLAGSに設定しろと言っていて、ソースからコンパイルしているとはいえオプションは粗方有効にしてるんだけどなあ。。

ググってもよく判らないのでソースコードからlibmystringsを探す。


$ grep -r libmystrings *
sql/share/charsets/README:    (libmystrings), and when should it be placed in a charset_name.xml
( ゚д゚) ・・・ 
  
(つд⊂)ゴシゴシ 
  
(;゚д゚) ・・・ 
  
(つд⊂)ゴシゴシゴシ 
  _, ._ 
(;゚ Д゚) …!? 

READMEしか引っかからない。
それに対してNDBCLUSTER 7.1のソースコードで同じことをやるとごっそり引っかかる。
(と、気付くまで7.3と7.2と7.1のバイナリーを順番に落としてconfigureのオプション変えながら何度もやった)

7.1.26のNDBCLUSTERをmakeして、mod_ndbをconfigureしなおして、もっかいmakeしたら通った。


$ make start
/usr/sbin/httpd -f /home/yoku/mod_ndb-1.1-release-r652/httpd.conf
make: *** [start] エラー 1

$ cat logs/ndb_error_log
[Tue Aug 27 15:43:23 2013] [notice] Cannot connect to NDB Cluster (connectstring: "(null)") 1/5
[Tue Aug 27 15:43:26 2013] [notice] Cannot connect to NDB Cluster (connectstring: "(null)") 2/5
[Tue Aug 27 15:43:29 2013] [notice] Cannot connect to NDB Cluster (connectstring: "(null)") 3/5
[Tue Aug 27 15:43:32 2013] [notice] Cannot connect to NDB Cluster (connectstring: "(null)") 4/5
[Tue Aug 27 15:43:35 2013] [notice] Cannot connect to NDB Cluster (connectstring: "(null)") 5/5
[Tue Aug 27 15:43:35 2013] [notice] Connection test failed.  Cannot connect to NDB Cluster.  Apache will exit.
Configuration Failed

ああ、そういえばndbdもndb_mgmdも上げてないや。。(;´д`)
続きはいつかやるかやらないかする。

mysqladminの小ネタ

$
0
0
mysqladminなんて基本的にshutdownの時しか使わないんですが、まあ色々SQLでやる時とのマッピングとか。

取り敢えず5.6.13ベースです。
⇒ http://dev.mysql.com/doc/refman/5.6/en/mysqladmin.html

  • mysqladmin create ⇒ CREATE DATABASEに同じ。
  • mysqladmin debug ⇒ SQLではできない。エラーログにデバッグ情報を出力する。
  • mysqladmin drop ⇒ DROP DATABASEに同じ。
  • mysqladmin extended-status ⇒ SHOW GLOBAL STATUSに同じ。
  • mysqladmin flush-hosts ⇒ FLUSH HOSTSに同じ。
  • mysqladmin flush-logs ⇒ FLUSH LOGSに同じ。ログ種別は設定できない。
  • mysqladmin flush-privileges ⇒ FLUSH PRIVILEGESに同じ。
  • mysqladmin flush-status ⇒ FLUSH STATUSに同じ。ただ、FLUSH STATUSって
    セッションスコープのステータス変数はクリアしてくれるけど
    グローバルスコープの扱いが微妙なので、
    使いどころがどれだけあるのかすごく謎。
  • mysqladmin flush-tables ⇒ FLUSH TABLESに同じ。
  • mysqladmin kill ⇒ KILLに同じ。
  • mysqladmin old-password ⇒ SET SESSION old_passwords= 1;
    SET PASSWORD= PASSOWRD('..');
    SET SESSION old_passwords= 0; にほぼ同じ。
  • mysqladmin password ⇒ SET PASSWORD= PASSWORD('..'); に同じ。
    こっちはold_passwordsの設定値に従う。
  • mysqladmin ping ⇒ SQLではできない。COM_PINGを投げて成功するか、
    認証に失敗すると0が返る。
    (認証に失敗しても、接続を拒否られたということは
    mysqldが生きているということ)
  • mysqladmin processlist ⇒ SHOW PROCESSLISTに同じ。
  • mysqladmin --verbose processlist ⇒ SHOW FULL PROCESSLISTに同じ。
  • mysqladmin reload ⇒ FLUSH PRIVILEGESに同じ。
    つまりmysqladmin flush-privilegesに同じ。
  • mysqladmin reflesh ⇒ FLUSH TABLES; FLUSH LOGS; に同じ。
  • mysqladmin shutdown ⇒ 対応するSQLなし。mysqldのシャットダウン。
  • mysqladmin start-slave ⇒ START SLAVEに同じ。
  • mysqladmin status ⇒ STATUSの結果を簡単にしたような感じ。
  • mysqladmin stop-slave ⇒ STOP SLAVEに同じ。
  • mysqladmin variables ⇒ SHOW GLOBAL VARIABLESに同じ。
  • mysqladmin version ⇒ STATUSの結果を簡単にしたような感じ。


ちなみにコマンドは複数指定可能なので、

$ mysqladmin -uroot -pxxx stop-slave create d2 start-slave flush-status

とかやると、

mysql> STOP SLAVE;
mysql> CREATE DATABASE d2;
mysql> START SLAVE;
mysql> FLUSH STATUS;

相当の動作になる。


あと目新しげな使い方としては(いや昔からあるけどこのオプション)、

$ mysqladmin -uroot -pxxx -r -i 10 extended-status

とやると、10秒ごとに(-i 10)前回との差分を(-r)表示してくれる。
-iはどのコマンドでも使えるけど、-rはextended-status専用。それ以外のコマンドにつけても単純に無視される。

$ mysqladmin -uroot -pxxx -i 5 flush-logs

とかやれば5秒おきにFLUSH LOGSできるよ!(しないよ)

mysqladmin.ccのコードリーディングのさわり

$
0
0
みんなmysqladminが大好きっぽいのでおいておきますね。

まずはソースコード取ってきて解凍します。全部解凍するの面倒なので、mysqladmin.ccだけ取り出しますかね。

$ wget http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.13.tar.gz/from/http://cdn.mysql.com/
$ tar xzf mysql-5.6.13.tar.gz mysql-5.6.13/client/mysqladmin.cc
$ vim mysql-5.6.13/client/mysqladmin.cc

はい。
頭の方から、GPLv2のライセンス表記(1~16), 各種変数の定義(35~68)くらい眺めておくと良いかも知れません。72~92に関数の定義があるのは、MySQL同梱のクライアントは個別にヘッダーファイルが作られていないからです。
98行目からcommandsとcommand_namesが定義されていて、command_namesが引数から受け取るコマンド、commandsがそれを内部で参照するときのマップです。まあ名前見れば何にマップされてるか判りますね。

124行目からのmy_long_optionsが受け取れるオプションを入れた構造体です。自分でオプション作る時にはここにも追加します。my_option構造体のプロトタイプはinclude/my_getopt.hに入っていますので、パラメータがよく判らない時は見ますが、似ている引数のやつをコピペしてテキトーに直せば動きます。

1つ飛ばして312行目からがmain関数です。322行目~343行目あたりでオプションを解釈してopt_*変数に突っ込み、350行目からmysql_option関数を使ってMySQLへのコネクション構造体(mysql)にオプションとして設定していきます。397行目までそんな感じです。

399行目、sql_connectでついにMySQLに接続しに行きます。400~422行目はMySQLへの接続に失敗した時の処理で、渡されたコマンドがpingだけなら接続エラーであっても0を返すような処理(エラーコードが返ってくる= mysqldは生きている、だから)が入ってます。

434行目からが接続に成功した時の処理で、先日の-iオプションとかついてる時はここをループします。438行目のexecute_commandsが実際に接続してゴニョゴニョしているところです。execute_commands関数は623行目から定義されています。

あとは丁寧に見ていけばいけると思います。
SQLにマッピングされているコマンドはsprintfでbuffにSQLを突っ込んでmysql_queryでクエリーとして投げる、マッピングされてないpingとかshutdownに関しては、libmysql/libmysql.cに定義された関数を呼んで処理しています。

これでみんなmysqladminのforkが作れますね!
Have fun!!

--replication-*-dbが判定するのは原則カレントデータベース

$
0
0
--replication-do-db, --replication-ignore-dbの判定ロジックについて。

公式はこちら。
http://dev.mysql.com/doc/refman/5.6/en/replication-rules-db-options.html

なんだけど、--replication-*-dbで指定しているスキーマなのにレプリケーションされない! という話はわりとあるあるなので書いておく。

  • STATEMENTモード(MIXEDでも非決定性の関数とかがなければこっち)でロギングされている場合は、default database(SELECT DATABASE()で出てくるやつ)
  • ROWモードの場合は、実際に影響を受けるデータベース
が、--replication-*-dbと比較判定される。

テスト。5.5がマスターで5.6がスレーブ。

mysql56> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: localhost
Master_User: replicator
Master_Port: 64055
Connect_Retry: 60
Master_Log_File: bin.000002
Read_Master_Log_Pos: 1986
Relay_Log_File: relay.000010
Relay_Log_Pos: 1878
Relay_Master_Log_File: bin.000002
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB: d1,d2
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:..

スレーブではd1, d2スキーマだけ--replicate-do-dbに指定。


マスターのbinlog_format= STATEMENTの場合。

$ mysql55 -e "SELECT @@global.binlog_format"
+------------------------+
| @@global.binlog_format |
+------------------------+
| STATEMENT |
+------------------------+

$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())"
$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())" d1
$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())" d2
$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())" d3
$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())" information_schema

カレントデータベースを変えながら5行INSERT。


mysql55> SELECT * FROM d1.t1;
+---------------------+--------------------+
| ts | val |
+---------------------+--------------------+
| 2013-09-05 11:52:21 | NULL |
| 2013-09-05 11:52:25 | d1 |
| 2013-09-05 11:52:26 | d2 |
| 2013-09-05 11:52:27 | d3 |
| 2013-09-05 11:52:31 | information_schema |
+---------------------+--------------------+
5 rows in set (0.05 sec)

マスターでは当然こうなる。


mysql56> SELECT * FROM d1.t1;
+---------------------+------+
| ts | val |
+---------------------+------+
| 2013-09-05 11:52:25 | d1 |
| 2013-09-05 11:52:26 | d2 |
+---------------------+------+
2 rows in set (0.00 sec)

スレーブではこうなる。
d3, information_schemaはd1でもd2でもないのは自明として、NULLもd1でもd2でもない、というのがよくあるハマりどころ。シェルスクリプトからmysqlコマンドラインクライアント呼んでるときとか、MySQL Workbenchで接続プロパティを作る時にDefault Schemaを空っぽのままにしてるときとか。GUI系はこれ危うい気がする。

あとついでにこの設定はSQLスレッドのものでI/Oスレッドには関係ないので、

$ mysqlbinlog /usr/mysql/5.6.13/data/relay.000010
..
# at 2707
#130905 11:52:21 server id 1055 end_log_pos 2917 Query thread_id=13 exec_time=0 error_code=0
SET TIMESTAMP=1378349541/*!*/;
INSERT INTO d1.t1 (val) VALUES (DATABASE())
/*!*/;
# at 2809
#130905 11:52:21 server id 1055 end_log_pos 2944 Xid = 87
COMMIT/*!*/;
# at 2836
#130905 11:52:25 server id 1055 end_log_pos 3010 Query thread_id=14 exec_time=0 error_code=0
SET TIMESTAMP=1378349545/*!*/;
BEGIN
/*!*/;
# at 2902
#130905 11:52:25 server id 1055 end_log_pos 3114 Query thread_id=14 exec_time=0 error_code=0
use `d1`/*!*/;
SET TIMESTAMP=1378349545/*!*/;
INSERT INTO d1.t1 (val) VALUES (DATABASE())
/*!*/;
# at 3006
#130905 11:52:25 server id 1055 end_log_pos 3141 Xid = 90
COMMIT/*!*/;
# at 3033
#130905 11:52:26 server id 1055 end_log_pos 3207 Query thread_id=15 exec_time=0 error_code=0
SET TIMESTAMP=1378349546/*!*/;
BEGIN
/*!*/;
# at 3099
#130905 11:52:26 server id 1055 end_log_pos 3311 Query thread_id=15 exec_time=0 error_code=0
use `d2`/*!*/;
SET TIMESTAMP=1378349546/*!*/;
INSERT INTO d1.t1 (val) VALUES (DATABASE())
/*!*/;
# at 3203
#130905 11:52:26 server id 1055 end_log_pos 3338 Xid = 93
COMMIT/*!*/;
# at 3230
#130905 11:52:27 server id 1055 end_log_pos 3404 Query thread_id=16 exec_time=0 error_code=0
SET TIMESTAMP=1378349547/*!*/;
BEGIN
/*!*/;
# at 3296
#130905 11:52:27 server id 1055 end_log_pos 3508 Query thread_id=16 exec_time=0 error_code=0
use `d3`/*!*/;
SET TIMESTAMP=1378349547/*!*/;
INSERT INTO d1.t1 (val) VALUES (DATABASE())
/*!*/;
# at 3400
#130905 11:52:27 server id 1055 end_log_pos 3535 Xid = 96
COMMIT/*!*/;
# at 3427
#130905 11:52:31 server id 1055 end_log_pos 3617 Query thread_id=17 exec_time=0 error_code=0
SET TIMESTAMP=1378349551/*!*/;
BEGIN
/*!*/;
# at 3509
#130905 11:52:31 server id 1055 end_log_pos 3737 Query thread_id=17 exec_time=0 error_code=0
use `information_schema`/*!*/;
SET TIMESTAMP=1378349551/*!*/;
INSERT INTO d1.t1 (val) VALUES (DATABASE())
/*!*/;
# at 3629
#130905 11:52:31 server id 1055 end_log_pos 3764 Xid = 99
COMMIT/*!*/;

リレーログまでは影響を受けずにちゃんと来ている。


逆のパターンとして、

$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())"
$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())" d1
$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())" d2
$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())" d3
$ mysql55 -e "INSERT INTO d3.t1 (val) VALUES (DATABASE())" information_schema

mysql55> SELECT * FROM d3.t1;
+---------------------+--------------------+
| ts | val |
+---------------------+--------------------+
| 2013-09-05 12:00:18 | NULL |
| 2013-09-05 12:00:20 | d1 |
| 2013-09-05 12:00:21 | d2 |
| 2013-09-05 12:00:23 | d3 |
| 2013-09-05 12:00:28 | information_schema |
+---------------------+--------------------+
5 rows in set (0.01 sec)

mysql56> SELECT * FROM d3.t1;
+---------------------+------+
| ts | val |
+---------------------+------+
| 2013-09-05 12:00:20 | d1 |
| 2013-09-05 12:00:21 | d2 |
+---------------------+------+
2 rows in set (0.00 sec)

--replicate-do-dbにリストされていなくても、カレントデータベースがマッチすればレプリケートされる。


binlog_format=ROWにすると、

$ mysql55 -e "SELECT @@global.binlog_format"
+------------------------+
| @@global.binlog_format |
+------------------------+
| ROW |
+------------------------+

$ mysql55 -e "INSERT INTO d2.t1 (val) VALUES (DATABASE())"
$ mysql55 -e "INSERT INTO d2.t1 (val) VALUES (DATABASE())" d1
$ mysql55 -e "INSERT INTO d2.t1 (val) VALUES (DATABASE())" d2
$ mysql55 -e "INSERT INTO d2.t1 (val) VALUES (DATABASE())" d3
$ mysql55 -e "INSERT INTO d2.t1 (val) VALUES (DATABASE())" information_schema

mysql55> SELECT * FROM d2.t1;
+---------------------+--------------------+
| ts | val |
+---------------------+--------------------+
| 2013-09-05 12:07:16 | NULL |
| 2013-09-05 12:07:18 | d1 |
| 2013-09-05 12:07:19 | d2 |
| 2013-09-05 12:07:20 | d3 |
| 2013-09-05 12:07:24 | information_schema |
+---------------------+--------------------+
5 rows in set (0.01 sec)

mysql56> SELECT * FROM d2.t1;
+---------------------+--------------------+
| ts | val |
+---------------------+--------------------+
| 2013-09-05 12:07:16 | NULL |
| 2013-09-05 12:07:18 | d1 |
| 2013-09-05 12:07:19 | d2 |
| 2013-09-05 12:07:20 | d3 |
| 2013-09-05 12:07:24 | information_schema |
+---------------------+--------------------+
5 rows in set (0.00 sec)


この通り。


本日のネタ提供は @nekogeruge_987さん でした。ごちそうさまです。

MySQLのView, Stored Procedureのセキュリティ設定、デフォルトはDEFINER

$
0
0
DEFINER= rootでSQL SECURITY DEFINERの話です。
論より実験。


d2user> SHOW GRANTS;
+------------------------------------------------+
| Grants for d2user@% |
+------------------------------------------------+
| GRANT USAGE ON *.* TO 'd2user'@'%' |
| GRANT ALL PRIVILEGES ON `d2`.* TO 'd2user'@'%' |
+------------------------------------------------+
2 rows in set (0.00 sec)


d2データベースにだけ権限を持ったd2userがいます。
ここにrootで、おとなりd1データベースのテーブルをSELECTするViewを作ります。


root> CREATE VIEW d2.v1 AS SELECT * FROM d1.t1;
Query OK, 0 rows affected (0.01 sec)

root> SELECT * FROM d2.v1;
+-----+-------+
| num | val |
+-----+-------+
| 1 | one |
| 2 | two |
| 3 | three |
+-----+-------+
3 rows in set (0.00 sec)


こんな感じで。
さてオチは読めると思いますがこのd2.v1はd2userで


d2user> SELECT * FROM d2.v1;
+-----+-------+
| num | val |
+-----+-------+
| 1 | one |
| 2 | two |
| 3 | three |
+-----+-------+
3 rows in set (0.00 sec)


参照できます。
どんなロジックが働いているかというと、


d2user> SHOW CREATE VIEW d2.v1\G
*************************** 1. row ***************************
View: v1
Create View: CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `d1`.`t1`.`num` AS `num`,`d1`.`t1`.`val` AS `val` from `d1`.`t1`
character_set_client: utf8
collation_connection: utf8_general_ci
1 row in set (0.00 sec)


さっきのCREATE VIEWで設定しなかったところが暗黙のデフォルトで補完されてますね。

ALGORITHMはViewを参照するときのベーステーブルからの取り出し方に関わるものなので今回は関係なく、DEFINER= root@localhost と SQL SECURITY DEFINERがキモです。

DEFINERはこのViewに紐付けられたユーザー属性で、暗黙のデフォルトはCURRENT_USERです。CREATE VIEWを叩いたのはroot@localhostだったのでその値が来ています。DEFINER= でCURRENT_USER以外の値を設定できるのはSuper_priv持ちのユーザーだけです。

SQL SECURITY DEFINERは「このビューを操作する時はDEFINERの権限を使ってViewの中身を処理する」という属性です。つまり、このビューに対するあらゆる操作はroot@localhostの権限で行われます。つまりが、

d2user> INSERT INTO v1 VALUES (4, 'four');
Query OK, 1 row affected (0.01 sec)

d2user> SELECT * FROM v1;
+-----+-------+
| num | val |
+-----+-------+
| 1 | one |
| 2 | two |
| 3 | three |
| 4 | four |
+-----+-------+
4 rows in set (0.00 sec)

d2user> DELETE FROM v1 WHERE num= 2;
Query OK, 1 row affected (0.02 sec)

d2user> SELECT * FROM v1;
+-----+-------+
| num | val |
+-----+-------+
| 1 | one |
| 3 | three |
| 4 | four |
+-----+-------+
3 rows in set (0.01 sec)


なんでもできます。
これを防ぐにはDEFINERをテキトーなユーザー権限に変えるか、SQL SECURITYをINVOKERにするかのどちらかです。

DEFINERを書き換えるのはSuper_priv持ちのユーザーでないといけない、また、結局「操作したユーザーではないユーザー権限でベーステーブルにアクセスする」可能性が捨てきれないので、SQL SECURITY INVOKERにすることが多いんじゃないでしょうか。


root> ALTER SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM d1.t1;
Query OK, 0 rows affected (0.00 sec)

d2user> SELECT * FROM v1;
ERROR 1356 (HY000): View 'd2.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them

Stored Procedure, Stored FunctiomもSQL SECURITY {DEFINER|INVOKER}を持っていて、同じような動き方をします。
RDSでは逆にSQL SECURITY DEFINERを上手く使って、mysql.slow_logテーブルを「rootだろうと直接操作させず、rdsadmin(でしたっけ?)の権限で操作する」ことでローテーションさせたりできますね。

ちなみにTRIGGERはDEFINER項目はありますがSQL SECURITY {DEFINER|INVOKER}は無く、常にDEFINER権限で操作されます。


root> CREATE TRIGGER t1_ins_trigger AFTER INSERT ON d2.t1 FOR EACH ROW INSERT INTO d1.t2 VALUES (new.num, new.val)//
Query OK, 0 rows affected (0.01 sec)

root> SHOW CREATE TRIGGER t1_ins_trigger\G
*************************** 1. row ***************************
Trigger: t1_ins_trigger
sql_mode: NO_ENGINE_SUBSTITUTION
SQL Original Statement: CREATE DEFINER=`root`@`localhost` TRIGGER t1_ins_trigger AFTER INSERT ON d2.t1 FOR EACH ROW INSERT INTO d1.t2 VALUES (new.num, new.val)
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)

d2user> INSERT INTO d2.t1 VALUES (10, 'ten');
Query OK, 1 row affected (0.01 sec)

root> SELECT * FROM d1.t2;
+-----+------+
| num | val |
+-----+------+
| 10 | ten |
+-----+------+
1 row in set (0.00 sec)


ちなみに、存在しないユーザーをDEFINER= で設定してSQL SECURITY DEFINERすると、誰からも操作できないViewが出来上がります。mysqldumpでビュー定義だけ引っ張り出してテスト環境にリストアした時にたまにやりますね。

知られざるinnodb_flush_methodの値

$
0
0
innodb_flush_methodの値といえば、fdatasync(暗黙のデフォルト)かO_DIRECTくらいしか知らなかった訳だが、O_DSYNCやO_DIRECT_NO_FSYNC(5.6.7から)もあったり、Percona ServerにはALL_O_DIRECTなんてものがあったりする。
http://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_flush_method
http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_flush_method
http://www.percona.com/doc/percona-server/5.1/scalability/innodb_io.html?id=percona-server:features:innodb_io_51&redirect=2#innodb_flush_method


が、実はlittlesyncとnosyncという値も設定できるっぽい。mysql-5.6.13/storage/innobase/srv/srv0start.cc には、

1665         } else if (0 == ut_strcmp(srv_file_flush_method_str, "fsync")) {
1666                 srv_unix_file_flush_method = SRV_UNIX_FSYNC;
1667
1668         } else if (0 == ut_strcmp(srv_file_flush_method_str, "O_DSYNC")) {
1669                 srv_unix_file_flush_method = SRV_UNIX_O_DSYNC;
1670
1671         } else if (0 == ut_strcmp(srv_file_flush_method_str, "O_DIRECT")) {
1672                 srv_unix_file_flush_method = SRV_UNIX_O_DIRECT;
1673
1674         } else if (0 == ut_strcmp(srv_file_flush_method_str, "O_DIRECT_NO_FSYNC")) {
1675                 srv_unix_file_flush_method = SRV_UNIX_O_DIRECT_NO_FSYNC;
1676
1677         } else if (0 == ut_strcmp(srv_file_flush_method_str, "littlesync")) {
1678                 srv_unix_file_flush_method = SRV_UNIX_LITTLESYNC;
1679
1680         } else if (0 == ut_strcmp(srv_file_flush_method_str, "nosync")) {
1681                 srv_unix_file_flush_method = SRV_UNIX_NOSYNC;
 しかもちゃんと

mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_flush_method';
+---------------------+--------+
| Variable_name       | Value  |
+---------------------+--------+
| innodb_flush_method | nosync |
+---------------------+--------+
1 row in set (0.00 sec)

認識されてるし。。


こんな機能メンテしたくないからドキュメントからは外した、とInnoDB開発者の弁。
 ⇒http://lists.mysql.com/mysql/148920

かなり速そうだから、バックアップからの戻しならアリかもね。。

MariaDB 10.0のMulti-source replicationを試す

$
0
0
MariaDB 10.0.2でインプリメントされたMulti-source replication(1スレーブに複数マスター構成)、MySQL 5.6に紛れて試していなかったんだけれど時間があったのでちょっとだけ試してみた。

リファレンス ⇒ https://mariadb.com/kb/en/multi-source-replication/

MariaDB KnowledgebaseはWEB遅い、マニュアル見づらい感。。



mariadb> SHOW ALL SLAVES STATUS\G
Empty set (0.00 sec)

mariadb> CHANGE MASTER 'mysql56' TO master_host= 'localhost', master_port= 64056, master_user= 'replicator', master_log_file= 'bin.000001', master_log_pos= 1;
Query OK, 0 rows affected (0.03 sec)

mariadb> SHOW SLAVE 'mysql56' STATUS\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: localhost
Master_User: replicator
Master_Port: 64056
Connect_Retry: 60
Master_Log_File: bin.000001
Read_Master_Log_Pos: 4
Relay_Log_File: relay-mysql56.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: bin.000001
Slave_IO_Running: No
Slave_SQL_Running: No
..
1 row in set (0.00 sec)

mariadb> CHANGE MASTER 'mysql55' TO master_host= 'localhost', master_port= 64055, master_user= 'replicator', master_log_file= 'bin.000001', master_log_pos= 1;
Query OK, 0 rows affected (0.02 sec)

mariadb> SHOW ALL SLAVES STATUS\G
*************************** 1. row ***************************
Connection_name: mysql55
Slave_SQL_State:
Slave_IO_State:
Master_Host: localhost
Master_User: replicator
Master_Port: 64055
Connect_Retry: 60
Master_Log_File: bin.000001
Read_Master_Log_Pos: 4
Relay_Log_File: relay-mysql55.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: bin.000001
Slave_IO_Running: No
Slave_SQL_Running: No
..
*************************** 2. row ***************************
Connection_name: mysql56
Slave_SQL_State:
Slave_IO_State:
Master_Host: localhost
Master_User: replicator
Master_Port: 64056
Connect_Retry: 60
Master_Log_File: bin.000001
Read_Master_Log_Pos: 4
Relay_Log_File: relay-mysql56.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: bin.000001
Slave_IO_Running: No
Slave_SQL_Running: No
..
2 rows in set (0.00 sec)

mariadb> SHOW SLAVE STATUS\G
Empty set (0.00 sec)

mariadb> SET SESSION default_master_connection= 'mysql55';
Query OK, 0 rows affected (0.01 sec)

mariadb> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: localhost
Master_User: replicator
Master_Port: 64055
Connect_Retry: 60
Master_Log_File: bin.000001
Read_Master_Log_Pos: 107
Relay_Log_File: relay-mysql55.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: bin.000001
Slave_IO_Running: No
Slave_SQL_Running: No
..

mariadb> START SLAVE;
ERROR 1200 (HY000): The server is not configured as slave; fix in config file or with CHANGE MASTER TO

こんな感じで設定。
取り敢えずハマったのは、
  • SHOW ALL SLAVESとか。
  • SET SESSION default_master_connectionしておくと、コネクション名を指定しないSTART SLAVEとかはそのコネクションあてになる。
  • connection name(mysql55とかmysql56とかしてるやつ)はクォートしないといけない。
  • START SLAVE 'mysql55'; もSTART ALL SLAVES; も何故か通らない(何が足りないのか調べてない。。)
    とはいえ、まあ何か面白そうですよね!

    以上、PHP Conference 2013のLT聞きながらでした。。


    【2013/09/17 12:49】
    START SLAVEできなかったのは、server-idを指定していなかったというまさかのオチでしたよ! orz

    MySQL 5.6.11以降のInnoDBテーブルでAUTO_INCREMENTの値を小さくできない件

    $
    0
    0
    なんか変だなーと思っていたんですがすっきりしました。

    mysql56> CREATE TABLE t1 (num serial, val varchar(32)) Engine= InnoDB;
    Query OK, 0 rows affected (0.05 sec)

    mysql56> SHOW CREATE TABLE t1\G
    *************************** 1. row ***************************
    Table: t1
    Create Table: CREATE TABLE `t1` (
    `num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `val` varchar(32) DEFAULT NULL,
    UNIQUE KEY `num` (`num`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)

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

    mysql56> SELECT * FROM t1;
    +-----+------+
    | num | val |
    +-----+------+
    | 1 | one |
    | 2 | two |
    +-----+------+
    2 rows in set (0.00 sec)

    mysql56> SHOW CREATE TABLE t1\G
    *************************** 1. row ***************************
    Table: t1
    Create Table: CREATE TABLE `t1` (
    `num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `val` varchar(32) DEFAULT NULL,
    UNIQUE KEY `num` (`num`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)

    mysql56> ALTER TABLE t1 AUTO_INCREMENT= 10000;
    Query OK, 0 rows affected (0.01 sec)
    Records: 0 Duplicates: 0 Warnings: 0

    mysql56> SHOW CREATE TABLE t1\G
    *************************** 1. row ***************************
    Table: t1
    Create Table: CREATE TABLE `t1` (
    `num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `val` varchar(32) DEFAULT NULL,
    UNIQUE KEY `num` (`num`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)

    mysql56> ALTER TABLE t1 AUTO_INCREMENT= 10;
    Query OK, 0 rows affected (0.01 sec)
    Records: 0 Duplicates: 0 Warnings: 0

    mysql56> SHOW CREATE TABLE t1\G
    *************************** 1. row ***************************
    Table: t1
    Create Table: CREATE TABLE `t1` (
    `num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `val` varchar(32) DEFAULT NULL,
    UNIQUE KEY `num` (`num`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)

    5.5のInnoDBならこれできるんです。5.6のMyISAMでもできます。

    マニュアルの書き方を比較しても、5.6のInnoDBでだけAUTO_INCREMENTを小さくできない様には読めないのでなんかもやもやします。

    5.5 ALTER TABLE: http://dev.mysql.com/doc/refman/5.5/en/alter-table.html
    ⇒MAX(auto_increment_column)より小さい値を与えた場合、MyISAMはMAX(auto_increment_column)+ 1に勝手に切り詰めて、InnoDBは値が変更されない。
    ⇒MAX(auto_increment_column)より大きい値の場合は言及なし。

    5.6 ALTER TABLE: http://dev.mysql.com/doc/refman/5.6/en/alter-table.html
    ⇒MyISAMもInnoDBもMAX(auto_increment_column)より小さい値を与えた場合はMAX(auto_increment_column)+ 1に切り詰める。
    ⇒MAX(auto_increment_column)より大きい値の場合は言及なし。


    と思ったらバグレポート見つけた。これだ。

    MySQL Bug #69882: http://bugs.mysql.com/bug.php?id=69882


    バグレポートのWorkaroundにはTRUNCATEしか紹介されていませんが、ALTER TABLE .. ALGORITHM= COPYを使えば小さくできました。ドキュメントの記載漏れなのか、バグとして扱われるのか微妙なとこですね。。


    mysql56> ALTER TABLE t1 ALGORITHM= COPY, AUTO_INCREMENT= 10;
    Query OK, 2 rows affected (0.11 sec)
    Records: 2 Duplicates: 0 Warnings: 0

    mysql56> SHOW CREATE TABLE t1\G
    *************************** 1. row ***************************
    Table: t1
    Create Table: CREATE TABLE `t1` (
    `num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `val` varchar(32) DEFAULT NULL,
    UNIQUE KEY `num` (`num`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8
    1 row in set (0.00 sec)

    これ見ても、InnoDB Online ALTER TABLEの時はできない、と言ってるようには見えないんですけどねー。。
    http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html

    pt-online-schema-changeのクエリーをレプリケーションしない方法

    $
    0
    0
    みんなのヒーロー pt-online-schema-changeですが、HandlerSocketと非常に相性が悪い。というかHandlerSocketとテーブルのメタデータをロックするDDL(つまりフツーのALTER TABLEでも)相性がすごく悪い。

    去年もハマってましたね。季節ものなんでしょうか。まだ試す予定ってタイトルに書いてあった割に1年放置してましたはい。
    ⇒ HandlerSocketの不思議な動作(まだ試す予定)

    HandlerSocketで読み書きしている環境でALTER TABLEをかけようとすると、

    1. ALTER TABLEはHandlerSocketのTable Closeを待つ
    2. 後続のクエリーはALTER TABLEのメタデータロックを待つ
    3. 何故かいつまでもTableをクローズせず使いまわすHandlerSocketがいる気配
    4. HandlerSocketの中にはテーブルをクローズして、ALTER TABLEのメタデータロック待ちに入るスレッドもいる
    5. もうだめぽ
    という訳で、HandlerSocketを(readの時だけ)使っているウチの環境でpt-online-schema-changeをする時は、

    1. マスターにはHandlerSocketが来ていないのでsql_log_bin= OFFでpt-online-schema-change
    2. スレーブに来ているHandlerSocketの向き先を全てマスターに向ける
    3. スレーブのHSコネクションが全部切れたら、FLUSH TABLESしてからpt-online-schema-change
    というステップでやっています。


    pt-online-schema-changeでセッション変数を押し込むには--set-vars=".."で設定します。

    $ pt-online-schema-change --execute --set-vars="sql_log_bin= OFF" --alter "MODIFY  order_col varchar(100) COLLATE utf8_bin NOT NULL, DROP KEY IX_ORDER_COL, Engine= InnoDB" h=localhost,D=dbname,t=tablename,u=root,p=xxxx,S=/var/lib/mysql/mysql.sock

    これで一連の操作の手前に"SET SESSION sql_log_bin= OFF;"が叩き込まれるので、1つ目のステップが実行できます。

    Percona-toolkit 2.2.1, 2.2.2のpt-online-schema-changeには--set-vars="SQL_LOG_BIN= 0"を渡すとInvalidだと怒られるバグがあるので、なんとなくsql_log_bin= OFFで設定するようにしてあります。
    ⇒ https://bugs.launchpad.net/percona-toolkit/+bug/1182856

    これ素敵だよなぁ。。

    MySQL Cluster Casual Talksで話せなかった、俺がNDBCLUSTERを使わない理由

    $
    0
    0
    なぁんて、もったいぶった話じゃないんですけど。

    あ、@kamipoさんアイスごちそうさまです。





    NDBCLUSTERを使いたくても使えない(んだか使わないんだかよく判らなくなってきた)理由は、

    • 今MySQL Serverで動いているのを全て突っ込むには容量がつらい。
      • 更新主体のテーブルだけ、というのは障害点が増えるだけなのでパス。
      • Diskテーブルに落としたらオワコンなベンチマークも見られたし。
    • ウチではmroongaによる全文検索マンセー。
      • だったらやりようによって全文検索&クラスタリングのXtraDB Clusterやりたい。
    • 何でもいいから突っ込むというのはどうも美しくない。
      • InnoDBの方が性能が出るパターンなら(ほとんど、ですね)InnoDBで上手く冗長化したい。
      • あと、それじゃあ説得力不足でサーバー出してもらえない。
    あたりです。

    シングルサインオンって訳じゃないんですが、1回ユーザー登録するといろんなサービスでそのアカウント情報を使い回せるっていう仕組みがあるので、そこの認証部分はNDBCLUSTER向きだなっと思っていたりしますが、そういうとこに限ってやっぱり改修しにくかったりするんですよねー。。


    スライドは半分くらいが余談で占められてるんですが、
     @Rkajiyamaさんの話と め ち ゃ め ち ゃ カ ブ っ て る し
    でもまあいいんです色々お話楽しかったです。

    最後になりますが、ご登壇いただいた @Rkajiyamaさん、@nippondanjiさん、@tsakuradaさん、@yyamasaki1さん、ご来場いただいたみなさま、ハッシュタグまとめてくれた @76whizkidzさん、ブログ書いてくれたみなさま、手伝ってくれた会社のみなさま。

    どうもありがとうございました。とても楽しかったです(俺が)
    次はMyISAM Deep Talksですかね :) 楽しみにしています。

    さて、今日はMyNA会だー。

    MySQL 5.7.2のEXPLAIN FOR CONNECTIONの使い方

    $
    0
    0
    昨日のMyNA会でちょっと話が出ていたEXPLAIN FOR CONNECTIONの使い方。
    正直これそんなに便利なものじゃないと思うんですが。

    http://dev.mysql.com/doc/refman/5.7/en/explain.html

    {explainable_stmt | FOR CONNECTION connection_id}
    となっている通り、EXPLAINに食わせるSQLステートメントの代わりにFOR CONNECTION x; として指定する。


    conn1> explain SELECT * FROM t1, t1 AS t2;
    +----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------------+
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    +----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------------+
    | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 953856 | NULL |
    | 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 953856 | Using join buffer (Block Nested Loop) |
    +----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------------+
    2 rows in set (0.00 sec)

    conn1> SELECT * FROM t1, t1 AS t2;
    ..(実行中)

    conn2> show processlist;
    +----+------+-----------+------+---------+------+--------------+----------------------------+
    | Id | User | Host | db | Command | Time | State | Info |
    +----+------+-----------+------+---------+------+--------------+----------------------------+
    | 3 | root | localhost | d1 | Query | 5 | Sending data | SELECT * FROM t1, t1 AS t2 |
    | 4 | root | localhost | NULL | Query | 0 | init | show processlist |
    +----+------+-----------+------+---------+------+--------------+----------------------------+
    2 rows in set (0.51 sec)

    conn2> EXPLAIN FOR CONNECTION 3;
    +----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------------+
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
    +----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------------+
    | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 953856 | NULL |
    | 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 953856 | Using join buffer (Block Nested Loop) |
    +----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------------+
    2 rows in set (0.25 sec)

    こんな感じ。おんなじじゃーん、て。

    ところでEXPLAIN FOR CONNECTIONかけるとかけた方のコネクション(id= 3のコネクション)がアボートするんだけどこれバグ?;

    【2013/10/01 13:36】
    EXPLAIN FOR CONNECTIONかけなくても落ちた。

    【2013/10/01 13:40】
    OOM Killerに殺されてるだけだった…(´;ω;`)

    Isucon #3に出場してチームをお通夜にしてしまった話

    $
    0
    0
    俺の大ポカに巻き込まれたチームメイトの @studio3104さんと @ayumu83sさんごめんなさい。
    俺がしでかしたことをありのまま話すぜ。


    • 取り敢えず吊るしのベンチマークを取る。
    • 開始早々、MySQL 5.6.13をPercona Server 5.6.13に取替え。
      • この時、datadirは使い回しでバックアップを取らなかった。
    • my.cnfをチューニングしてibdata1とib_logfile*を再作成。
      • その間アプリ側ではTCP/IP接続をsocket接続に書き換えてもらったり、プロファイル仕込んでもらったり。
    • この時点からベンチマークが通らなくなる。
      • 原因はInnoDB memcachedを殺したからなんですがこの時点では判らず。
      • アプリ側のコードを切り戻したりPercona ServerをMySQLに戻したりしたけれど、ベンチマークは相変わらずFAILする。
    • すたじおさんの到着も遅れていたし、取り敢えずエラーは後で解析するとして判るところからチューニングを開始することに。
    • テーブルにINDEXをガスガス追加。INSERT, UPDATEのトラフィックは大したことがなさそうだったので、基本covering index狙いの長めのキー。covering indexが狙えないところはORDER BY狙いのキー。
    • Percona Serverだろうとオプティマイザーは残念な感じなので、片っ端からUSE INDEXを付けてインデックスを固定。あと、変なループがあるって教えてもらったのでJOINに書き換え。
      • このJOINも、最終的にはスコアが下がったっていって切り戻したんだけど(´・ω・`)
    • Apacheをnginxに入れ替えたりアプリをデバッグしてもらっても原因が見えないし、general logを眺めていたら"WHERE id=NULL"ってクエリーが㌧でいたので、データがおかしいのかなぁと思ってinit.sql.gzからデータを再投入したり、mysqldumpを吸い上げてリストアしてみたり。当然何も起こらない。
      • ええ、InnoDB memcachedにセッション情報保存してましたから、セッション情報が引き渡されないんですよね。
    • ピンポイントに打ってくるINSERT, UPDATEとSELECTはHandlerSocketかInnoDB memcachedでやっつけるかと思ってNet::HandlerSocketをmake。InnoDB memcached PluginもPercona Serverのバイナリーには入ってなかったのでソース落としてきてmake。
      • innodb_memcache.containersをごにょごにょして何度か再起動するものの、目的の用途に使うには向かないのでHandlerSocketの方が良さそうだと判断。
      • アプリ環境周りのセットアップが全く判っていないので、HandlerSocketのライブラリをmake installしてもパスが通っておらず結局使えない(´・ω・`)
    • SQLは「結果セットを変えない」ことを大前提にちょっと書き換えただけ。
      • 普段の仕事っぷりが見事に出るなぁ。。_| ̄|○
    • 残り1時間、ついにインスタンスを破棄して新しいインスタンスを立ち上げることを決意。
      • 勿論ベンチ通る。
    • Percona Serverに変える。
      • 通らなくなる。
      • ここかよ! だがデータストアが変わっただけで何が変わる?
    • ところでなんかエラーログのInnoDB memcachedのエラー(" InnoDB_Memcached: the value column c2 in table test/demo_test should be INTEGER, CHAR or VARCHAR.")がウザいので、UNINSTALL PLUGIN。
      • 通らなくなる。
      • Σ(゚д゚lll) うっそぉ!?
      • ま・さ・か…!
    • mysql> SELECT * FROM test.demo_test;
    • うわあああああ(AA略
    • Perconaに入れ替えてインデックス作ってSQLにヒント句追加して…ここまでで、時間切れ。


    というわけで、@studio3104 さんと @ayumu83s さんに
     8 時 間 耐 久 デ バ ッ グ 祭 り
    を強要した挙句、
     責 め る べ き は た だ 一 人
    だけど、2人とも優しくて責めるに責められず、結果お通夜に。

    MySQLしかいじれなかった結果の最終スコアは3440でした。
    本当にごめんなさい。

    mysqldが起動するときにどのmy.cnfを舐めてるのか知りたいとき

    $
    0
    0
    いやまあ、マニュアルに書いてあるけど。
    http://dev.mysql.com/doc/refman/5.6/en/option-files.html

    Linuxで公式MySQL、--defaults-file, --defaults-extra-fileを指定しない場合は、

    • /etc/my.cnf
    • /etc/mysql/my.cnf
    • /usr/local/mysql/etc/my.cnf
      • SYSCONFDIR/my.cnfのこと。
      • 公式のバイナリーはSYSCONFDIR== /usr/local/mysql/etc。cmakeのオプションで変えられる。
      • MariaDBはこいつが設定されていないっぽい。
    • /usr/local/mysql/my.cnf
      • $MYSQL_HOME/my.cnfのこと。
      • basedirを設定してれば$MYSQL_HOME== basedirになる。
    • /home/mysql/.my.cnf
      • $HOME/.my.cnfのこと。コイツだけドットファイル。
    の順番で読む。
    とはいえこんなのめんどくさくて憶えてられなかったり、SYSCONFDIRやMYSQL_HOMEが思いもしない変なところになってたりすることがあったりなかったりするかも知れないので、調べ方だけメモってみる。


    # strace -ff -e open -e stat /usr/mysql/5.5.34/bin/mysqld_multi start 55,56 2>&1 | grep "my\.cnf"

    [pid 7359] stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    [pid 7359] stat("/etc/mysql/my.cnf", 0x7fffa1322e40) = -1 ENOENT (No such file or directory)
    [pid 7359] stat("/usr/local/mysql/etc/my.cnf", 0x7fffa1322e40) = -1 ENOENT (No such file or directory)
    [pid 7359] stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    stat("/etc/mysql/my.cnf", 0x1739130) = -1 ENOENT (No such file or directory)
    stat("/usr/mysql/5.5.34/my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    stat("/usr/mysql/5.5.34/my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 7361] stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    [pid 7361] stat("/etc/mysql/my.cnf", 0x7fffdaeb46e0) = -1 ENOENT (No such file or directory)
    [pid 7361] stat("/usr/local/mysql/etc/my.cnf", 0x7fffdaeb46e0) = -1 ENOENT (No such file or directory)
    [pid 7361] stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 7365] stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    [pid 7365] stat("/etc/mysql/my.cnf", 0x7fff4038abf0) = -1 ENOENT (No such file or directory)
    [pid 7365] stat("/usr/local/mysql/etc/my.cnf", 0x7fff4038abf0) = -1 ENOENT (No such file or directory)
    [pid 7365] stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 7371] stat("/usr/mysql/5.6.14/data/my.cnf", 0x7fff0d4bc2b0) = -1 ENOENT (No such file or directory)
    [pid 7371] stat("/usr/mysql/5.6.14/my.cnf", 0x7fff0d4bc2c0) = -1 ENOENT (No such file or directory)
    [pid 7371] stat("/usr/mysql/5.6.14/data/my.cnf", 0x7fff0d4bc320) = -1 ENOENT (No such file or directory)
    [pid 7407] stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    [pid 7407] stat("/etc/mysql/my.cnf", 0x7fff380ae700) = -1 ENOENT (No such file or directory)
    [pid 7407] stat("/usr/local/mysql/etc/my.cnf", 0x7fff380ae700) = -1 ENOENT (No such file or directory)
    [pid 7407] stat("/usr/mysql/5.6.14/my.cnf", 0x7fff380ae700) = -1 ENOENT (No such file or directory)
    [pid 7407] stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 7364] stat("/usr/mysql/5.5.34/data/my.cnf", 0x7fffb0b1a980) = -1 ENOENT (No such file or directory)
    [pid 7364] stat("/usr/mysql/5.5.34/my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 7364] stat("/usr/mysql/5.5.34/data/my.cnf", 0x7fffb0b1a990) = -1 ENOENT (No such file or directory)
    [pid 7364] stat("/usr/mysql/5.5.34/data/my.cnf", 0x7fffb0b1a9f0) = -1 ENOENT (No such file or directory)
    [pid 7421] stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    [pid 7421] stat("/etc/mysql/my.cnf", 0x7fffde0a77e0) = -1 ENOENT (No such file or directory)
    [pid 7421] stat("/usr/local/mysql/etc/my.cnf", 0x7fffde0a77e0) = -1 ENOENT (No such file or directory)
    [pid 7421] stat("/usr/mysql/5.5.34/my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 7421] stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 8339] stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    [pid 8339] stat("/etc/mysql/my.cnf", 0x7fff59e821d0) = -1 ENOENT (No such file or directory)
    [pid 8339] stat("/usr/local/mysql/etc/my.cnf", 0x7fff59e821d0) = -1 ENOENT (No such file or directory)
    [pid 8339] stat("/usr/mysql/5.6.14/my.cnf", 0x7fff59e821d0) = -1 ENOENT (No such file or directory)
    [pid 8339] stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 8416] stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=3584, ...}) = 0
    [pid 8416] stat("/etc/mysql/my.cnf", 0x7fffc5f4d310) = -1 ENOENT (No such file or directory)
    [pid 8416] stat("/usr/local/mysql/etc/my.cnf", 0x7fffc5f4d310) = -1 ENOENT (No such file or directory)
    [pid 8416] stat("/usr/mysql/5.5.34/my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
    [pid 8416] stat("/root/.my.cnf", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0


    実際どれを開いたかだけでよければ、"-e stat"はイラナイ。
    小ネタ、小ネタ。

    5.1.72のmysql_upgrade --skip-write-binlogでスクリプトが転けるバグ

    $
    0
    0
    昨日、MySQL5.0.83から5.1.72へのバージョンアップをしてたんですが、バイナリー入れ替えてmysql_upgradeかけても何か様子がおかしい。
    前に5.0.83に上げた時にmysql_upgrade忘れてたんじゃないかと疑ってたんですが、実際5.1.72のmysql_upgradeのバグっぽいです。ごめんとなりのひと。

    http://dev.mysql.com/doc/refman/5.1/en/mysql-upgrade.html


    mysql_upgradeに--skip-write-binlogまたは--write-binlogのオプションを引き渡すと、実行されるべき`mysql_fix_privilege_tables'のクエリーが全て転けてアップグレードできない、というもの。
    取り敢えず5.1.72のmysql_upgradeで確認しましたが、どこで混入したのかは謎。


    $ gdb --args mysql_upgrade --socket=/xxx/mysql.sock -uroot -p'xxx' --skip-write-binlog

    brake mysql_check.c:770 and go on.

    770 run_query(mysql_fix_privilege_tables,
    771 &ds_result, /* Collect result */
    772 TRUE);

    (gdb) bt full
    #0 run_tool (tool_path=, ds_res=0x7fffffffe7f0) at mysql_upgrade.c:372
    ret =
    arg =
    args = {{gp_offset = 48, fp_offset = 0, overflow_arg_area = 0x7fffffffe170, reg_save_area = 0x7fffffffe060}}
    ds_cmdline = {
    str = 0x610650 "'/home/yoku0825/mysql-5.1.72/client/mysql''--no-defaults''--socket=/xxx/mysql.sock''--user=root''--password=xxx''--write-binlog''--user=root''--database=mysql''--batch''--"..., length = 240,
    max_length = 512, alloc_increment = 512}
    #1 0x00000000004019bf in run_query (ba
    query=0x4028b0 "-- Copyright (c) 2007, 2008 MySQL AB, 2009 Sun Microsystems, Inc.\n -- Use is subject to license terms.\n -- \n -- This program is free software; you can redistribute it and/or modify\n -- it under the te"...,
    ds_res=0x7fffffffe7f0, force=1 '\001') at mysql_upgrade.c:497
    ret =
    fd = 7
    query_file_path = "/tmp/sqlQG2xcV", '\000'"\220, \353\200\313>\000\000\000\200\027\365\312>", '\000', "\b\000\000\000\060\000\000\000\300\343\377\377\377\177\000\000\000\343\377\377\377\177", '\000', "#\245\306\312>", '\000'"\200, \027\365\312>\000\000\000(\000\000\000\000\000\000\000\n\000\000\000\000\000\000\000\000\000\253\252\252*\000\000\066\244\306\312>\000\000\000\000\002\000\000\000\000\000\000\200\027\365\312>\000\000\000\200\027\365\312>\000\000\000\n\000\000\000\000\000\000\000\360\347\377\377\377\177\000\000\264\253\306\312>\000\000\000\200\027\365\312>\000\000\000\224\342@\000\000\000\000\000\200\027\365\312>\000\000\000\020\374\305\312>\000\000\000\224\342@\000\000\000\000\000\032\025@\000\000\000\000\000\b\000\000\000\060\000\000\000\300\343\377\377\377\177\000\000\000\343\377\377\377\177\000\000\020\000a\000\000\000\000\000\331\342@\000\000\000\000\000"...
    sql_log_bin = "SET SQL_LOG_BIN=0;"
    #2 0x000000000040211c in run_sql_fix_privilege_tables (argc=0, argv=0x6102f8) at mysql_upgrade.c:770
    found_real_errors = 0
    ds_result = {str = 0x610420 "/home/yoku0825/mysql-5.1.72/client/.libs/lt-mysql: unknown option '--write-binlog'\n",
    length = 82, max_length = 512, alloc_increment = 512}
    #3 main (argc=0, argv=0x6102f8) at mysql_upgrade.c:872
    self_name = "/home/yoku0825/mysql-5.1.72/client/.libs/mysql_upgrade", '\000'
    (gdb)
    (gdb) p *ds_res
    $1 = {str = 0x610420 "/home/yoku0825/mysql-5.1.72/client/.libs/lt-mysql: unknown option '--write-binlog'\n", length = 82,
    max_length = 512, alloc_increment = 512}


    mysql_upgradeがテンポラリーファイルにアップグレード用のステートメントを書き出し(この時、--skip-write-binlogならファイルの先頭に"SET SQL_LOG_BIN= 0;"を書き込む)、mysqlコマンドラインクライアントを呼んでそのファイルを食わせる、という流れで実際のALTERが行われる手筈になっているのだけれど、その時にmysql_upgradeが--skip-write-binlog, --write-binlogオプションをmysqlコマンドラインクライアントに渡してしまうのが問題で、unknown optionって怒られる羽目に遭う。

    mysql_upgrade --skip-write-binlogの時はmysql .. --write-binlog .. --skip-write-binlogの両方を渡して、後から渡した--skipの方でオーバーライドさせてる、mysql_upgrade --write-binlogの時はmysql .. --write-binlog ..と渡してる。
    どっちも指定されていない時はどちらのオプションもmysqlに渡さず、5.1では暗黙のデフォルトとして--write-binlog扱いになり、"SET SQL_LOG_BIN= 0;"は書き込まれない。

    --write-binlogオプションを押し込むところを--loose-write-binlogにして、mysql_upgradeに渡すのを--loose-skip-write-binlogにすれば何とかなるかな、と思いつつパッチでも書こうかしら。でも電車動き始めたらしいのでそろそろ出勤するかな。

    ちなみにこれ、MySQL Bugsにも上げたんですが、backtraceの中にパスワードとか残したままにしてしまって、しかもBugsは後から投稿取り消せないのでモニョモニョな状態になってコメントで泣きついたところ、Access deniedにしてもらいました。

    「Oracle様にとってはゴミ屑のようなコミュニティエディションの1ユーザーの懇願など無視されるかな」と思ってたんですが、数時間でさっくり対応してくれて感謝感謝です。
    正直ちょっと(だけ)見直した。

    MySQL Bugs #70624 mysql_upgrade with --skip-write-binlog doesn't work
    http://bugs.mysql.com/bug.php?id=70624


    【2013/10/16 16:05】
    パッチ書きました。

    https://gist.github.com/yoku0825/7003161

    【2013/10/24 12:29】
    本家でもVerifyされたので、次のリリースでは直るといいなー。
    Viewing all 581 articles
    Browse latest View live