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

MySQL Casual Talks vol.5 でバーボンハウスしてきた

$
0
0
やあ(´・ω・`)
ようこそ、バーボンハウスへ。



というわけで、MySQL Casual Talks vol.5に参加してきました。
ハッシュタグもちょこちょこ拾ってあるので、ブログ書いた方などは追加していただけると幸いです。

今回は前の週のOSC 2013 Tokyo/Fallで死んでたのでさっくり愚痴って15分! とか思ってたんですが、前々日に30ページまで書いて5分しか保たずにあわてて追加していったらなんか60ページくらいになってしまったという。それでもやっぱり10分くらいでやったみたいですが、一発ネタなのでそれくらいで終わって良かったですね。

内容については概ね`本当にあった怖くない話'なんですが、1つのテーブルじゃなくて2つ3つのテーブルのイケてないところを寄せ集めてデフォルメした感じです。

あとはvarbinary型が色々あると思いますが、utf8mb4で40文字入れるだけで161bytes対41bytesになって、BIGINTだDATETIMEだTINYINTだ言ってるのが馬鹿馬鹿しいくらいの差になるので、容量がタイトな時はvarbinary型にしています。スッカスカな時は別にjsonをTEXT型に突っ込んでも(それほど)文句は言いません。

あと、カラム名をインデックス名に流用する時は 必ず カラムと全く同じ名前のインデックスにならないように、ALTER TABLE .. DROP KEY ..のKEYキーワードを忘れてもカラムがドロップされたりしないように 気をつけて下さい。

じゃあ、バリウム飲んできます(健康診査

MySQLインスタンス間でテーブルを移行する投げ遣りベンチマーク

$
0
0
よく考えるとレアケースなんですが今月末に作業が控えているのでメモ。
データベース名はdb1, 移行対象のテーブル(2つ)はtable1, table2とする。移行元ではMyISAMエンジンを使っていて、移行先ではInnoDBエンジンに変える。


シンプルなパターン。何も考えずにmysqldumpでばっこ抜く。

$ time mysqldump -S /xxx/mysql.sock -uroot -pxxx --default-character-set=binary --single-transaction db1 table1 table2 > mysqldump.sql

real 0m30.573s
user 0m27.051s
sys 0m3.078s

$ ll -h
合計 2.0G
-rw-rw-r-- 1 mysql mysql 2.0G 11月 6 13:38 2013 mysqldump.sql

合計2GB。scpで転送して20秒ちょっと。コイツをリストア先に流し込む。
まずはMyISAMでリストアしてからInnoDBに変換。

$ time mysql -S /xxx/mysql.sock -uroot -pxxx --default-character-set=binary new_db1 < mysqldump.sql

real 7m53.261s
user 0m57.426s
sys 0m1.956s

mysql> ALTER TABLE table1 Engine= InnoDB;
Query OK, 11730918 rows affected (6 min 14.90 sec)
Records: 11730918 Duplicates: 0 Warnings: 0

mysql> ALTER TABLE table2 Engine= InnoDB;
Query OK, 16171279 rows affected (15 min 44.07 sec)
Records: 16171279 Duplicates: 0 Warnings: 0


合計30分弱。やっぱりmysqldumpはリストアが遅い…。
吸い上げたmysqldumpをs/ENGINE=MyISAM/ENGINE=InnoDB/してみる。

$ time sed -i 's/ENGINE=MyISAM/ENGINE=InnoDB/' mysqldump.sql

real 0m25.722s
user 0m3.923s
sys 0m2.994s

$ time mysql -S /xxx/mysql.sock -uroot -pxxx --default-character-set=binary new_db1 < mysqldump.sql

real 23m54.496s
user 0m46.110s
sys 0m2.204s
25分弱。多少速くなった。ALTER TABLEで変換よりはマシ。 気を取り直してmysqldumpを--tabにしたらどうだろうか。
$ time mysqldump -S /xxx/mysql.sock -uroot -pxxx --default-characster-set=binary --single-transaction --tab=./xxx --fields-terminated-by=',' --fields-enclosed-by='"' --fields-escaped-by='\\' --lines-terminated-by='\n' db1 table1 table2
real 0m35.618s
user 0m0.002s
sys 0m0.004s

$ ll -h
合計 1.9G
-rw-rw-r-- 1 mysql mysql 2.0K 11月 6 13:34 2013 table1.sql
-rw-rw-r-- 1 mysql mysql 728M 11月 6 13:34 2013 table1.txt
-rw-rw-r-- 1 mysql mysql 2.1K 11月 6 13:34 2013 table2.sql
-rw-rw-r-- 1 mysql mysql 1.2G 11月 6 13:34 2013 table2.txt
MyISAMのまま入れてALTER TABLEはもういい気がするので、InnoDBにしておいてLOAD DATA INFILEで流し込むパターン。
$ time cat table1.sql table2.sql | sed 's/ENGINE=MyISAM/ENGINE=InnoDB/' | mysql -S /xxx/mysql.sock -uroot -pxxx --default-character-set=binary new_db1

real 0m0.017s
user 0m0.005s
sys 0m0.003s

mysql> LOAD DATA INFILE '/tmp/table1.txt' INTO TABLE table1 FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\' LINES TERMINATED BY '\n';
Query OK, 11730887 rows affected (6 min 32.29 sec)
Records: 11730887 Deleted: 0 Skipped: 0 Warnings: 0

mysql> LOAD DATA INFILE '/tmp/table2.txt' INTO TABLE table2 FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\' LINES TERMINATED BY '\n';
Query OK, 16171279 rows affected (16 min 28.26 sec)
Records: 16171279 Deleted: 0 Skipped: 0 Warnings: 0

25分弱。バルクインサートより速いつもりでいたけどそんなに変わらなかった。。

ここから本命。
SH2さんの電波を受信してINDEXをゴニョゴニョしながらインポートする。
  ⇒ MySQL 5.6における大量データロード時の考慮点 - SH2の日記

$ time cat table1.sql table2.sql | mysql -S /xxx/mysql.sock -uroot -pxxx --default-character-set=binary new_db1

real 0m0.017s
user 0m0.005s
sys 0m0.003s

mysql> ALTER TABLE table1 Engine= InnoDB, DEFAULT CHARACTER SET= utf8, DROP KEY xxx, DROP KEY xxx, DROP KEY xxx, AUTO_INCREMENT= 1;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> ALTER TABLE table2 Engine= InnoDB, DEFAULT CHARACTER SET= utf8, DROP KEY xxx, DROP KEY xxx, DROP KEY xxx, DROP KEY xxx;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> LOAD DATA INFILE '/tmp/table1.txt' INTO TABLE table1 FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\' LINES TERMINATED BY '\n';
Query OK, 11730887 rows affected (2 min 10.14 sec)
Records: 11730887 Deleted: 0 Skipped: 0 Warnings: 0

mysql> LOAD DATA INFILE '/tmp/table2.txt' INTO TABLE table2 FIELDS TERMINATED BY ',' ENCLOSED BY '"' ESCAPED BY '\\' LINES TERMINATED BY '\n';
Query OK, 16171279 rows affected (4 min 52.82 sec)
Records: 16171279 Deleted: 0 Skipped: 0 Warnings: 0

mysql> ALTER TABLE table1 ADD KEY xxx (..), ADD KEY xxx (..), ADD KEY xxx (..);
Query OK, 0 rows affected (2 min 49.87 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> ALTER TABLE table2 ADD KEY xxx (..), ADD KEY xxx (..), ADD KEY xxx (..), ADD KEY xxx (..);
Query OK, 0 rows affected (4 min 35.72 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysqldumpを入れ込んで、インデックスを外して、LOAD DATA INFILEしてからインデックスを戻す。
合計15分もかからない。すごいはやいー。
table1でLOAD DATA INFILE, ALTER TABLE, table2でLOAD DATA INFILE, ALTER TABLEの順番にすると、バッファプールの効率がよくなって多少速くなるかもしれない。

今回本番で使うのは↑の手順になるけど、試した中で最速だったのは、
・テキトーに5.6の移動元サーバーのレプリケーションスレーブを作って
・そっちでInnoDB化しておいて
・移動先サーバーも5.6にして
・FLUSH TABLES .. FOR EXPORT
でした。

移動元サーバーで、
mysql> FLUSH TABLES table1, table2 FOR EXPORT;
Query OK, 0 rows affected (0.03 sec)

$ ll -h
合計 4.8G
-rw-rw---- 1 yoku0825 yoku0825 65 11月 6 15:45 2013 db.opt
-rw-rw---- 1 yoku0825 yoku0825 1.3K 11月 6 16:24 2013 table1.cfg
-rw-rw---- 1 yoku0825 yoku0825 8.7K 11月 6 16:14 2013 table1.frm
-rw-rw---- 1 yoku0825 yoku0825 1.5G 11月 6 16:17 2013 table1.ibd
-rw-rw---- 1 yoku0825 yoku0825 1.7K 11月 6 16:24 2013 table2.cfg
-rw-rw---- 1 yoku0825 yoku0825 8.8K 11月 6 16:17 2013 table2.frm
-rw-rw---- 1 yoku0825 yoku0825 3.3G 11月 6 16:21 2013 table2.ibd

.cfgファイルと.ibdファイルをインポート先にコピー(FLUSH TABLES .. FOR EXPORTしたクライアントを閉じちゃうと.cfgファイルが消えてしまうので、クライアントセッションは開いたまま別ターミナルを上げる感じでやる)

ファイルをscpで転送して、移動先のサーバーで
mysql> CREATE TABLE table1 (..) Engine= InnoDB;
Query OK, 0 rows affected (0.35 sec)

mysql> CREATE TABLE table2 (..) Engine= InnoDB;
Query OK, 0 rows affected (0.24 sec)

mysql> ALTER TABLE table1 DISCARD TABLESPACE;
Query OK, 0 rows affected (0.66 sec)

mysql> ALTER TABLE table2 DISCARD TABLESPACE;
Query OK, 0 rows affected (0.03 sec)

$ sudo mv table[12].* /xxx/new_db1

mysql> ALTER TABLE table1 IMPORT TABLESPACE;
Query OK, 0 rows affected (28.00 sec)

mysql> ALTER TABLE table2 IMPORT TABLESPACE;
Query OK, 0 rows affected (1 min 2.25 sec)
流石に速い。

知らないと手順がごちゃごちゃする&使いどころが限られてるけど、上手く使えば5GB/5分くらいで移動できちゃう、という風情。
xtrabackupあたりがバックアップをこの形式で出力できるようにならないかな。。

MySQL 5.6のクラッシュセーフなレプリケーションの仕組み

$
0
0
ちょこちょこ思うこと。

MySQL 5.6のnutshellを読むと、`クラッシュセーフなレプリケーションを実現するためにはmaster_info_repositoryとrelay_log_info_repositoryをTABLEに設定しな! おっと、relay_log_recoveryも1にしておくんだぜ'みたいことが書いてある。

で、このバグレポートを見た時からずっと、sync_master_infoの暗黙のデフォルト10000のまんまじゃクラッシュセーフじゃなくね? sync_master_info= 1必須? それはパフォーマンス的に死ねる。という感じでモヤモヤしていたのだが、やっと合点がいったのでメモしときます。

まず、出てくるパラメータの説明。
  • master_info_repository
    • FILEの場合、従来どおりmaster.infoファイルに書く。
      • ファイル名はmaster_info_fileにより可変。
      • 更新があるたび(バイナリーログを受信するたび)にファイルにwrite、sync_master_info回イベントを受信するとfdatasync。5.5までの暗黙のデフォルトは0なのでfsyncはしない。5.6.6以降の暗黙のデフォルトは10000。
    • TABLEの場合、mysql.slave_master_infoテーブルに書く。
      • コイツはmysqlスキーマ初のInnoDBテーブル。
      • sync_master_info回イベントを受信するたびにUPDATE。
      • sync_master_info= 0にすると、mysql.slave_master_infoは *アップデートされない*
        • SHOW SLAVE STATUSはこのテーブルの情報を読んでいる訳ではないので、ちゃんと更新される。
  • relay_log_info_repository
    • master_info_repositoryのrelay-log.info版っぽいが、TABLEを指定した場合は少し振る舞いが異なる(俺的混乱のもとその1)
    • FILEの場合はいっしょなので割愛。
    • TABLEの場合、mysql.slave_relay_log_infoテーブルに書く。
      • SQLスレッドがリレーログから読み出したステートメントを実行する時に、同じトランザクションの中でmysql.slave_relay_log_infoを更新するようになる。
      • sync_relay_log_infoの値に依存せず、毎回テーブルは更新される。
  • relay_log_recovery
    • 実は5.5からあるオプション
    • 起動した時に、持っているリレーログとrelay-log.infoを比較して、まだ適用していない(Exec_Master_Log_Pos以降の)リレーログを破棄する。
という訳で、relay_log_recovery= ONであれば、「Exec_Master_Log_Posの情報が正しくrelay-log.info(or mysql.slave_relay_log)に反映されている限り、バイナリーログの二重取りや欠損は発生しない(はず)」、かつ、relay_log_info_repository= TABLEであれば、「SQLスレッドが変更を反映するタイミングでmysql.slave_relay_log_infoも同じトランザクションで更新されるため、All or Nothingで片方だけが失われる(不整合が起こる)ことはない(はず)」になり、

relay_log_recovery && relay_log_info_repository= TABLEならクラッシュセーフになる、という理屈らしい。

………master_info_repository関係なくない? 関係ないよね?

全文検索エンジンGroongaを囲む夕べ(以下、ぐるんべ)4に参加して感じたことなど

$
0
0
このエントリーは 全文検索エンジンGroonga Advent Calendar 2013 の3日目です :)

さて、今年もいい肉の日(11/29)に ぐるんべ4が開催されたので今年も行ってきました。去年のぐるんべ3に続き、2年連続2回目の参加です(去年は発表とかしませんでしたが)

少し遅れ馳せながら感想エントリーです。
当日のまとめは おんがえしさんブログのおんがえし Groongaを囲む夕べ4をサクッと振り返るなどにリンクを張りつつ誤魔化します。


さて、今年のぐるんべで受けた印象は *かなりMySQLに寄ってきたな* というイメージでした。かなり偏見ですが。

すとーさん が`*roonga使ってる人ー?'と振った感じでも、去年よりMroonga使いは多そう。それに対してRroonga使いの参加が少なかったイメージです。

去年のぐるんべ3のアジェンダと比較していただくと判るのですが(すいません、具体的な数はタイトル見ても思い出せませんでした。。)去年は半分くらいがRroongaネタ、残りがGroongaとMroonga(2本?)で、どちらかというと比較的少数派だったはず。え? Nroo…ごめん誰か来たみたい

ところが今年は 林さんのセッションを除けば事例紹介は半分(4本)がMroonga関連、Groongaが3本にRroongaが1本といううーん、これは我ながらバイアスのかかった見方だとは思うんですが、ボリューム的にだいぶ増えた気がするんですよ。気がするんです。

その他にも池田さんから「Mroongaサポートの紹介」があって、対応する(?)南さんのセッションはプロダクトに関わらず*roonga族全般'に関するもの、斯波さんからのMroonga今年の収穫は去年と変わらないペースだったので、やっぱりMroongaの話題が出ていた時間は長かったように思われます。
去年の 森さんのセッション では割と`全文検索もできる *KVS*'っぽく紹介されていた感じがしましたが、今年のセッションではそれに触れてはいたものの分量は少なくなった感じ(今年はGroonga本体でなくてDroongaでしたしね)

別に良い訳でも悪い訳でもなく、なんとなーく、そんなことを感じました。来年はどうなるでしょうね。来年こそNroo…うん何でもない。


そういえば去年のぐるんべで斯波さんに「ねえねえMariaDBマダー?」とねだった事とか色々思い出してきました。懐かしいですが、長くなりそうなのでこのへんで。

( ´-`).oO(ところで、httpしゃべれるようにするとかNode.jsから叩けるようにするのは最近の流行りなんですかね。NDBCLUSTERから学ぶところとしては、memcachedプロトコルなら使う人いるかもって感じですが


明日は KitaitiMakotoさんです!

あなたのMySQL 5.6トレンド力をチェックする15の質問

$
0
0
このエントリーは MySQL Casual Advent Calendar 2013 参加記事です。カジュアルカジュアル。
MySQL 5.6のGAリリースからはや10ヶ月、みなさんそろそろカジュアルに導入なされていることだと思います。

漢(オトコ)のコンピュータ道: 優れたMySQL DBAを見分ける27+3の質問のオマージュです。
Islands in the byte stream: 「優れたPerlプログラマを見分ける27の質問」の日本語訳@__gfx__さんからメンションをいただいたので作りました :)

がんばってみます :)
独断と偏見で有名そうなの並べてあるだけですので、他にも色々ありますよ探してみましょう :)

  1. mysql_install_dbに加わった新機能2つ。
  • mysqlコマンドラインクライアントは~/.mysql_historyにコマンド履歴を格納するが、5.6から--histignoreオプションで指定した文字列と'ある文字列'を含む行は格納されなくなった(上矢印とか押しても履歴として出てこない) この除外される文字列2つ。
    • ユーザーを量産する時にちょっと不便(´・ω・`)
    • こたえ=> "PASSWORDとIDENTIFIED"
    • なお、これ以外の文字列を--histignoreで指定しても上書きはされず、追加されるだけなので無効化するにはソースコードをいじる必要がある。
  • InnoDBに追加された新機能を5つ以上
  • 予めWHERE句以外でパーティションを指定してSELECT, UPDATE, DELETEその他が可能になったが、そのシンタックス。
  • 5.6になって *非推奨* になった機能2つ以上
  • 待望のスレーブ自動昇格スクリプト mysqlfailoverを動作させるための条件
    • こたえ=> GTIDが有効になっていること
  • クラッシュセーフスレーブに必要なパラメータ2つ
  • mysqlbinlogをバイナリーログのリアルタイムバックアップとして使う時のオプション2つ
  • レプリケーション関連で追加された新機能3つ
  • 5.6になってオプティマイザーは賢くなったか? [Y/N]
  • 不正Connectのカウントアップがついにテーブルで参照できるように! スキーマとテーブルは?
  • 5.6未満から5.6への移行はmysqldump推奨。なんで?
  • mysql_config_editorで作った.mylogin.cnfの最大の弱点は?
  • 5.6で追加された新しいEXPLAIN2種類
  • Optimizer Traceの出力させ方

    1. さて、みなさん如何でしたか?
      答えはほとんどナッツシェルからたどればいけます。ここにピックアップしなかったものも色々ありますので、「へーこんなの追加されてたんだー」とか思っていただければ幸いです。



      明日は bluerabbit777jpさんです!

      wsrep_sst_authでハマったメモ

      $
      0
      0
      Percona XtraDB Clusterでクラスターの初期化中に、

      wsrep_sst_method= xtrabackup
      wsrep_sst_auth= username:password
      としてても、JOINER側で

      131212 17:22:05 [Note] WSREP: State transfer required:
      Group state: 64863b36-62f7-11e3-bef2-a746941be182:1
      Local state: 00000000-0000-0000-0000-000000000000:-1
      131212 17:22:05 [Note] WSREP: New cluster view: global state: 64863b36-62f7-11e3-bef2-a746941be182:1, view# 10: Primary, numbe
      r of nodes: 2, my index: 1, protocol version 2
      131212 17:22:05 [Warning] WSREP: Gap in state sequence. Need state transfer.
      131212 17:22:07 [Note] WSREP: Running: 'wsrep_sst_xtrabackup --role 'joiner' --address '192.168.xxx.xxx' --auth '' --datadir '/xxx' --defaults-file '/xxx/my.cnf' --parent '3382''
      WSREP_SST: [INFO] Streaming with tar (20131212 17:22:08.058)
      WSREP_SST: [INFO] Using socat as streamer (20131212 17:22:08.063)
      WSREP_SST: [INFO] Evaluating socat -u TCP-LISTEN:4444,reuseaddr stdio | tar xfi - --recursive-unlink -h; RC=( ${PIPESTATUS[@]}
      ) (20131212 17:22:08.092)
      131212 17:22:08 [Note] WSREP: Prepared SST request: xtrabackup|192.168.xxx.xxx:4444/xtrabackup_sst
      131212 17:22:08 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
      131212 17:22:08 [Note] WSREP: Assign initial position for certification: 1, protocol version: 2
      131212 17:22:08 [Warning] WSREP: Failed to prepare for incremental state transfer: Local state UUID (00000000-0000-0000-0000-000000000000) does not match group state UUID (64863b36-62f7-11e3-bef2-a746941be182): 1 (Operation not permitted)
      at galera/src/replicator_str.cpp:prepare_for_IST():445. IST will be unavailable.
      131212 17:22:08 [Note] WSREP: Node 1 (xxx) requested state transfer from '*any*'. Selected 0 (xxx)(SYNCED) as donor.
      131212 17:22:08 [Note] WSREP: Shifting PRIMARY -> JOINER (TO: 1)
      131212 17:22:08 [Note] WSREP: Requesting state transfer: success, donor: 0
      tar: これは tar アーカイブではないようです
      tar: 処理中にエラーが起きましたが、最後まで処理してからエラー終了させました
      WSREP_SST: [ERROR] Error while getting data from donor node: exit codes: 0 2 (20131212 17:22:09.063)
      WSREP_SST: [ERROR] Cleanup after exit with status:32 (20131212 17:22:09.068)
      131212 17:22:09 [Warning] WSREP: 0 (xxx): State transfer to 1 (xxx) failed: -1 (Operation not permitted)
      131212 17:22:09 [ERROR] WSREP: gcs/src/gcs_group.c:gcs_group_handle_join_msg():719: Will never receive state. Need to abort.

      SSTに失敗したログが出てて、DONOR側でも、

      131214 16:12:12 [Note] WSREP: Shifting SYNCED -> DONOR/DESYNCED (TO: 4)
      131214 16:12:12 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
      131214 16:12:12 [Note] WSREP: Running: 'wsrep_sst_xtrabackup --role 'donor' --address '192.168.xxx.xxx:4444/xtrabackup_sst' --au
      th '(null)' --socket '/xxx/mysql.sock' --datadir '/xxx/' --defaults-file '/xxx/my.cnf'
      --gtid '64863b36-62f7-11e3-bef2-a746941be182:4''
      131214 16:12:12 [Note] WSREP: sst_donor_thread signaled with 0
      WSREP_SST: [INFO] Streaming with tar (20131214 16:12:13.133)
      WSREP_SST: [INFO] Using socat as streamer (20131214 16:12:13.138)
      WSREP_SST: [INFO] Streaming the backup to joiner at 192.168.xxx.xxx 4444 (20131214 16:12:13.154)
      WSREP_SST: [INFO] Evaluating innobackupex --defaults-file=/xxx/my.cnf $INNOEXTRA --galera-info --stream=$sfmt ${T
      MPDIR} 2>${DATA}/innobackup.backup.log | socat -u stdio TCP:192.168.xxx.xxx:4444; RC=( ${PIPESTATUS[@]} ) (20131214 16:12:13.159
      )
      WSREP_SST: [ERROR] innobackupex finished with error: 1. Check /xxx//innobackup.backup.log (20131214 16:12:13.569
      )
      WSREP_SST: [ERROR] Cleanup after exit with status:22 (20131214 16:12:13.574)
      131214 16:12:13 [ERROR] WSREP: Failed to read from: wsrep_sst_xtrabackup --role 'donor' --address '192.168.xxx.xxx:4444/xtraback
      up_sst' --auth '(null)' --socket '/xxx/mysql.sock' --datadir '/xxx/' --defaults-file '/xxx/my.cnf' --gtid '64863b36-62f7-11e3-bef2-a746941be182:4'
      131214 16:12:13 [ERROR] WSREP: Process completed with error: wsrep_sst_xtrabackup --role 'donor' --address '192.168.xxx.xxx:4444/xtrabackup_sst' --auth '(null)' --socket '/xxx/mysql.sock' --datadir '/xxx/' --defaults-file '/xxx/my.cnf' --gtid '64863b36-62f7-11e3-bef2-a746941be182:4': 22 (Invalid argument)
      131214 16:12:13 [Warning] WSREP: 1 (xxx): State transfer to 0 (xxx) failed: -1 (Operation not permitted)

      innobackupexが失敗したログが出る。

      innobackupexのログを見てみると、

      131214 16:47:12  innobackupex: Connecting to MySQL server with DSN 'dbi:mysql:;mysql_read_default_file=/xxx/my.cnf;mysql_read_default_group=xtrabackup' (using password: NO).
      innobackupex: Error: Failed to connect to MySQL server: DBI connect(';mysql_read_default_file=/xxx/my.cnf;mysql_read_default_group=xtrabackup','',...) failed: Access denied for user 'mysql'@'localhost' (using password: NO) at /usr/local/bin/innobackupex line 2954

      うーん、wsrep_sst_authでユーザー名とパスワードを指定してるのに、まるっと無視されている様子。取り敢えず、my.cnfの[xtrabackup]セクションに
      [xtrabackup]
      user= username
      password= password
      を追加して通るようにはなったけど、これバグなのか調べ方が悪いのかよく判らん。。


      上手く行ってからメモ用にわざと失敗させてログを吐かせてるので、時間がバラバラなのはご愛嬌。


      【2013/12/16 18:00】
      Indeed, this is not a bug.

      wsrep_sst_auth=username:password

      でいけた。スペース入れるとダメぽい。

      もしMroongaのWプラグマがなかったら

      $
      0
      0
      このエントリーは 全文検索エンジンGroonga Advent Calendar 2013の17日目です。

      2013/05/29リリースの Mroonga 3.04 の新機能として、TritonnにあったWプラグマのバックポート(?)があります。重み付け検索というより、マルチセクションインデックスの一部を使って検索するために使うことが多いような気もします。

      mysql> SHOW CREATE TABLE t1\G
      *************************** 1. row ***************************
      Table: t1
      Create Table: CREATE TABLE `t1` (
      `num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `val1` varchar(20) DEFAULT NULL,
      `val2` varchar(20) DEFAULT NULL,
      `val3` varchar(20) DEFAULT NULL,
      UNIQUE KEY `num` (`num`),
      FULLTEXT KEY `val1` (`val1`,`val2`,`val3`)
      ) ENGINE=mroonga AUTO_INCREMENT=100002 DEFAULT CHARSET=utf8
      1 row in set (0.00 sec)

      mysql> SELECT * FROM t1 WHERE MATCH(val1) AGAINST('デコ助' IN BOOLEAN MODE) ORDER BY num LIMIT 3;
      ERROR 1191 (HY000): Can't find FULLTEXT index matching the column list

      mysql> SELECT * FROM t1 WHERE MATCH(val1, val2, val3) AGAINST('+デコ助' IN BOOLEAN MODE) ORDER BY num LIMIT 3;
      +-----+---------------------------------------+--------------------------------------+---------------------------------------------+
      | num | val1 | val2 | val3 |
      +-----+---------------------------------------+--------------------------------------+---------------------------------------------+
      | 5 | アイドルマスター | アウトブレイク奇妙な冒険 | 人類はデコ助野郎! |
      | 9 | 13日のデコ助野郎! | 恋と選挙と恋がしたい! | ジョジョのUC[ユニコーン] |
      | 14 | アウトブレイクデコ助野郎! | 魔法少女カンパニー | コードギアス亡国の恋がしたい! |
      +-----+---------------------------------------+--------------------------------------+---------------------------------------------+
      3 rows in set (0.01 sec)

      mysql> SELECT * FROM t1 WHERE MATCH(val1, val2, val3) AGAINST('*W0:1,1:0,2:0 +デコ助' IN BOOLEAN MODE) ORDER BY num LIMIT 3;
      +-----+---------------------------------------+---------------------------------+---------------------------------------------+
      | num | val1 | val2 | val3 |
      +-----+---------------------------------------+---------------------------------+---------------------------------------------+
      | 9 | 13日のデコ助野郎! | 恋と選挙と恋がしたい! | ジョジョのUC[ユニコーン] |
      | 14 | アウトブレイクデコ助野郎! | 魔法少女カンパニー | コードギアス亡国の恋がしたい! |
      | 115 | インフィニットデコ助野郎! | 僕は友達が金曜日 | 魔法少女@がんばらない |
      +-----+---------------------------------------+---------------------------------+---------------------------------------------+
      3 rows in set (0.02 sec)

      val1からだけ"デコ助"を検索したいのですが、val1だけに張られたフルテキストインデックスは無いので怒られます(1つ目のSELECT)し、かといってMATCH(val1, val2, val3)とするとval1, val2, val3全てから"デコ助"を探してしまいます。Wプラグマがあれば、重み付けでカラム0(=val1)の重みを1に、カラム1(=val2)とカラム2(=val3)の重みを0にしてやることで、val1からだけ検索することができます。

      重みも考慮した検索結果を返したいときはORDER BYを併用して

      mysql> SELECT *, MATCH(val1, val2, val3) AGAINST('+デコ助' IN BOOLEAN MODE) AS score FROM t1 WHERE MATCH(val1, val2, val3) AGA
      INST('+デコ助' IN BOOLEAN MODE) ORDER BY score DESC, num ASC LIMIT 3;
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      | num | val1 | val2 | val3 | score |
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      | 8808 | 13日のデコ助野郎! | さんをつけろよデコ助野郎! | さんをつけろよデコ助野郎! | 3 |
      | 10139 | 進撃のデコ助野郎! | ささみさんデコ助野郎! | インフィニットデコ助野郎! | 3 |
      | 12213 | 人類はデコ助野郎! | 僕は友達がデコ助野郎! | アイドルデコ助野郎! | 3 |
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      3 rows in set (0.02 sec)

      mysql> SELECT *, MATCH(val1, val2, val3) AGAINST('*W0:2,1:1,2:1 +デコ助' IN BOOLEAN MODE) AS score FROM t1 WHERE MATCH(val1, val2, val3) AGAINST('*W0:2,1:1,2:1 +デコ助' IN BOOLEAN MODE) ORDER BY score DESC, num LIMIT 3;
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      | num | val1 | val2 | val3 | score |
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      | 8808 | 13日のデコ助野郎! | さんをつけろよデコ助野郎! | さんをつけろよデコ助野郎! | 4 |
      | 10139 | 進撃のデコ助野郎! | ささみさんデコ助野郎! | インフィニットデコ助野郎! | 4 |
      | 12213 | 人類はデコ助野郎! | 僕は友達がデコ助野郎! | アイドルデコ助野郎! | 4 |
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      3 rows in set (0.03 sec)

      スコアが大きい順番に並べてみたりできます。スコアだけでORDER BYしている場合、スコアが同じタプルが存在する場合、どんな順番で返すかは保証されません。たぶん.mrnファイルの内部構造に依存するんだと思います。

      全ての重みが均等(Wプラグマの中で重みを指定しなかった場合、全部のカラムが重み1で扱われる)の時のスコアに比べて、val1の重みを2にした時のスコアが上がってます。わかりやすい。

      さて、Wプラグマが無かった昔(といっても、半年ちょっと前のことだし、簡単にMroongaをアップグレードする訳にいかない人たちは今なお)、重み付け検索をするにはどうすれば良かったんだろうという *思考実験*です。

      mysql> SHOW CREATE TABLE t1\G
      *************************** 1. row ***************************
      Table: t1
      Create Table: CREATE TABLE `t1` (
      `num` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `val1` varchar(20) DEFAULT NULL,
      `val2` varchar(20) DEFAULT NULL,
      `val3` varchar(20) DEFAULT NULL,
      UNIQUE KEY `num` (`num`),
      FULLTEXT KEY `val1` (`val1`),
      FULLTEXT KEY `val2` (`val2`),
      FULLTEXT KEY `val3` (`val3`)
      ) ENGINE=mroonga AUTO_INCREMENT=100002 DEFAULT CHARSET=utf8
      1 row in set (0.00 sec)

      mysql> SELECT *, MATCH(val1) AGAINST('+デコ助' IN BOOLEAN MODE)* 2+ MATCH(val2) AGAINST('+デコ助' IN BOOLEAN MODE)+ MATCH(val3) AGAINST('+デコ助' IN BOOLEAN MODE) AS score FROM t1 WHERE MATCH(val1) AGAINST('+デコ助' IN BOOLEAN MODE)* 2+ MATCH(val2) AGAINST('+デコ助' IN BOOLEAN MODE)+ MATCH(val3) AGAINST('+デコ助' IN BOOLEAN MODE) ORDER BY score DESC, num ASC LIMIT 3;
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      | num | val1 | val2 | val3 | score |
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      | 8808 | 13日のデコ助野郎! | さんをつけろよデコ助野郎! | さんをつけろよデコ助野郎! | 4 |
      | 10139 | 進撃のデコ助野郎! | ささみさんデコ助野郎! | インフィニットデコ助野郎! | 4 |
      | 12213 | 人類はデコ助野郎! | 僕は友達がデコ助野郎! | アイドルデコ助野郎! | 4 |
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      3 rows in set (0.33 sec)

      クエリーが見にくい上に重い。。容量効率も悪そうですが、他にぱっと思い付きませんでした。
      プロファイルの結果はこんなかんじ(Warningが出てるのは5.6では@@profileはdeprecatedだよってアレ)

      mysql> SHOW PROFILE FOR QUERY 54;
      +--------------------------------+----------+
      | Status | Duration |
      +--------------------------------+----------+
      | starting | 0.000037 |
      | Waiting for query cache lock | 0.000005 |
      | init | 0.000004 |
      | checking query cache for query | 0.000164 |
      | checking permissions | 0.000011 |
      | Opening tables | 0.000042 |
      | init | 0.000063 |
      | System lock | 0.000043 |
      | Waiting for query cache lock | 0.000004 |
      | System lock | 0.000031 |
      | optimizing | 0.000017 |
      | statistics | 0.000365 |
      | preparing | 0.000027 |
      | FULLTEXT initialization | 0.020383 |
      | Sorting result | 0.000022 |
      | executing | 0.000005 |
      | Sending data | 0.000018 |
      | Creating sort index | 0.302915 |
      | end | 0.000026 |
      | query end | 0.000008 |
      | closing tables | 0.000212 |
      | freeing items | 0.000355 |
      | Waiting for query cache lock | 0.000007 |
      | freeing items | 0.000350 |
      | Waiting for query cache lock | 0.000006 |
      | freeing items | 0.000004 |
      | storing result in query cache | 0.000006 |
      | cleaning up | 0.000034 |
      +--------------------------------+----------+
      28 rows in set, 1 warning (0.01 sec)

      mysql> explain SELECT *, MATCH(val1) AGAINST('+デコ助' IN BOOLEAN MODE)* 2+ MATCH(val2) AGAINST('+デコ助' IN BOOLEAN MODE)+ MATCH(val3) AGAINST('+デコ助' IN BOOLEAN MODE) AS score FROM t1 WHERE MATCH(val1) AGAINST('+デコ助' IN BOOLEAN MODE)* 2+ MATCH(val2) AGAINST('+デコ助' IN BOOLEAN MODE)+ MATCH(val3) AGAINST('+デコ助' IN BOOLEAN MODE) ORDER BY score DESC, num ASC LIMIT 3;
      +----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+
      | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
      +----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+
      | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 100001 | Using where; Using filesort |
      +----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+
      1 row in set (0.00 sec)

      EXPLAINがALLになっちゃった(´・ω・`) ほんとかな。
      こっちがWプラグマでさっくりやった時のプロファイル。

      mysql> show profile;
      +--------------------------------+----------+
      | Status | Duration |
      +--------------------------------+----------+
      | starting | 0.000039 |
      | Waiting for query cache lock | 0.000004 |
      | init | 0.000004 |
      | checking query cache for query | 0.000135 |
      | checking permissions | 0.000011 |
      | Opening tables | 0.000041 |
      | init | 0.000068 |
      | System lock | 0.000020 |
      | Waiting for query cache lock | 0.000003 |
      | System lock | 0.000024 |
      | optimizing | 0.000017 |
      | statistics | 0.000423 |
      | preparing | 0.000028 |
      | FULLTEXT initialization | 0.018467 |
      | Sorting result | 0.000028 |
      | executing | 0.000005 |
      | Sending data | 0.000024 |
      | Creating sort index | 0.000195 |
      | end | 0.000010 |
      | query end | 0.000010 |
      | closing tables | 0.000184 |
      | freeing items | 0.000178 |
      | Waiting for query cache lock | 0.000006 |
      | freeing items | 0.000315 |
      | Waiting for query cache lock | 0.000006 |
      | freeing items | 0.000003 |
      | storing result in query cache | 0.000006 |
      | cleaning up | 0.000037 |
      +--------------------------------+----------+
      28 rows in set, 1 warning (0.00 sec)

      mysql> explain SELECT *, MATCH(val1, val2, val3) AGAINST('*W0:2,1:1,2:1 +デコ助' IN BOOLEAN MODE) AS score FROM t1 WHERE MATCH(val1, val2, val3) AGAINST('*W0:2,1:1,2:1 +デコ助' IN BOOLEAN MODE) ORDER BY score DESC, num ASC LIMIT 3;
      +----+-------------+-------+----------+---------------+------+---------+------+------+---------------------------------------------------+
      | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
      +----+-------------+-------+----------+---------------+------+---------+------+------+---------------------------------------------------+
      | 1 | SIMPLE | t1 | fulltext | val1 | val1 | 0 | NULL | 1 | Using where with pushed condition; Using filesort |
      +----+-------------+-------+----------+---------------+------+---------+------+------+---------------------------------------------------+
      1 row in set (0.00 sec)

      意外なのは、FULLTEXT initやfreeing itemsはそこまで盛大に時間がかかる訳じゃなく、ソートの部分(Creating sort index)でのみ大きく差が出ていること。

      mysql> SELECT *, MATCH(val1, val2, val3) AGAINST('+デコ助' IN BOOLEAN MODE)+ (val1 LIKE '%デコ助%') AS score FROM t1 WHERE MATCH(val1, val2, val3) AGAINST('+デコ助' IN BOOLEAN MODE) ORDER BY score DESC, num ASC LIMIT 3;
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      | num | val1 | val2 | val3 | score |
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      | 8808 | 13日のデコ助野郎! | さんをつけろよデコ助野郎! | さんをつけろよデコ助野郎! | 4 |
      | 10139 | 進撃のデコ助野郎! | ささみさんデコ助野郎! | インフィニットデコ助野郎! | 4 |
      | 12213 | 人類はデコ助野郎! | 僕は友達がデコ助野郎! | アイドルデコ助野郎! | 4 |
      +-------+---------------------------+---------------------------------------+---------------------------------------+-------+
      3 rows in set (0.09 sec)

      mysql> explain SELECT *, MATCH(val1, val2, val3) AGAINST('+デコ助' IN BOOLEAN MODE)+ (val1 LIKE '%デコ助%') AS score FROM t1 WHERE MATCH(val1, val2, val3) AGAINST('+デコ助' IN BOOLEAN MODE) ORDER BY score DESC, num ASC LIMIT 3;
      +----+-------------+-------+----------+---------------+------+---------+------+------+---------------------------------------------------+
      | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
      +----+-------------+-------+----------+---------------+------+---------+------+------+---------------------------------------------------+
      | 1 | SIMPLE | t1 | fulltext | val1 | val1 | 0 | NULL | 1 | Using where with pushed condition; Using filesort |
      +----+-------------+-------+----------+---------------+------+---------+------+------+---------------------------------------------------+
      1 row in set (0.00 sec)

      mysql> SHOW PROFILE FOR QUERY 77;
      +--------------------------------+----------+
      | Status | Duration |
      +--------------------------------+----------+
      | starting | 0.000055 |
      | Waiting for query cache lock | 0.000005 |
      | init | 0.000004 |
      | checking query cache for query | 0.000141 |
      | checking permissions | 0.000011 |
      | Opening tables | 0.000043 |
      | init | 0.000070 |
      | System lock | 0.000019 |
      | Waiting for query cache lock | 0.000003 |
      | System lock | 0.000018 |
      | optimizing | 0.000015 |
      | statistics | 0.000356 |
      | preparing | 0.000025 |
      | FULLTEXT initialization | 0.011795 |
      | Sorting result | 0.000020 |
      | executing | 0.000004 |
      | Sending data | 0.000018 |
      | Creating sort index | 0.075364 |
      | end | 0.000023 |
      | query end | 0.000009 |
      | closing tables | 0.000201 |
      | freeing items | 0.000181 |
      | Waiting for query cache lock | 0.000006 |
      | freeing items | 0.000318 |
      | Waiting for query cache lock | 0.000006 |
      | freeing items | 0.000002 |
      | storing result in query cache | 0.000007 |
      | cleaning up | 0.000033 |
      +--------------------------------+----------+
      28 rows in set, 1 warning (0.00 sec)

      重みをつけるカラムが大したことない数の場合は、(val1 LIKE '%..%')で無理矢理重みをつけてしまう(LIKE句にマッチした場合は1, マッチしない場合は0が戻るので、掛け算なり足し算なりある程度スコアとして使える)
      こっちの方がまだマシそうな速度。。ただしクエリーはいやんな感じだなあ。。

      やっぱりおとなしく最新のMroonga使うべきですね! :)

      ちなみに、テストデータは MySQL Casual Advent Calendar 2013 の13日目、 @meijikさん の テストデータ作成時に便利なストアドファンクション で作りました :)


      明日は @ongaeshiさん の2回目です! :)

      Mroongaのロックが突き刺さるとどうなるか

      $
      0
      0
      むるーん、ガッ(挨拶
      このエントリーは 全文検索エンジンGroonga Advent Calendar 2013の20日目です。

      みなさん、最近、ロック刺さりましたか?
      クラッシュ以外でそうそう刺さることはないと思うんですが、いざ刺さるとどうなるのか…というのは予め考えておいた方が良いと思います(こんなエントリーを書いている時点でお察しください)

      復旧作業については @yoshi_kenさんの Y-Ken Studio: groonga/mroongaの.mrnファイルがlock failedとなった場合の復旧方法 が詳しいですのでそちらで。

      クラッシュして刺さるなら、クラッシュした時点で諸々通知されたりサービスから切り離されているので、まあそんなに困りはしないんですが、*何故かクラッシュせずに刺さってしまった場合* の動きは(当然)かなりシビアなものになります。

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

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

      まあ、こんなテーブルを作るわけです。

      ロックの刺し方(どのプロセスも触っていないのに、mrnファイルがロックされたまま解放されない状態の作り方)は すとーさんにTwitterで教えてもらいました。



      $ /usr/ruby/2.0.0/bin/ruby -e 'require "groonga"; Groonga::Database.open("/usr/local/mysql/data/d1.mrn"); Groonga["t1"].lock; exit'

      と、こう刺してやると、

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

      mysql>
      mysql> DELETE FROM t1 WHERE num= 1;

      SELECTはできるけど、DELETEは返ってこなくなります。
      これは良い。別に良い。想定通りの動作です。
      困る(?)のはここから後。

      mysql> SELECT @@global.lock_wait_timeout;
      +----------------------------+
      | @@global.lock_wait_timeout |
      +----------------------------+
      | 1 |
      +----------------------------+
      1 row in set (0.00 sec)
      mysql> SHOW PROCESSLIST;
      +----+------+-----------+------+---------+------+----------+-----------------------------+
      | Id | User | Host | db | Command | Time | State | Info |
      +----+------+-----------+------+---------+------+----------+-----------------------------+
      | 3 | root | localhost | d1 | Query | 63 | updating | DELETE FROM t1 WHERE num= 1 |
      | 4 | root | localhost | d1 | Query | 0 | init | SHOW PROCESSLIST |
      +----+------+-----------+------+---------+------+----------+-----------------------------+
      2 rows in set (0.00 sec)

      実際ロックが競合してはいるんですが、lock_wait_timeoutは効きません。SHOW PROCESSLISTを見てやると、Stateはupdatingで、Lock待ち扱いではないですね。ちょっとgdbで覗いてみます。

      $ gdb -p `pidof mysqld`
      ..
      (gdb) thread apply all bt
      ..
      Thread 3 (Thread 0x7fb2000be700 (LWP 8370)):
      #0 0x0000003eb480ed2d in nanosleep () at ../sysdeps/unix/syscall-template.S:82
      #1 0x00007fb200645f79 in grn_nanosleep (nanoseconds=) at ctx.c:81
      #2 0x00007fb20074bc72 in grn_io_lock (ctx=0x7fb17400fb38, io=0x7fb17402b0c0, timeout=10000000) at io.c:1320
      #3 0x00007fb20066df80 in grn_table_delete_by_id (ctx=0x7fb17400fb38, table=0x7fb17402adf0, id=1) at db.c:1784
      #4 0x00007fb1e38ea422 in ha_mroonga::storage_delete_row (this=0x7fb17400e9e0, buf=0x7fb1740379f0 "\001")
      at ha_mroonga.cpp:6241
      #5 0x0000000000590460 in handler::ha_delete_row (this=0x7fb17400e9e0, buf=0x7fb1740379f0 "\001")
      at /home/yoku0825/mysql-5.6.15/sql/handler.cc:7281
      #6 0x0000000000823ebb in mysql_delete (thd=0x46a4ec0, table_list=0x7fb174005038, conds=0x7fb174005b58,
      order_list=, limit=18446744073709551615, options=0)
      at /home/yoku0825/mysql-5.6.15/sql/sql_delete.cc:373
      #7 0x00000000006d5c4a in mysql_execute_command (thd=0x46a4ec0) at /home/yoku0825/mysql-5.6.15/sql/sql_parse.cc:3571
      #8 0x00000000006d8607 in mysql_parse (thd=0x46a4ec0, rawbuf=, length=,
      parser_state=) at /home/yoku0825/mysql-5.6.15/sql/sql_parse.cc:6247
      #9 0x00000000006d9db3 in dispatch_command (command=COM_QUERY, thd=0x46a4ec0, packet=0x7fb2000bdaf0 "\300Nj\004",
      packet_length=27) at /home/yoku0825/mysql-5.6.15/sql/sql_parse.cc:1334
      #10 0x00000000006a23ed in do_handle_one_connection (thd_arg=)
      at /home/yoku0825/mysql-5.6.15/sql/sql_connect.cc:982
      #11 0x00000000006a2522 in handle_one_connection (arg=)
      at /home/yoku0825/mysql-5.6.15/sql/sql_connect.cc:898
      #12 0x0000000000972a37 in pfs_spawn_thread (arg=0x4766040) at /home/yoku0825/mysql-5.6.15/storage/perfschema/pfs.cc:1858
      #13 0x0000003eb4807851 in start_thread (arg=0x7fb2000be700) at pthread_create.c:301
      #14 0x0000003eb40e894d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115
      ..

      grn_io_lockがgrn_nanosleepを呼んでいるので、まあこの辺りですよね。
      grn_io_lockはGroonga(not Mroonga)のlib/io.c に実装されています。

      1290 grn_io_lock(grn_ctx *ctx, grn_io *io, int timeout)
      1291 {
      ..
      1296   for (count = 0;; count++) {
      ..
      1306       if (!timeout || (timeout > 0 && timeout == count)) {
      1307         GRN_LOG(ctx, GRN_LOG_WARNING,
      1308                 "[DB Locked] time out(%d): io(%s) collisions(%d/%d)",
      1309                 timeout, io->path, _ncolls, _ncalls);
      1310         break;
      1311       }
      ..
      1320       grn_nanosleep(1000000);
      1321       continue;
      1322     }
      ..
      1327 }

      タイムアウトするまで自前でループしています。mysqld的にはMroongaストレージエンジンが勝手にループしているだけでロックを検知することができませんので、StateはLockedにはならず、Lock wait timeout exceededも起こりません(´・ω・`)

      で、コイツがタイムアウトしてくれるまでが長い。

      grn_nanosleep(1000000)なので、1回のスリープは1ms。timeoutはgrn_io_lockの呼び出し元すべてで10000000がハードコードされているので、10,000s。

      …いちまんびょう? 3時間?;

      GroongaのRedmineにも上がってるけど、長いし変更できないのはつらい。
      取り敢えずゴニョゴニョしてtimeoutを1000msに変更してみる。

      mysql> INSERT INTO t1 VALUES (1, 'one');
      ERROR 1026 (HY000): grn_io_lock failed

      $ perror 1026
      MySQL error code 1026 (ER_ERROR_ON_WRITE): Error writing file '%-.200s' (errno: %d - %s)

      $ less groonga.log
      2013-12-19 18:04:52.165728|n|120c6700|io(d1.mrn.0000100) collisions(1000/117): lock failed 1000 times
      2013-12-19 18:04:52.165901|w|120c6700|[DB Locked] time out(1000): io(d1.mrn.0000100) collisions(1000/117)
      2013-12-19 18:04:52.165917|e|120c6700|grn_io_lock failed
      ..

      うん、やっぱりこれ自分で設定したいなぁ。。(チラッチラッ
      欲を言えばmy.cnfで設定したいし、更に言うならMySQLのlog-errorにも吐いてほしい。


      同じくラッパーモード。

      mysql> SHOW CREATE TABLE t1\G
      *************************** 1. row ***************************
      Table: t1
      Create Table: CREATE TABLE `t1` (
      `num` int(10) unsigned NOT NULL,
      `val` varchar(32) NOT NULL,
      PRIMARY KEY (`num`),
      FULLTEXT KEY `val` (`val`)
      ) ENGINE=mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
      1 row in set (0.00 sec)
      $ /usr/ruby/2.0.0/bin/ruby -e 'require "groonga"; Groonga::Database.open("/usr/local/mysql/data/d2.mrn"); Groonga["t1"].lock; exit'

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

      mysql> DELETE FROM t1 WHERE num= 1;

      $ gdb -p `pidof mysqld`
      ..
      (gdb) thread apply all bt
      ..
      Thread 2 (Thread 0x7fd1a009d700 (LWP 16797)):
      #0 0x0000003eb480ed2d in nanosleep () at ../sysdeps/unix/syscall-template.S:82
      #1 0x00007fd20c481f79 in grn_nanosleep (nanoseconds=) at ctx.c:81
      #2 0x00007fd20c587c72 in grn_io_lock (ctx=0x7fd17c012a18, io=0x7fd17c310200, timeout=10000000) at io.c:1320
      #3 0x00007fd20c4a9f80 in grn_table_delete_by_id (ctx=0x7fd17c012a18, table=0x7fd17c330ae0, id=1) at db.c:1784
      #4 0x00007fd1ed583422 in ha_mroonga::storage_delete_row (this=0x7fd17c0118c0, buf=0x7fd17c038a80 "\001")
      at ha_mroonga.cpp:6241
      #5 0x0000000000590460 in handler::ha_delete_row (this=0x7fd17c0118c0, buf=0x7fd17c038a80 "\001")
      at /home/yoku0825/mysql-5.6.15/sql/handler.cc:7281
      #6 0x0000000000823ebb in mysql_delete (thd=0x44a0910, table_list=0x7fd17c005038, conds=0x7fd17c005b58,
      order_list=, limit=18446744073709551615, options=0)
      at /home/yoku0825/mysql-5.6.15/sql/sql_delete.cc:373
      #7 0x00000000006d5c4a in mysql_execute_command (thd=0x44a0910) at /home/yoku0825/mysql-5.6.15/sql/sql_parse.cc:3571
      #8 0x00000000006d8607 in mysql_parse (thd=0x44a0910, rawbuf=, length=,
      parser_state=) at /home/yoku0825/mysql-5.6.15/sql/sql_parse.cc:6247
      #9 0x00000000006d9db3 in dispatch_command (command=COM_QUERY, thd=0x44a0910, packet=0x7fd1a009caf0 "\020\tJ\004",
      packet_length=27) at /home/yoku0825/mysql-5.6.15/sql/sql_parse.cc:1334
      #10 0x00000000006a23ed in do_handle_one_connection (thd_arg=)
      at /home/yoku0825/mysql-5.6.15/sql/sql_connect.cc:982
      #11 0x00000000006a2522 in handle_one_connection (arg=)
      at /home/yoku0825/mysql-5.6.15/sql/sql_connect.cc:898
      #12 0x0000000000972a37 in pfs_spawn_thread (arg=0x45619d0) at /home/yoku0825/mysql-5.6.15/storage/perfschema/pfs.cc:1858
      #13 0x0000003eb4807851 in start_thread (arg=0x7fd1a009d700) at pthread_create.c:301
      #14 0x0000003eb40e894d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

      全く同じところで突き刺さる。
      これ、ラッパーモードだろうと更新であればmrnファイルは常に並列性が無くて、ただ mrnファイルはトランザクションのコミットに関わらず先行して更新が終わってしまう からこのジャイアントロックが問題になることは無くて…というシナリオなんだろう、きっと、たぶん(あるいは、ほんのり問題にはなっているけどそれに気付かないか)

      で、mrnファイルのロックが突き刺さると更新系のクエリーが全てupdatingとかexecuteで自前ループを回し、connectionがどんどん溜まりそいつらも自前でループし、最終的にCPUを振り切って略…となったることが予想されるので、タイムアウト値は短めに設定して、groonga.logかアプリのログを監視しておいた方が良いかも。

      別にしょっちゅうロックが刺さっててGroongaなんとかしる、という訳ではなく、たまたま刺さった時に大変だったそうなので備えておきましょうね、というお話でした :)

      Percona XtraDB Cluster設計の迷いどころ(現在進行形)

      $
      0
      0
      今、とあるサービスに Percona XtraDB Clusterを突っ込むことを画策しているんですが、今まさに迷っている真っ最中なのでメモしておきます。誰か何か思いついたら教えてください。

      Percona XtraDB Cluster(以下PXC)というか Galera Replicationの仕組みというかコンセプトというか自体が、「マルチマスターなMySQL *の* クラスター」なわけで、PXCを使えばこれは簡単に実現できます。

      が。

      PXCでマルチマスターすると、バイナリーログの扱いがちょっと問題になってきます。
      wsrep providerが提供するPXC内の同期はGalera Cacheと呼ばれる専用のバイナリーログ(からヘッダーを取り除いたもの)に格納され、同期されます。Galera Cacheのファイルは(どういう仕組みなのか調べてないけれど)原則として一定サイズで(必要に応じて拡張され、必要なくなったら元に戻る)、これだけ見てるとInnoDBログファイルみたいにシーケンシャルに書いて、ファイルの末端に言ったら先頭に戻っている気がしなくもないです(調べてないです)

      これは別にどうでも良いんですが、Galera Cacheを使って同期されたデータは log-slave-updatesで拾えず、つまりバイナリーログは *更新SQLを受け取ったmysqldのみ* が保存します。
      この前提かつマルチマスターにすると、

      バ イ ナ リ ー ロ グ が 複 数 台 に 分 散 さ れ 、 ポ イ ン ト イ ン タ イ ム リ カ バ リ ー が 出 来 な い

      ことになるんじゃないでしょうか。ってか、今のところなってます。
      分散するところまでは良いです。バックアップ用と割り切れば、 MariaDB 10.0のマルチソースレプリケーションとか、 MySQL 5.7のLabs版でlog-slave-updatesもアリな気はします。MySQLには 由緒正しいN:1レプリケーションもありますが、今回はデータをごにょごにょしたいのではなくバイナリーログの保全が目的になるのでちょっとこの方式は取れなそうです。

      いや、MariaDB 10.0もMySQL 5.7も *シリアライズを保証できない*時点で完全にアウトなんですけどね :(

      バイナリーログを一箇所に集めたところで、PITRの為に流しなおす範囲のバイナリーログを全てデコードして、時間順に並べ変えて…としても、バイナリーログは秒までしか時間を保存しないので、その間に同じレコードが2度更新されたりすると、どっちが最終状態として正しいのか判らなくなってPITRの一貫性が崩れる。

      かといってGalera Cacheは上手く吸い上げてやるにはファイルの機構がバイナリーログとは違いそうだし、なんともかんともはや。。

      codershipのフォーラムでも未対応ってことになってた(URLわかんなくなった。。)んですが、一応 *将来ね!* って雰囲気ではありました。

      どうしようかなー。。


      【2014/01/15 14:59】
      今現在はこんなことを考えてます。。


      mysqlコマンドラインクライアントでuseの代わりにcdを使う

      $
      0
      0
      何を言っているのか略という感じな気もするタイトルですね。。
      話の流れとしては、


      とか馬鹿なこと言ってたら
      とツッコミが入ったので、やってみました。
      最初はuseはステートメントだと思っていたので、SQLパーサーをいじって USEステートメントをCDステートメントに変えてやろうと思っていたんですが、"SQLデリミターを省略しても実行される", "mysqlコマンドラインクライアント上でhelp叩くと出てくる"からmysqlコマンドラインクライアント上のコマンドだよ、とツッコミをもらい、そっちをいじくってみることに。

      mysql-5.6.15/client/mysql.ccのこのあたりですね。

       322 static COMMANDS commands[] = {
      ..
       357   { "use",    'u', com_use,    1,
       358     "Use another database. Takes database name as argument." },
      ..

      mysqlコマンドラインクライアント上のuseコマンドを叩くとcom_use関数が呼ばれて、com_use関数の中でmysql_select_db(&mysql, ..)を叩いてcurrent_dbを変更しています。
      com_use関数以降は同じで構わないので、ここだけちゃちゃっと変えてコンパイル。


      $ client/mysql -uroot -S /usr/mysql/5.6.15/data/mysql.sock
      Welcome to the MySQL monitor. Commands end with ; or \g.
      Your MySQL connection id is 8
      Server version: 5.6.15-log Source distribution

      Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

      Oracle is a registered trademark of Oracle Corporation and/or its
      affiliates. Other names may be trademarks of their respective
      owners.

      Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

      mysql> help

      For information about MySQL products and services, visit:
      http://www.mysql.com/
      For developer information, including the MySQL Reference Manual, visit:
      http://dev.mysql.com/
      To buy MySQL Enterprise support, training, or other products, visit:
      https://shop.mysql.com/

      List of all MySQL commands:
      Note that all text commands must be first on line and end with ';'
      ? (\?) Synonym for `help'.
      clear (\c) Clear the current input statement.
      connect (\r) Reconnect to the server. Optional arguments are db and host.
      delimiter (\d) Set statement delimiter.
      edit (\e) Edit command with $EDITOR.
      ego (\G) Send command to mysql server, display result vertically.
      exit (\q) Exit mysql. Same as quit.
      go (\g) Send command to mysql server.
      help (\h) Display this help.
      nopager (\n) Disable pager, print to stdout.
      notee (\t) Don't write into outfile.
      pager (\P) Set PAGER [to_pager]. Print the query results via PAGER.
      print (\p) Print current command.
      prompt (\R) Change your mysql prompt.
      quit (\q) Quit mysql.
      rehash (\#) Rebuild completion hash.
      source (\.) Execute an SQL script file. Takes a file name as an argument.
      status (\s) Get status information from the server.
      system (\!) Execute a system shell command.
      tee (\T) Set outfile [to_outfile]. Append everything into given outfile.
      cd (\u) Use another database. Takes database name as argument.
      charset (\C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets.
      warnings (\W) Show warnings after every statement.
      nowarning (\w) Don't show warnings after every statement.

      For server side help, type 'help contents'

      mysql> SHOW DATABASES;
      +--------------------+
      | Database |
      +--------------------+
      | information_schema |
      | d1 |
      | mysql |
      | performance_schema |
      | test |
      +--------------------+
      5 rows in set (0.00 sec)

      mysql> SELECT database();
      +------------+
      | database() |
      +------------+
      | NULL |
      +------------+
      1 row in set (0.02 sec)

      mysql> cd d1
      Database changed

      mysql> SELECT database();
      +------------+
      | database() |
      +------------+
      | d1 |
      +------------+
      1 row in set (0.00 sec)

      mysql> use mysql
      -> ;
      Query OK, 0 rows affected (0.00 sec)

      mysql> SELECT database();
      +------------+
      | database() |
      +------------+
      | mysql |
      +------------+
      1 row in set (0.00 sec)

      helpの結果がuseコマンドからcdコマンドになり、useを叩くと末尾にセミコロンを求められる。で、セミコロンつけて送信するとMySQLのパーサーがUSEステートメントとして解釈するから"Query OK, .."の応答メッセージ。

      ううむ、意外と面白いぞこれ。

      Percona XtraDB Cluster設定中に気付いたwsrep_certify_nonPK

      $
      0
      0
      Primary Keyが無いテーブル + Galera Clusterで悲惨な目に なんていうことが昔ありました。

      PXC(wsrepの要求なので、Gelara Cluster for MySQL, MariaDB Galera Clusterも同じ)はbinlog_format= ROW が必須パラメーターになっているのでPrimary Keyが無いと悲惨な目に遭うんですが、これを強制的に禁止するオプション(wsrep_certify_nonPK)を発見。

      mysql> SELECT @@wsrep_certify_nonPK;
      +-----------------------+
      | @@wsrep_certify_nonPK |
      +-----------------------+
      | 1 |
      +-----------------------+
      1 row in set (0.00 sec)

      デフォルトはON。

      mysql> CREATE TABLE d1.t1 ( num INT, val varchar(32) ) Engine= InnoDB;
      Query OK, 0 rows affected (0.02 sec)

      mysql> INSERT INTO d1.t1 VALUES (1, 'one');
      Query OK, 1 row affected (0.00 sec)

      mysql> UPDATE d1.t1 SET val= 'eins' WHERE num= 1;
      Query OK, 1 row affected (0.01 sec)
      Rows matched: 1 Changed: 1 Warnings: 0

      mysql> SELECT * FROM d1.t1;
      +------+------+
      | num | val |
      +------+------+
      | 1 | eins |
      +------+------+
      1 row in set (0.00 sec)

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

      Primary Keyが無くてもINSERTもUPDATEもDELETEもできる。フツーの動作。

      mysql> SET GLOBAL wsrep_certify_nonPK= OFF;
      Query OK, 0 rows affected (0.00 sec)

      mysql> SELECT @@wsrep_certify_nonPK;
      +-----------------------+
      | @@wsrep_certify_nonPK |
      +-----------------------+
      | 0 |
      +-----------------------+
      1 row in set (0.00 sec)

      これをOFFる。と、想像通り、

      mysql> INSERT INTO d1.t1 VALUES (1, 'one');
      ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

      容赦なく蹴られるように。
      Error 1213のDeadlock found.. が表示されるのは、全ノード間でデータの同期を保証する為の2相コミットっぽい仕組み(実際には2相コミットじゃない)で失敗したから(これをwsrepではCertificationと呼んでいる)で、Local Certification Failure(XA Prepare的なのに失敗)はError 1213 Deadlock found..で返される仕様になっているから。

      なお飽くまで"Local" Certification Failureなので、他の(wsrep_certify_nonPK= ONな)ノードでINSERTするとINSERTできる。よって、設定する時は全てに設定しておくこと。


      ところでこの動作、MariaDBには標準で無かったっけ? とか思ったけど違った。RBRの時にPkeyが無いテーブルへの更新は拒否する、じゃなくて、RBRの時でPkeyが無いテーブルへの更新でも最適化してスレーブに適用してくれる、という風情らしい。

      Row-based replication with no primary key

      Perconaがオリジナルだっていうのも知らなかった。

      Galera Arbitratorというもの(garbd)

      $
      0
      0
      Galera Cluster(俺が実際に色々やってるのはPercona XtraDB Cluster)はスプリットブレイン対策としてQuorum方式を採用している。クラスターを構成するノードの *過半数*とコミュニケーションが取れていない場合、自身がネットワークから切り離されているとみなして *全ての操作を*拒否するようになる。Galera Clusterはそもそも全てのノードのデータが「仮想完全同期」されているという前提でマルチマスターのトポロジーを提供しているため、コミュニケーションパスが途切れて同期できていないかもしれない状態は、マルチマスターを提供するための下地が崩れるからこうなっている。

      mysql> use test
      ERROR 1047 (08S01): Unknown command

      mysql> SELECT * FROM d1.t1;
      ERROR 1047 (08S01): Unknown command

      出力はこんな感じで、何をしてもError: 1047のUnknown commandが返ってくるようになる。流石にmysqlコマンドラインクライアントだけで完結するコマンド(helpとかcharsetとかteeとか)は実行できるけど、サーバー側から情報を引っ張って何かするコマンド(statusとuseくらい?)は実行できなくなる。

      で、コミュニケーションパスが生きている必要があるノードはクラスターを構成するノードの *過半数*なので、2台構成にしてしまうと1台お亡くなりになった時に 残った1台> 2* 50% が成り立たないため、生き残った1台はこの状態に突入する。これを避けるために、Galera Clusterは最低3台での構成を推奨 している。

      SET GLOBAL wsrep_provider_options= 'pc.ignore_sb= ON'; という手もあるけれど(名前の通り、スプリットブレイン状態になっててもコマンドを拒否しなくなる)、本当にネットワークがおかしくなった時に何が起こるかわからない(2つのmysqldそれぞれに矛盾する更新がかかったら、とか、更新が伝達されずにレコードを読んじゃったら、とか)ので、あまりやりたくない。かといって物理サーバー3台…うーん、微妙。

      そんなわたしに贈る、Galera Arbitrator。プロセス名はgarbd。

      $ ll /usr/local/mysql5534_pxc/bin/garbd
      -rwxr-xr-x 1 root root 18015250 12月  2 21:20 /usr/local/mysql5534_pxc/bin/garbd

      Percona XtraDB Cluster 5.5.34の.tar.gzのバイナリーを解凍した版にはbinの下に入ってた。ソースからコンパイルした人にはいない。どこから手に入れるのかもよく判らないけど、ソースからコンパイルした人、libの下にlibgalera_smm.soもなくて別途codershipから取ってきて入れないといけないから、そこから入れるのかな。PXCなら.tar.gz版がオールインワンで扱いやすいと思う余談。MariaDB Galera Clusterは(入ってるのか入ってないのか)知らない。

      $ /usr/local/mysql5534_pxc/bin/garbd --help

      Usage: /usr/local/mysql5534_pxc/bin/garbd [options] [group address]

      Configuration:
      -d [ --daemon ] Become daemon
      -a [ --address ] arg Group address
      -g [ --group ] arg Group name
      --sst arg SST request string
      --donor arg SST donor name
      -o [ --options ] arg GCS/GCOMM option list
      -l [ --log ] arg Log file
      -c [ --cfg ] arg Configuration file


      Other options:
      -v [ --version ] Print version
      -h [ --help ] Show help message

      FATAL: Exit

      ざっくりと、必要最低限っぽいオプションは-a(PXCのwsrep_cluster_addressに相当)と-g(PXCのwsrep_cluster_nameに相当)かしら。

      $ /usr/local/mysql5534_pxc/bin/garbd -a "gcomm://xxx.xxx.xxx.xxx:13333" -g "cluster_name" -o "gmcast.listen_addr= tcp://0.0.0.0:13334"
      2014-01-23 12:13:20.944 INFO: Read config:
      daemon: 0
      address: gcomm://xxx.xxx.xxx.xxx:13333
      group: cluster_name
      sst: trivial
      donor:
      options: gmcast.listen_addr= tcp://0.0.0.0:13334; gcs.fc_limit=9999999; gcs.fc_factor=1.0; gcs.fc_master_slave=yes
      cfg:
      log:

      2014-01-23 12:13:20.952 INFO: protonet asio version 0
      2014-01-23 12:13:20.953 INFO: backend: asio
      2014-01-23 12:13:20.967 INFO: GMCast version 0
      2014-01-23 12:13:20.972 INFO: (50019916-83dc-11e3-a310-bfb42459a728, 'tcp://0.0.0.0:13334') listening at tcp://0.0.0.0:13334
      2014-01-23 12:13:20.972 INFO: (50019916-83dc-11e3-a310-bfb42459a728, 'tcp://0.0.0.0:13334') multicast: , ttl: 1
      2014-01-23 12:13:20.992 INFO: EVS version 0
      2014-01-23 12:13:21.000 INFO: PC version 0
      2014-01-23 12:13:21.000 INFO: gcomm: connecting to group 'cluster_name', peer 'xxx.xxx.xxx.xxx:13333'
      2014-01-23 12:13:21.007 INFO: (50019916-83dc-11e3-a310-bfb42459a728, 'tcp://0.0.0.0:13334') turning message relay requesting on, nonlive peers: tcp://xxx.xxx.xxx.xxx:13332
      2014-01-23 12:13:21.489 INFO: (50019916-83dc-11e3-a310-bfb42459a728, 'tcp://0.0.0.0:13334') turning message relay requesting off
      2014-01-23 12:13:21.505 INFO: declaring 1663d468-79c2-11e3-a5e5-2f016f2db075 stable
      2014-01-23 12:13:21.505 INFO: declaring bc9b8196-83d6-11e3-b4d0-1a4766eec11e stable
      2014-01-23 12:13:21.506 INFO: Node 1663d468-79c2-11e3-a5e5-2f016f2db075 state prim
      2014-01-23 12:13:21.506 INFO: view(view_id(PRIM,1663d468-79c2-11e3-a5e5-2f016f2db075,43) memb {
      1663d468-79c2-11e3-a5e5-2f016f2db075,
      50019916-83dc-11e3-a310-bfb42459a728,
      bc9b8196-83d6-11e3-b4d0-1a4766eec11e,
      } joined {
      } left {
      } partitioned {
      })
      2014-01-23 12:13:22.005 INFO: gcomm: connected
      2014-01-23 12:13:22.005 INFO: Changing maximum packet size to 64500, resulting msg size: 32636
      2014-01-23 12:13:22.005 INFO: Shifting CLOSED -> OPEN (TO: 0)
      2014-01-23 12:13:22.005 INFO: Opened channel 'cluster_name'
      2014-01-23 12:13:22.006 INFO: New COMPONENT: primary = yes, bootstrap = no, my_idx = 1, memb_num = 3
      2014-01-23 12:13:22.006 INFO: STATE EXCHANGE: Waiting for state UUID.
      2014-01-23 12:13:22.006 INFO: STATE EXCHANGE: sent state msg: 5055781a-83dc-11e3-b9e1-e2687a7bb5a6
      2014-01-23 12:13:22.006 INFO: STATE EXCHANGE: got state msg: 5055781a-83dc-11e3-b9e1-e2687a7bb5a6 from 0 (xxxx_3333)
      2014-01-23 12:13:22.006 INFO: STATE EXCHANGE: got state msg: 5055781a-83dc-11e3-b9e1-e2687a7bb5a6 from 2 (xxxx_3332)
      2014-01-23 12:13:22.007 INFO: STATE EXCHANGE: got state msg: 5055781a-83dc-11e3-b9e1-e2687a7bb5a6 from 1 (garb)
      2014-01-23 12:13:22.007 INFO: Quorum results:
      version = 2,
      component = PRIMARY,
      conf_id = 33,
      members = 2/3 (joined/total),
      act_id = 29266,
      last_appl. = -1,
      protocols = 0/4/2 (gcs/repl/appl),
      group UUID = 64863b36-62f7-11e3-bef2-a746941be182
      2014-01-23 12:13:22.007 INFO: Flow-control interval: [9999999, 9999999]
      2014-01-23 12:13:22.007 INFO: Shifting OPEN -> PRIMARY (TO: 29266)
      2014-01-23 12:13:22.007 INFO: Sending state transfer request: 'trivial', size: 7
      2014-01-23 12:13:22.007 INFO: Node 1 (garb) requested state transfer from '*any*'. Selected 0 (xxxx_3333)(SYNCED) as donor.
      2014-01-23 12:13:22.007 INFO: Shifting PRIMARY -> JOINER (TO: 29266)
      2014-01-23 12:13:22.008 INFO: 0 (xxxx_3333): State transfer to 1 (garb) complete.
      2014-01-23 12:13:22.008 INFO: 1 (garb): State transfer from 0 (xxxx_3333) complete.
      2014-01-23 12:13:22.008 INFO: Shifting JOINER -> JOINED (TO: 29266)
      2014-01-23 12:13:22.008 INFO: Member 0 (xxxx_3333) synced with group.
      2014-01-23 12:13:22.008 INFO: Member 1 (garb) synced with group.
      2014-01-23 12:13:22.008 INFO: Shifting JOINED -> SYNCED (TO: 29266)

      "gmcast.listen_addr= tcp://0.0.0.0:13334"は、このノード既にPXCが3インスタンスいるのでポートがカブらないように調整しているだけで、フツーにPXCのmysqldを起動させた時と同じように、コミュニケーションパスを使って自分のステータスをSYNCEDまで持って行ってる。SST(=フル同期)はしない。

      他のノードからSHOW GLOBAL STATUS LIKE 'wsrep_cluster_size'で見ると、確かに増えている。これでmysqldが3つなくても、1台落ちただけなら過半数を満たせる数が確保できるという算段。APサーバーの先頭か何かにgarbdだけ入れておけばね。


      ……MHA-Managerみたい。。

      全文検索のテスト用にtweets.csvを食わせるSQL

      $
      0
      0
      毎回手打ちするのが面倒なのでメモ。

      mysql> CREATE TABLE tweets (tweet_id bigint unsigned primary key, timestamp timestamp NOT NULL, text text NOT NULL, FULLTEXT KEY(text)) Engine= mroonga;
      Query OK, 0 rows affected (0.05 sec)

      mysql> LOAD DATA INFILE '/tmp/tweets.csv' INTO TABLE tweets FIELDS TERMINATED BY ',' ENCLOSED BY '"' IGNORE 1 ROWS (tweet_id, @dummy, @dummy, @timestamp, @dummy, text, @dummy) SET timestamp= DATE_ADD(@timestamp, INTERVAL 9 HOUR);
      Query OK, 14410 rows affected, 28821 warnings (0.35 sec)
      Records: 14410 Deleted: 0 Skipped: 0 Warnings: 28821

      CSVのtext以降の部分をTRUNCATEしたってワーニングがごちゃっと出る。
      timestampは"2013-12-17 03:06:59 +0000"(=UTC)の形で入っていて、SET TIME_ZONEで頑張ってみたけど上手く行かなかったのでSETで無理矢理9時間足してみた。

      groonga-index-dumpで遊んでみる

      $
      0
      0
      これがやりたくてtweets.csvをMroongaに食わせていた

      $ time /usr/ruby/2.0.0/bin/groonga-index-dump /usr/mysql/5.6.15/data/mroonga.mrn

      real 0m33.430s
      user 0m29.649s
      sys 0m2.924s

      $ ll
      合計 16
      drwxr-xr-x 2 root root 4096 7月 3 19:14 2013 hsperfdata_root
      drwxr-xr-x 2 yoku0825 yoku0825 4096 11月 6 11:49 2013 hsperfdata_ttanaka
      drwxrwxr-x 3 yoku0825 yoku0825 4096 1月 24 12:04 2014 index-dump
      drwx------ 2 yoku0825 tmux 4096 1月 10 15:19 2014 tmux-3012

      --output-directoryで指定しない場合はカレントディレクトリの下にindex-dumpディレクトリを掘る。

      $ cd index-dump/

      $ ll
      合計 2012
      drwxrwxr-x 2 yoku0825 yoku0825 2056192 1月 24 12:04 2014 tweets-text.index

      $ cd tweets-text.index/

      $ ll | wc
      47997 479962 3723665

      index-dumpの下に<テーブル名>-<インデックス名>.indexディレクトリが作られるぽい。
      ls -l叩くと泣きたくなるくらいにファイルの数が多い。

      $ ll | head
      合計 201992
      -rw-rw-r-- 1 yoku0825 yoku0825 222 1月 24 12:04 2014 %21%21%21%21%21%21%21%21%21%21%21%21%21%21.dump
      -rw-rw-r-- 1 yoku0825 yoku0825 218 1月 24 12:04 2014 %21%21%21%21%21%21%21%E3%80%8D.dump
      -rw-rw-r-- 1 yoku0825 yoku0825 256 1月 24 12:04 2014 %21%21%21%21%21%21%21.dump
      -rw-rw-r-- 1 yoku0825 yoku0825 337 1月 24 12:04 2014 %21%21%21%21%21%21.dump
      -rw-rw-r-- 1 yoku0825 yoku0825 216 1月 24 12:04 2014 %21%21%21%21%21%E3%80%8D.dump
      -rw-rw-r-- 1 yoku0825 yoku0825 417 1月 24 12:04 2014 %21%21%21%21%21.dump
      -rw-rw-r-- 1 yoku0825 yoku0825 218 1月 24 12:04 2014 %21%21%21%21%E3%80%8D%E3%80%8C.dump
      -rw-rw-r-- 1 yoku0825 yoku0825 417 1月 24 12:04 2014 %21%21%21%21.dump
      -rw-rw-r-- 1 yoku0825 yoku0825 212 1月 24 12:04 2014 %21%21%21%28.dump

      なんかざっと見ただけで、1つのトークンに対して1つの<トークン>.dumpファイルが出来上がっている予感。

      $ cat %21%21%21%21%21%21%21%21%21%21%21%21%21%21.dump
      index: tweets-text.index term: <!!!!!!!!!!!!!!> domain: tweets-text range: tweets have_section: false have_weight: false have_position: true
      weight position term_frequency record
      0 74 1 tweets[398641294714499072].text

      $ cat %E3%81%84%E3%81%BC.dump
      index: tweets-text.index term: <いぼ> domain: tweets-text range: tweets have_section: false have_weight: false have_position: true
      weight position term_frequency record
      0 40 1 tweets[198244626002481152].text

      やりたいことは myisam_ftdump的なことなので、この.dumpファイルをテキトーにパースしてやらないといけないっぽい。

      取りあえず、投げやりなPerlワンライナーで。

      $ cat `ls | head -1000` | perl -nle 'if (/\sterm:\s<(.+)>\s/) {printf("%d\t%s\n", $count, $term); $term= $1; $count= 0;} elsif (/\s([0-9]+)\s+tweets\[[0-9]+\]\.text$/) {$count+= $1;}' | sort -nr | head
      12305 、
      11013 。
      9295 @
      7042 /
      4599 :
      3583 。。
      3479 #
      2577 ://
      2561 (
      2243 「

      cat *でやろうとしたらなんか怒られた(´・ω・`)

      $ cat * | perl -nle 'if (/\sterm:\s<(.+)>\s/) {printf("%d\t%s\n", $count, $term); $
      term= $1; $count= 0;} elsif (/\s([0-9]+)\s+tweets\[[0-9]+\]\.text$/) {$count+= $1;}' | sort -nr | head
      cat: オプションが違います -- '%'
      詳しくは `cat --help'を実行して下さい.

      これで索引の中の無駄なトークンをあぶりだしてメンテしてやるのがゴールのつもり。


      【2014/01/24 13:46】
      catがダメなのは"-"で始まるファイルっぽい。

      $ for f in *; do cat $f > /dev/null || (echo $f; break); done
      cat: オプションが違います -- '%'
      詳しくは `cat --help'を実行して下さい.
      -%23.dump
      cat: オプションが違います -- '%'
      詳しくは `cat --help'を実行して下さい.
      -%25%27.dump
      cat: オプションが違います -- '%'
      詳しくは `cat --help'を実行して下さい.
      -%26.dump
      ..

      cat ./*にしたら食えた。

      $ cat ./* | perl -nle 'if (/\sterm:\s<(.+)>\s/) {printf("%d\t%s\n", $count, $term); $term= $1; $count= 0;} elsif (/\s([0-9]+)\s+tweets\[[0-9]+\]\.text$/) {$count+= $1;}' | sort -nr | head
      12305 、
      11013 。
      10217 _
      9295 @
      7042 /
      6456 って
      4599 :
      4514 mysql
      4147 ない
      3801 5

      うん、mysql多いなオイ。

      MySQLでUDFを含んだクエリーをクエリーキャッシュに載せるライフハック

      $
      0
      0
      kazeburoさんのツイートを見てふとやってみたくなった。
      反省はしていない。



      取りあえずmroonga_snippetで試してみようと思って、mroonga 2.07のリリースノート をまるっとテストケースにする。

      mysql56> CREATE TABLE snippet_test (id int NOT NULL, text text, PRIMARY KEY(id), FULLTEXT KEY(text)) Engine= mroonga;
      Query OK, 0 rows affected (0.10 sec)

      mysql56> INSERT INTO snippet_test (id, text) VALUES (1, 'An open-source fulltext search engine and column store.');
      Query OK, 1 row affected (0.01 sec)

      mysql56> INSERT INTO snippet_test (id, text) VALUES (2, 'An open-source storage engine for fast fulltext search with MySQL.');
      Query OK, 1 row affected (0.01 sec)

      mysql56> INSERT INTO snippet_test (id, text) VALUES (3, 'Tritonn is a patched version of MySQL that supports better fulltext search function with Senna.');
      Query OK, 1 row affected (0.00 sec)

      mysql56> SELECT @@query_cache_type;
      +--------------------+
      | @@query_cache_type |
      +--------------------+
      | DEMAND |
      +--------------------+
      1 row in set (0.00 sec)

      mysql56> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
      +-------------------------+-------+
      | Variable_name | Value |
      +-------------------------+-------+
      | Qcache_queries_in_cache | 0 |
      +-------------------------+-------+
      1 row in set (0.00 sec)

      mysql56> SELECT * FROM snippet_test;
      +----+-------------------------------------------------------------------------------------------------+
      | id | text |
      +----+-------------------------------------------------------------------------------------------------+
      | 1 | An open-source fulltext search engine and column store. |
      | 2 | An open-source storage engine for fast fulltext search with MySQL. |
      | 3 | Tritonn is a patched version of MySQL that supports better fulltext search function with Senna. |
      +----+-------------------------------------------------------------------------------------------------+
      3 rows in set (0.00 sec)

      mysql56> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
      +-------------------------+-------+
      | Variable_name | Value |
      +-------------------------+-------+
      | Qcache_queries_in_cache | 0 |
      +-------------------------+-------+
      1 row in set (0.00 sec)

      mysql56> SELECT sql_cache * FROM snippet_test;
      +----+-------------------------------------------------------------------------------------------------+
      | id | text |
      +----+-------------------------------------------------------------------------------------------------+
      | 1 | An open-source fulltext search engine and column store. |
      | 2 | An open-source storage engine for fast fulltext search with MySQL. |
      | 3 | Tritonn is a patched version of MySQL that supports better fulltext search function with Senna. |
      +----+-------------------------------------------------------------------------------------------------+
      3 rows in set (0.01 sec)

      mysql56> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
      +-------------------------+-------+
      | Variable_name | Value |
      +-------------------------+-------+
      | Qcache_queries_in_cache | 1 |
      +-------------------------+-------+
      1 row in set (0.00 sec)

      query_cache_type= 2なので、sql_cacheオプションをつけたときだけクエリーキャッシュに入る。

      mysql56> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
      +-------------------------+-------+
      | Variable_name | Value |
      +-------------------------+-------+
      | Qcache_queries_in_cache | 1 |
      +-------------------------+-------+
      1 row in set (0.00 sec)

      mysql56> SELECT sql_cache id, mroonga_snippet(text, 8, 2, 'utf8_general_ci', 1, 1, '...', '...
      ',
      -> 'fulltext', '', '',
      -> 'MySQL', '', '',
      -> 'search', '', '')
      -> FROM snippet_test WHERE MATCH(text) AGAINST('fulltext');
      +----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
      | id | mroonga_snippet(text, 8, 2, 'utf8_general_ci', 1, 1, '...', '...
      ',
      'fulltext', '', '',
      'MySQL', '', '',
      |
      +----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
      | 1 | ...fulltext...
      ... search ...
      |
      | 2 | ...fulltext...
      ... search ...
      |
      | 3 | ...f MySQL ...
      ...fulltext...
      |
      +----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
      3 rows in set (0.00 sec)

      mysql56> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
      +-------------------------+-------+
      | Variable_name | Value |
      +-------------------------+-------+
      | Qcache_queries_in_cache | 1 |
      +-------------------------+-------+
      1 row in set (0.00 sec)

      おー、ホントだ、載らない。

      昔取った杵柄で、何とかすれば何とでもなりそうな気配がしたので、キューピー3分ハッキングといきます。

      まずはsql/sql_cache.cc の中のどこかで、UDFを使ったクエリーはキャッシュしない、と判定されていいるはずなのでそこを探す。たぶん、is_cacheable とかいう名前の何かがあったはず。
      mysql-5.6.16/sql/sql_cache.cc
      ..
      3707 TABLE_COUNTER_TYPE
      3708 Query_cache::is_cacheable(THD *thd, size_t query_len, const char *query,
      3709                           LEX *lex,
      3710                           TABLE_LIST *tables_used, uint8 *tables_type)
      3711 {
      3712   TABLE_COUNTER_TYPE table_count;
      3713   DBUG_ENTER("Query_cache::is_cacheable");
      3714
      3715   if (query_cache_is_cacheable_query(lex) &&
      3716       (thd->variables.query_cache_type == 1 ||
      3717        (thd->variables.query_cache_type == 2 && (lex->select_lex.options &
      3718                                                  OPTION_TO_QUERY_CACHE))))
      3719   {
      ..
      ここかなー。
      thd->variables.query_cache_typeはSET SESSION query_ache_type= ..で指定するやつだし、lex->select_lex.optionsは"sql_cache", "sql_no_cache"とかを持つところだから違うし、明らかにquery_cache_is_cacheable_queryが怪しい。
      mysql-5.6.16/sql/sql_cache.h
      ..
      562 #define query_cache_is_cacheable_query(L) \
      563   (((L)->sql_command == SQLCOM_SELECT) && (L)->safe_to_cache_query && \
      564    !(L)->describe)
      ..
      lex->safe_to_cache_queryかな?
      mysql-5.6.16/sql/item_create.cc
      ..
      2733 Item*
      2734 Create_udf_func::create(THD *thd, udf_func *udf, List<Item> *item_list)
      2735 {
      ..
      2826   thd->lex->safe_to_cache_query= 0;
      2827   DBUG_RETURN(func);
      2828 }
      ..
      ここっぽいので取り敢えず= 1に書き換えてmakeする。


      mysql56> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
      +-------------------------+-------+
      | Variable_name | Value |
      +-------------------------+-------+
      | Qcache_queries_in_cache | 0 |
      +-------------------------+-------+
      1 row in set (0.00 sec)

      mysql56> SELECT sql_cache id, mroonga_snippet(text, 8, 2, 'utf8_general_ci', 1, 1, '...', '...
      ',
      -> 'fulltext', '', '',
      -> 'MySQL', '', '',
      -> 'search', '', '')
      -> FROM snippet_test WHERE MATCH(text) AGAINST('fulltext');
      +----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
      | id | mroonga_snippet(text, 8, 2, 'utf8_general_ci', 1, 1, '...', '...
      ',
      'fulltext', '', '',
      'MySQL', '', '',
      |
      +----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
      | 1 | ...fulltext...
      ... search ...
      |
      | 2 | ...fulltext...
      ... search ...
      |
      | 3 | ...f MySQL ...
      ...fulltext...
      |
      +----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
      3 rows in set (0.00 sec)

      mysql56> SHOW GLOBAL STATUS LIKE 'Qcache_queries_in_cache';
      +-------------------------+-------+
      | Variable_name | Value |
      +-------------------------+-------+
      | Qcache_queries_in_cache | 1 |
      +-------------------------+-------+
      1 row in set (0.00 sec)

      載った。満足。

      デブサミ2014のコミュニティーLTしてきました

      $
      0
      0
      ずさー。





      日本MySQLユーザ会のひととしてLTさせてもらいました。
      みんな、自分のコミュニティーにまつわるStory(発祥からの経緯とか、最近の活動とか)とか話してたのに、俺だけ自分のStory語っててやっぱりすべりました。どうしてこうなった。

      すべり駆動コミュニティーでもいいじゃないみたいな話になってますが、出会い厨でコミュニティー参加しても楽しいもんですよ、という話をしたかったです本当は。

      Chiba.pmにはすたじおさんに会いたくて行ったし
      Groongaを囲む夕べ には斯波さんに会いたくてGroonga使ってないのに行ったし
      MyNA会 2013年3月 には SH2さん に会いたくて(強引にネタを作って)行ったし
      JAWS-UG(第何回か忘れた…) にはSH2さん(again)とこんまめさんに会いたくて行ったし(でもこんまめさんのセッション聞かずに帰った…orz)
      MySQL Cluster Casual Talks は「企画したら誰か会えるかなー」と思っていたらみんな来てくれて嬉しかったし
      OracleのMySQL Tech Tour Tokyoはやまさきさんに会いに行ってるようなもんだし

      不純な動機ではじめても、結構楽しいものですよ :)
      Have fun!

      MySQLで「任意のテーブルのDROPは許可したい」けれど、「スキーマはDROPさせたくない」

      $
      0
      0
      MySQL分補充エントリー。

      MyNA 15987のメール でそんな話題があっていろいろ考えてネタにしようと思ってたんですが、気が付けばこれ去年の7月じゃないですか。。

      やりたいこと。

      mysql56> SHOW GRANTS;
      +------------------------------------------------+
      | Grants for user1@localhost |
      +------------------------------------------------+
      | GRANT USAGE ON *.* TO 'user1'@'localhost' |
      | GRANT DROP ON `user1`.* TO 'user1'@'localhost' |
      +------------------------------------------------+
      2 rows in set (0.00 sec)
      なユーザーに

      mysql56> DROP TABLE user1.t1;
      Query OK, 0 rows affected (0.07 sec)

      は(マニュアルにあるとおり)成功させたいけれど、

      mysql56> DROP DATABASE user1;
      Query OK, 0 rows affected (0.15 sec)

      は(フツーやると成功するところを)失敗させたい。


      思い付いたやり方は2つ。

      *** sql/sql_db.cc.orig  2014-01-15 00:38:00.000000000 +0900
      --- sql/sql_db.cc 2014-02-28 17:13:19.383810416 +0900
      ***************
      *** 44,49 ****
      --- 44,51 ----
      #endif
      #include "debug_sync.h"

      + #include "sql_parse.h" // check_global_access
      +
      #define MAX_DROP_TABLE_Q_LEN 1024

      const char *del_exts[]= {".frm", ".BAK", ".TMD", ".opt", ".OLD", NullS};
      ***************
      *** 772,777 ****
      --- 774,782 ----

      bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
      {
      + if (check_global_access(thd, SUPER_ACL))
      + DBUG_RETURN(0);
      +
      ulong deleted_tables= 0;
      bool error= true;
      char path[2 * FN_REFLEN + 16];

      安定の、いつものパターン。

      mysql56> DROP TABLE user1.t1;
      Query OK, 0 rows affected (0.02 sec)

      mysql56> DROP DATABASE user1;
      ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER privilege(s) for this operation

      書き足したのもシンプルだし、俺にしては珍しく(!)ちゃんとmtrやったので、どこかで使う機会があったら使ってもいいなくらいの出来。というか必要があったら使うわこのパッチ。

      あともう1つ、ソースコードをいじることなく出来る汚い手としては、

      $ pwd
      /usr/mysql/5.6.16/data/user1

      $ touch drop_database_prevention

      $ ll
      合計 112
      -rw-rw---- 1 yoku0825 yoku0825 61 2月 28 17:36 2014 db.opt
      -rw-rw-r-- 1 yoku0825 yoku0825 0 2月 28 17:43 2014 drop_database_prevention
      -rw-rw---- 1 yoku0825 yoku0825 8558 2月 28 17:42 2014 t1.frm
      -rw-rw---- 1 yoku0825 yoku0825 98304 2月 28 17:42 2014 t1.ibd

      mysql56> DROP TABLE user1.t1;
      Query OK, 0 rows affected (0.02 sec)

      mysql56> DROP DATABASE user1;
      ERROR 1010 (HY000): Error dropping database (can't rmdir './user1/', errno: 17)

      $ ll
      合計 0
      -rw-rw-r-- 1 yoku0825 yoku0825 0 2月 28 17:43 2014 drop_database_prevention

      DROP DATABASEがMySQLのテーブルに関するファイルを全部消したあとにrmdirを呼んでいるのを逆手に取った嫌がらせ小ネタでした。

      MySQLのパラメーターチューニング at OSC 2014 Tokyo/Spring

      $
      0
      0
      去る2014/03/01(土)の OSC 2014 Tokyo/SpringでMyNAとしてセミナーの枠をいただいたので、おはなしししてきました。

      雨の中たくさんの方に足を運んでいただきました。本当にどうもありがとうございました。



      "MySQLパラメーターチューニングの理屈と定石"と銘打って、普段アプリを書いているような人向けに、俺が普段やってるパラメーターチューニングと同じくらいのことが誰にでもできるように…とか思って資料を作っていたんですが(社内のDB勉強会に使うネタのうち、パラメーター調整の部分だけを切り出した、というのがもともとのコンセプトです)、作れば作るほど、いかに自分が「考えるな。感じるんだ」でチューニングしているのかをむしろ思い知らされました。

      「これとこれ見るでしょ? そうするとなんか変な感じがするじゃない? で、こことここだからこれかなって。いじってみたら違うから、近いパラメーターいじってみったら上手くいった」 みたいな。更に言うなら、パラメーターだけいじることなんて運用フェーズに入ればそんなになくて、「このSQLのネックはこことここのはずで、こっちはSQL書き換えて回避できるけどもう片方はすぐにはできなさそうだから、パラメーターで誤魔化しておく」とかフツーにあるわけですよ。

      そうやってあたりをつけながら切り分けて、最適…とまではいかなくともまあまあな状態にチューニングするんですが、これを体系立てて説明する…? とか、正直スキルが足りなさすぎました_| ̄|○

      「これだけ設定しておけば間違いない!」みたいなのを紹介したかったし、期待されていたと思うんですが、さりとてテキトーなことを言うわけにもいかず、innodb_buffer_pool_sizeとinnodb_log_file_size* innodb_log_files_in_groupくらいで、そんなの当然みなさんご存知ですよねごめんなさい、という風情です。

      スライド中に出てきたツールとか、リンク張るの忘れてた(というか、PDFだ。。)ので、こちらにリンクだけ掲載しておきます。




      どうもありがとうございました。

      information_schemaでちょこちょこ使えるTIPS

      $
      0
      0
      ちょこちょこ使うi_s関連のSELECTステートメント。
      やる前にSET GLOBAL innodb_stats_on_metadata= 0; しておかないと重くなる。

      • xxxってカラム、どのテーブルにあるんだっけ?
      mysql56> SELECT CONCAT(table_schema, '.', table_name) AS object, column_type FROM columns WHERE column_name= ? ORDER BY 1, 2;

      • データベース上のINDEXの一覧がほしい。
      mysql56> SELECT CONCAT(table_schema, '.', table_name) AS object, index_name, GROUP_CONCAT(column_name ORDER BY seq_in_index) AS columns FROM statistics WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema') GROUP BY 1, 2;

      • フルテキストインデックスどこだっけ?
      mysql56> SELECT CONCAT(table_schema, '.', table_name) AS object, index_name, GROUP_CONCAT(column_name ORDER BY seq_in_index) AS columns FROM statistics WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema') AND index_type= 'FULLTEXT' GROUP BY 1, 2;

      • パーティションの一覧と、入っている件数がほしい。
        • partition_name IS NOT NULLをはずすと、パーティショニングされてないテーブルも出力できる。
      mysql56> SELECT CONCAT(table_schema, '.', table_name) AS object, partition_name, table_rows FROM partitions WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema') partition_name IS NOT NULL ORDER BY 1, 2;

      • PARTITION .. LESS THAN MAXVALUEなパーティションに今入っている件数をチェック。
      mysql56> SELECT CONCAT(table_schema, '.', table_name) AS object, partition_name, table_rows FROM partitions WHERE partition_description= 'MAXVALUE' ORDER BY 1, 2;

      • InnoDBバッファプールにどのテーブルのデータがどれくらい載ってるか知りたい。
        • table_name, index_nameがNULLのは空きページ。
      mysql56> SELECT table_name, index_name, SUM(number_records) AS record, SUM(data_size) AS datasize FROM innodb_buffer_page GROUP BY 1, 2;

      • テーブルごと、スキーマごと、エンジンごとのデータサイズを一発で。
      mysql56> SELECT engine, table_schema, table_name, SUM(data_length+ index_length) AS size FROM tables WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema') GROUP BY 1, 2, 3 WITH ROLLUP;

      mysql56> SELECT name, count, status FROM innodb_metrics;


      InnoDBのロック競合を解析するアレはご本家をどうぞ。
       ⇒ MySQL InnoDBにおけるロック競合の解析手順 - SH2の日記

      Mroongaのテーブルからノイズになるトークンを(手で)取り除いてみる

      $
      0
      0
      データは 日々の覚書: 全文検索のテスト用にtweets.csvを食わせるSQL で書いた、わたしのツイートをテーブルに突っ込んだもので試してみました。

      トークナイザーとノーマライザーはデフォルトのTokenBigram, NormalizerMySQLGeneralCIにそれぞれなっています。

      $ /usr/groonga/4.0.0/bin/groonga /usr/mysql/5.6.16/data/mroonga.mrn
      > table_list --output_type tsv
      0 1393927353.03097 0.000258684158325195
      "id""UInt32"
      "name""ShortText"
      "path""ShortText"
      "flags""ShortText"
      "domain""ShortText"
      "range""ShortText"
      "default_tokenizer""ShortText"
      "normalizer""ShortText"
      259
      "tweets"
      "/usr/mysql/5.6.16/data/mroonga.mrn.0000103"
      "TABLE_PAT_KEY|PERSISTENT"
      "UInt64"



      263
      "tweets-text"
      "/usr/mysql/5.6.16/data/mroonga.mrn.0000107"
      "TABLE_PAT_KEY|PERSISTENT"
      "ShortText"

      "TokenBigram"
      "NormalizerMySQLGeneralCI"
      END

      たぶん中の人には嫌われるでしょうが、わたしは--output_type tsv好きですよ :)
      では早速トークンを覗き込んでみる。

      > select tweets-text --output_type tsv
      0 1393927720.5169 0.000890493392944336
      51762
      [ "_id""UInt32" ] [ "_key""ShortText" ] [ "index""tweets" ]
      26653 "\t" 45
      44250 "\tな" 1
      15055 "\tを" 1
      4 "\n" 944
      3 "\n\n" 945
      11621 "\nあ" 60
      3101 "\nい" 23
      14566 "\nう" 21
      5776 "\nえ" 1
      11625 "\nお" 77
      END

      いかにもノイズっぽいのがいっぱいありますね!
      あ、"index""tweets"のカラム(3カラム目の、数字が並んでるやつ)は、そのトークンを持ってるレコードの数とかじゃないのでご注意を。

      もとの速度は

      mysql56> pager cat > /dev/null
      PAGER set to 'cat > /dev/null'

      mysql56> SELECT * FROM tweets WHERE MATCH(text) AGAINST ('+MySQL' IN BOOLEAN MODE);
      2778 rows in set (0.02 sec)

      mysql56> nopager
      PAGER set to stdout

      mysql56> SHOW PROFILE;
      +--------------------------------+----------+
      | Status | Duration |
      +--------------------------------+----------+
      | starting | 0.000055 |
      | Waiting for query cache lock | 0.000007 |
      | init | 0.000006 |
      | checking query cache for query | 0.000131 |
      | checking permissions | 0.000016 |
      | Opening tables | 0.000115 |
      | init | 0.000084 |
      | System lock | 0.000030 |
      | optimizing | 0.000019 |
      | statistics | 0.000663 |
      | preparing | 0.000036 |
      | FULLTEXT initialization | 0.003164 |
      | executing | 0.000021 |
      | Sending data | 0.019530 |
      | end | 0.000027 |
      | query end | 0.000012 |
      | closing tables | 0.000177 |
      | freeing items | 0.003743 |
      | cleaning up | 0.000056 |
      +--------------------------------+----------+
      19 rows in set, 1 warning (0.00 sec)

      まあこんなもんとしておきます。

      $ /usr/groonga/4.0.0/bin/groonga /usr/mysql/5.6.16/data/mroonga.mrn "select tweets-text --output_type tsv --limit -1" | perl -nlae 'if (@F[1] =~ /\\/) {my $cmd= sprintf("/usr/groonga/4.0.0/bin/groonga /usr/mysql/5.6.16/data/mroonga.mrn \"delete tweets-text --output_type tsv --filter \\\"_id== %d\\\"\"", @F[0]); system($cmd);}'
      ..

      とまあ、要らなさそうなトークンを選んで消していきます。他にも記号だけで構成されたトークンとかいろいろゴニョゴニョがんばって消してみました。どうか。

      mysql56> pager cat > /dev/null
      PAGER set to 'cat > /dev/null'

      mysql56> SELECT * FROM tweets WHERE MATCH(text) AGAINST ('+MySQL' IN BOOLEAN MODE);
      2778 rows in set (0.03 sec)

      mysql56> nopager
      PAGER set to stdout

      mysql56> SHOW PROFILE;
      +--------------------------------+----------+
      | Status | Duration |
      +--------------------------------+----------+
      | starting | 0.000045 |
      | Waiting for query cache lock | 0.000009 |
      | init | 0.000006 |
      | checking query cache for query | 0.000099 |
      | checking permissions | 0.000012 |
      | Opening tables | 0.000119 |
      | init | 0.000054 |
      | System lock | 0.000019 |
      | optimizing | 0.000022 |
      | statistics | 0.000623 |
      | preparing | 0.000033 |
      | FULLTEXT initialization | 0.003134 |
      | executing | 0.000018 |
      | Sending data | 0.019653 |
      | end | 0.000038 |
      | query end | 0.000012 |
      | closing tables | 0.000199 |
      | freeing items | 0.002571 |
      | cleaning up | 0.000047 |
      +--------------------------------+----------+
      19 rows in set, 1 warning (0.00 sec)

      うん、誤差!
      プロファイルのDurationを足し合わせると2回目の方がほんの少し小さいのに、"2778 rows in set"のあとの数字は2回目の方が大きいふしぎ!

      そこはそれとして、さらに、アルファベットのみで構成されたトークン以外を全部消してみた。

      mysql56> pager cat > /dev/null
      PAGER set to 'cat > /dev/null'

      mysql56> SELECT * FROM tweets WHERE MATCH(text) AGAINST ('+MySQL' IN BOOLEAN MODE);
      2778 rows in set (0.02 sec)

      mysql56> nopager
      PAGER set to stdout

      mysql56> SHOW PROFILE;
      +--------------------------------+----------+
      | Status | Duration |
      +--------------------------------+----------+
      | starting | 0.000050 |
      | Waiting for query cache lock | 0.000006 |
      | init | 0.000007 |
      | checking query cache for query | 0.000104 |
      | checking permissions | 0.000013 |
      | Opening tables | 0.000055 |
      | init | 0.000055 |
      | System lock | 0.000021 |
      | optimizing | 0.000018 |
      | statistics | 0.000387 |
      | preparing | 0.000032 |
      | FULLTEXT initialization | 0.002243 |
      | executing | 0.000021 |
      | Sending data | 0.018932 |
      | end | 0.000027 |
      | query end | 0.000012 |
      | closing tables | 0.000175 |
      | freeing items | 0.003542 |
      | cleaning up | 0.000046 |
      +--------------------------------+----------+
      19 rows in set, 1 warning (0.00 sec)

      プロファイルのDurationの合計、もともと 27.892ms, ノイズ削除 26.713ms, アルファベット以外全部削除 25.746ms。

      結論、効果はあるんだろうけど(少なくとも16000レコードちょっと、51000トークン程度では)わざわざやる必要なし。

      本番データで試したくなってきました。
      Viewing all 581 articles
      Browse latest View live