2014年11月27日木曜日

MySQL 5.7.5-labsのQuery Rewrite Plugin

Query Rewrite pluginの一番ベーシックな使い方はこちら。
The Query Rewrite Plugins | MySQL Server Blog

MySQL 5.7.5-labsに$basedir/lib/plugin/install_rewriter_plugin.sqlを食わせてやれば取り敢えず有効になる。MySQL 5.7.5-m15にはこのプラグインは存在しないので注意。

$ mysql57 < install_rewriter_plugin.sql

mysql57> SELECT @@version;
+------------------------+
| @@version              |
+------------------------+
| 5.7.5-labs-preview-log |
+------------------------+
1 row in set (0.00 sec)

mysql57> show plugins;
+----------------------------+--------+--------------------------+-------------+---------+
| Name                       | Status | Type                     | Library     | License |
+----------------------------+--------+--------------------------+-------------+---------+
..
| Rewriter                   | ACTIVE | QUERY REWRITE POST PARSE | rewriter.so | GPL     |
+----------------------------+--------+--------------------------+-------------+---------+
43 rows in set (0.00 sec)

mysql57> show databases like 'query_rewrite';
+--------------------------+
| Database (query_rewrite) |
+--------------------------+
| query_rewrite            |
+--------------------------+
1 row in set (0.00 sec)

mysql57> show tables;
+-------------------------+
| Tables_in_query_rewrite |
+-------------------------+
| rewrite_rules           |
+-------------------------+
1 row in set (0.00 sec)


このquery_rewrite.rewrite_rulesにクエリー書き換えのルールを記述していく感じ。
記述したら、query_rewrite.flush_rewrite_rulesを呼んで更新してやる(FLUSH PRIVILEGESみたいな感じだ)

mysql57> INSERT INTO query_rewrite.rewrite_rules (pattern, replacement) VALUES ('INSERT INTO t1 VALUES (?)', 'INSERT INTO t2 VALUES (? + 1)');
Query OK, 1 row affected (0.01 sec)

mysql57> SELECT * FROM query_rewrite.rewrite_rules;
+---------------------------+------------------+-------------------------------+---------+---------+
| pattern                   | pattern_database | replacement                   | enabled | message |
+---------------------------+------------------+-------------------------------+---------+---------+
| INSERT INTO t1 VALUES (?) | NULL             | INSERT INTO t2 VALUES (? + 1) | Y       | NULL    |
+---------------------------+------------------+-------------------------------+---------+---------+
1 row in set (0.00 sec)

mysql57> CALL query_rewrite.flush_rewrite_rules();
Query OK, 1 row affected (0.01 sec)

mysql57> SELECT * FROM query_rewrite.rewrite_rules;
+---------------------------+------------------+-------------------------------+---------+--------------------------------------------------+
| pattern                   | pattern_database | replacement                   | enabled | message                                          |
+---------------------------+------------------+-------------------------------+---------+--------------------------------------------------+
| INSERT INTO t1 VALUES (?) | NULL             | INSERT INTO t2 VALUES (? + 1) | N       | Parse error in pattern: >>No database selected<< |
+---------------------------+------------------+-------------------------------+---------+--------------------------------------------------+
1 row in set (0.00 sec)


あれ、なんか怒られた。 "No database selected" なので、pattern_databaseがNULLなのがいけないのか…ってテーブル名を指定しない時はNULLでも大丈夫ぽい流れが冒頭のブログには書いてあった(´・ω・`)

mysql57> UPDATE query_rewrite.rewrite_rules SET pattern_database= 'd1', enabled= 'Y';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql57> SELECT * FROM query_rewrite.rewrite_rules;
+---------------------------+------------------+-------------------------------+---------+--------------------------------------------------+
| pattern                   | pattern_database | replacement                   | enabled | message                                          |
+---------------------------+------------------+-------------------------------+---------+--------------------------------------------------+
| INSERT INTO t1 VALUES (?) | d1               | INSERT INTO t2 VALUES (? + 1) | Y       | Parse error in pattern: >>No database selected<< |
+---------------------------+------------------+-------------------------------+---------+--------------------------------------------------+
1 row in set (0.00 sec)


enabledは自分で'Y'に戻さないと、次にflush_rewrite_rulesを呼んでもそもそもFLUSHしようとしてくれなくなる(はまった)

mysql57> CALL query_rewrite.flush_rewrite_rules();
Query OK, 1 row affected (0.00 sec)

mysql57> SELECT * FROM query_rewrite.rewrite_rules;
+---------------------------+------------------+-------------------------------+---------+---------------------------------+
| pattern                   | pattern_database | replacement                   | enabled | message                         |
+---------------------------+------------------+-------------------------------+---------+---------------------------------+
| INSERT INTO t1 VALUES (?) | d1               | INSERT INTO t2 VALUES (? + 1) | N       | Pattern not a select statement. |
+---------------------------+------------------+-------------------------------+---------+---------------------------------+
1 row in set (0.00 sec)


(´・ω・`) SELECTステートメントしか食ってくれないようなことを言っている。かなしい。

mysql57> DELETE FROM query_rewrite.rewrite_rules;
Query OK, 1 row affected (0.00 sec)

mysql57> INSERT INTO query_rewrite.rewrite_rules (pattern, pattern_database, replacement) VALUES ('SELECT * FROM t1', 'd1', 'SELECT * FROM t1 LIMIT 1');
Query OK, 1 row affected (0.00 sec)

mysql57> SELECT * FROM query_rewrite.rewrite_rules;
+------------------+------------------+--------------------------+---------+---------+
| pattern          | pattern_database | replacement              | enabled | message |
+------------------+------------------+--------------------------+---------+---------+
| SELECT * FROM t1 | d1               | SELECT * FROM t1 LIMIT 1 | Y       | NULL    |
+------------------+------------------+--------------------------+---------+---------+
1 row in set (0.00 sec)

mysql57> CALL query_rewrite.flush_rewrite_rules();
Query OK, 1 row affected (0.01 sec)

mysql57> SELECT * FROM query_rewrite.rewrite_rules;
+------------------+------------------+--------------------------+---------+---------+
| pattern          | pattern_database | replacement              | enabled | message |
+------------------+------------------+--------------------------+---------+---------+
| SELECT * FROM t1 | d1               | SELECT * FROM t1 LIMIT 1 | Y       | NULL    |
+------------------+------------------+--------------------------+---------+---------+
1 row in set (0.00 sec)


という訳で、d1.t1のテーブルを単純SELECTした時に"LIMIT 1"を押し込むようなやつを作って(まともに読み込まれたときは、messageはNULLのままで見た目変化がないっぽい)

mysql57> SELECT * FROM t1;
+------+
| num  |
+------+
|    1 |
+------+
1 row in set, 1 warning (0.00 sec)

mysql57> SHOW WARNINGS;
+-------+------+---------------------------------------------------------------------------------------+
| Level | Code | Message                                                                               |
+-------+------+---------------------------------------------------------------------------------------+
| Note  | 1105 | Query 'SELECT * FROM t1' rewritten to 'SELECT * FROM t1 LIMIT 1' by plugin: Rewriter. |
+-------+------+---------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

おお、書き換えてくれたぽい。 クエリーキャッシュぽく、クエリーの文字列で判定しているだけぽいけど


mysql57> select * FROM t1;
+------+
| num  |
+------+
|    1 |
+------+
1 row in set, 1 warning (0.00 sec)

mysql57> SELECT /* dummy */  * FROM t1;
+------+
| num  |
+------+
|    1 |
+------+
1 row in set, 1 warning (0.00 sec)

mysql57> SELECT *
    -> FROM t1;
+------+
| num  |
+------+
|    1 |
+------+
1 row in set, 1 warning (0.00 sec)

mysql57> SELECT sql_cache * FROM t1;
+------+
| num  |
+------+
|    1 |
|    2 |
|    3 |
|    4 |
|    5 |
+------+
5 rows in set (0.00 sec)


大文字小文字の吸収と、コメント, スペースや改行文字くらいは無視してくれるっぽい。SQL構文として有効なsql_cacheとか入るとさすがにダメ。簡単なプレースホルダー(?文字)は使えるけど、テーブル名の書き換えとかはできない。

SELECT以外の何かをゴニョゴニョしたかったり、テーブル名の書き換えをやりたかったら、Rewrite Pluginを自作することになるっぽい。
Write Yourself a Query Rewrite Plugin: Part 1 | MySQL Server Blog

取り敢えずrewrite_example(クエリーを全て小文字に書き換える)というのが付属しているので、

mysql57> INSTALL PLUGIN rewrite_example SONAME 'rewrite_example.so';
Query OK, 0 rows affected (0.01 sec)

mysql57> use d1
Database changed, 1 warning

mysql57> INSERT INTO t1 VALUES (100);
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql57> SHOW WARNINGS;
+-------+------+------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                    |
+-------+------+------------------------------------------------------------------------------------------------------------+
| Note  | 1105 | Query 'INSERT INTO t1 VALUES (100)' rewritten to 'insert into t1 values (100)' by plugin: rewrite_example. |
| Note  | 1105 | Query 'SHOW WARNINGS' rewritten to 'show warnings' by plugin: rewrite_example.                             |
+-------+------+------------------------------------------------------------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

うむ、INSERTもSHOWも軒並み全部イケる。こっちはC++で文字列を書き換えてるだけだから結構融通が利く感じ。

Enjoy :)

2014年11月26日水曜日

MySQL Sandboxで./use_allをrootでやりたい

いっつも忘れるのだ!
前に使ってても忘れるのだ!
http://yoku0825.blogspot.jp/2014/11/mysql-fabric_29.html

こたえ: MYCLIENT_OPTIONS

$ ./use_all "select current_user"
# server: 1:
current_user
msandbox@localhost
# server: 2:
current_user
msandbox@localhost
# server: 3:
current_user
msandbox@localhost
# server: 4:
current_user
msandbox@localhost

$ export MYCLIENT_OPTIONS="-uroot"

$ ./use_all "select current_user"
# server: 1:
current_user
root@localhost
# server: 2:
current_user
root@localhost
# server: 3:
current_user
root@localhost
# server: 4:
current_user
root@localhost

2014年11月25日火曜日

MySQL Fabricつらい(FABRIC_OPT_MODE = roでスレーブだけを見に行かせる方法が見つかったと思ったのに失敗した)

FABRIC_OPT_MODE = roはスレーブだけを見に行くわけじゃない の時にはweightにしたがってコネクションがラウンドロビンされるから、スレーブのweightを大きくして誤魔化す的なことをしていたけど、なんかそれっぽいものを見つけた。


$ mysqlfabric group lookup_servers my_first_fabric
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode weight
------------------------------------ --------------- --------- ---------- ------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886   PRIMARY READ_WRITE    1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887 SECONDARY  READ_ONLY    1.0



このmodeの値、"READ_WRITE", "READ_ONLY"が入ってるところの取りうるバリエーションを知りたい。
マニュアルにはread_only, read_write, offlineっぽいのが書いてはあるものの、 正直信用ならないので


$ grep -r READ_WRITE /usr/lib/python2.6/site-packages/mysql/fabric/
/usr/lib/python2.6/site-packages/mysql/fabric/server.py:    :type mode: OFFLINE, READ_ONLY, READ_WRITE.
/usr/lib/python2.6/site-packages/mysql/fabric/server.py:    READ_WRITE = "READ_WRITE"
/usr/lib/python2.6/site-packages/mysql/fabric/server.py:    SERVER_MODE = [OFFLINE, READ_ONLY, WRITE_ONLY, READ_WRITE]
Binary file /usr/lib/python2.6/site-packages/mysql/fabric/server.pyo matches
/usr/lib/python2.6/site-packages/mysql/fabric/services/highavailability.py:    master.mode = _server.MySQLServer.READ_WRITE
Binary file /usr/lib/python2.6/site-packages/mysql/fabric/services/highavailability.pyc matches
/usr/lib/python2.6/site-packages/mysql/fabric/services/server.py:        (_server.MySQLServer.WRITE_ONLY, _server.MySQLServer.READ_WRITE)
Binary file /usr/lib/python2.6/site-packages/mysql/fabric/services/highavailability.pyo matches
Binary file /usr/lib/python2.6/site-packages/mysql/fabric/services/server.pyo matches
Binary file /usr/lib/python2.6/site-packages/mysql/fabric/services/server.pyc matches
Binary file /usr/lib/python2.6/site-packages/mysql/fabric/server.pyc matches

ああ、あるね、マニュアルに載ってないのあるね。Fabricクオリティだもんね。


$ mysqlfabric server set_mode 47cf54df-63fc-11e4-942e-fa163e020fd0 WRITE_ONLY
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                                uuid finished success result
------------------------------------ -------- ------- ------
0389370b-8f9e-4b68-ab2a-75e2940701cb        1       1      1

state success          when                                                   description
----- ------- ------------- -------------------------------------------------------------
    3       2   1.41689e+09 Triggered by <mysql .fabric.events.event="" 0x217fc90="" at="" object="">.
    4       2   1.41689e+09                          Executing action (_set_server_mode).
    5       2   1.41689e+09                           Executed action (_set_server_mode).


$ mysqlfabric group lookup_servers my_first_fabric
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                         server_uuid         address    status       mode weight
------------------------------------ --------------- --------- ---------- ------
47cf54df-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20886   PRIMARY WRITE_ONLY    1.0
4b7036a9-63fc-11e4-942e-fa163e020fd0 127.0.0.1:20887 SECONDARY  READ_ONLY    1.0



セットできた。。


$ less test.cc
    mysql_options(&mysql, FABRIC_OPT_GROUP, "my_first_fabric");
    mysql_options(&mysql, FABRIC_OPT_DEFAULT_MODE, "ro");
    mysql_options4(&mysql, FABRIC_OPT_GROUP_CREDENTIALS, "msandbox", "msandbox");

    for (n= 0; n < 1000; n++)
    {
      //mysql_options(&mysql, FABRIC_OPT_MODE, "ro");
      mysql_query(&mysql, "SELECT @@port, @@read_only");
      res= mysql_store_result(&mysql);
      row = mysql_fetch_row(res);
      std::cout << "port: " << row[0] << ", read_only: " << row[1] << std::endl;
      mysql_free_result(res);
      mysql_query(&mysql, "COMMIT");
    }

$ gcc -I/usr/local/mysql/include -L/usr/local/mysql/lib -lmysqlclient -g test.cc

$ ./a.out | sort | uniq -c
    506 port: 20886, read_only: 0
    494 port: 20887, read_only: 1
      1 Using Fabric for MYSQL connection

だが有効にならないFabricクオリティ!
とはいえ。


$ gdb a.out
gdb> b Fabric_context::pick_connection
gdb> r
gdb> p *m_grp
..
  m_iter = {
    m_grp = 0x6221a0,
    m_srv_list = std::vector of length 2, capacity 2 = {{
        m_host = "127.0.0.1",
        m_port = 20886,
        m_mode = fabric::Server::WRITE_ONLY,
        m_status = fabric::Server::PRIMARY,
        m_weight = 1,
        m_uuid = {
          uuid = "G\317T\337c\374\021\344\224.\372\026>\002\017", 
        },
        m_parent_group = 0x6221a0,
        m_tainted = false
      }, {
        m_host = "127.0.0.1",
        m_port = 20887,
        m_mode = fabric::Server::READ_ONLY,
        m_status = fabric::Server::SECONDARY,
        m_weight = 1,
        m_uuid = {
          uuid = "Kp6\251c\374\021\344\224.\372\026>\002\017", 
        },
        m_parent_group = 0x6221a0,
        m_tainted = false
      }},
..

クライアント側にWRITE_ONLYは渡されてるから、Connector/Cがまだ対応してないってことなのかしら。labsとはいえ同じ会社が作ってるConnector/Cでこれだというのに、PHPのMySQL Fabric対応実装の mysqlnd とか本当に大丈夫かしら。。愛想尽かされない程度にやってくださいね。。

2014年11月18日火曜日

MySQL Fabricつらい(Fabricサーバー上のMySQLプロトコルの口でFabricのAPIが呼べる編)

もともとは、mysqlコマンドラインクライアントにHA切り替え機能を実装する編 でいじってたクライアントにもう少しパラメーターを突っ込もうとか Fabricサーバー上でstatusコマンド叩くとmysqlコマンドラインクライアントがクラッシュする件 を直したいとかそんなことを考えてソースを読んでいたときのこと。

mysql-utilities-1.5.3/mysql/fabric/protocols/mysqlrpc.py のMySQLRPCRequestHandler.handleあたりの中で
 925             # Handle CALL
 926             if CHECK_CALL.match(data):
 927                 call_match = PARSE_CALL.match(data)
 928                 if call_match:
 929                     self._handle_call(call_match)
 930                 else:
 931                     self.send_syntax_error()
 932                 continue
 933
 934             # Handle SHOW CREATE PROCEDURE
 935             if CHECK_SHOW_CREATE_PROC.match(data):
 936                 showproc_match = PARSE_SHOW_CREATE_PROC.match(data)
 937                 if showproc_match:
 938                     self._handle_show_create_procedure(showproc_match)
 939                 else:
 940                     self.send_syntax_error()
 941                 continue
 942
 943             # Handle INFORMATION_SCHEMA.ROUTINES
 944             is_routines_match = PARSE_IS_ROUTINES.match(data)
 945             if is_routines_match:
 946                 self._handle_information_schema_routines(is_routines_match)
 947                 continue
 948
 949             # Handle SET
 950             set_match = PARSE_SET.match(data)
 951             if set_match and set_match.group(1).lower() in ('format',):
 952                 format = dequote(set_match.group(2).strip()).strip().lower()
 953                 if format not in ('json',):
 954                     self.send_error(
 955                         errorcode.ER_LOCAL_VARIABLE,
 956                         "Format '{0}' is not supported".format(format),
 957                         "42000"
 958                     )
 959                 else:
 960                     _LOGGER.debug("Format set to %s", format)
 961                     self._format = format
 962
 963             # We accept anything else without doing anything
 964             self.send_packet(self.ok_packet())


なんか特定の文字列が来た時だけ別ハンドルにしているように見える。
さかのぼってこのPARSE_*の中身を覗いてみると、
  57 # Regular expression getting group, command and the arguments as a string
  58 CHECK_CALL = re.compile(
  59     r"""^CALL\s*""", re.IGNORECASE
  60 )
  61 PARSE_CALL = re.compile(
  62     r"""CALL\s+(\w+)\.(\w+)\s*\((.*)\)"""
  63     r"""(?=(?:[^"']*["'][^"']*["'])*[^"']*$)""", re.IGNORECASE
  64 )
  65 PARSE_CALL_ARGS = re.compile(
  66     r""",(?=(?:[^"']*["'][^"']*["'])*[^"']*$)"""
  67 )
  68 PARSE_CALL_KWARG = re.compile(
  69     r"""=(?=(?:[^"']*["'][^"']*["'])*[^"']*$)"""
  70 )
  71
  72 # Regular expression for SHOW CREATE PROCEDURE
  73 CHECK_SHOW_CREATE_PROC = re.compile(
  74     r"""^SHOW\s+CREATE\s+PROCEDURE\s*""", re.IGNORECASE
  75 )
  76 PARSE_SHOW_CREATE_PROC = re.compile(
  77     r"""SHOW\s+CREATE\s+PROCEDURE\s+(\w+).(\w+)$""", re.IGNORECASE
  78 )
  79
  80 # Regular expression for INFORMATION_SCHEMA.ROUTINES
  81 PARSE_IS_ROUTINES = re.compile(
  82     r"""SELECT\s+(.*)\s+FROM\s+INFORMATION_SCHEMA.ROUTINES\s*(.*)""",
  83     re.IGNORECASE
  84 )
  85
  86 # Regular expression getting SET statements
  87 PARSE_SET = re.compile(
  88     r"""SET\s+(\w+)\s*=(.*)""", re.IGNORECASE
  89 )

このあたりのクエリーは無視せずに返してくれるらしい。

$ client/mysql -P 32275 -u admin -p --protocol=tcp
mysql> SELECT * FROM information_schema.routines;
+----------------------------+-----------------+----------------+----------------------+----------------+
| SPECIFIC_NAME              | ROUTINE_CATALOG | ROUTINE_SCHEMA | ROUTINE_NAME         | ROUTINE_TYPE   |
+----------------------------+-----------------+----------------+----------------------+----------------+
| statistics.node            | fabric          | statistics     | node                 | FABRIC_COMMAND |
| statistics.group           | fabric          | statistics     | group                | FABRIC_COMMAND |
| statistics.procedure       | fabric          | statistics     | procedure            | FABRIC_COMMAND |
| group.activate             | fabric          | group          | activate             | FABRIC_COMMAND |
| group.description          | fabric          | group          | description          | FABRIC_COMMAND |
| group.deactivate           | fabric          | group          | deactivate           | FABRIC_COMMAND |
| group.create               | fabric          | group          | create               | FABRIC_COMMAND |
| group.remove               | fabric          | group          | remove               | FABRIC_COMMAND |
| group.add                  | fabric          | group          | add                  | FABRIC_COMMAND |
| group.health               | fabric          | group          | health               | FABRIC_COMMAND |
| group.lookup_servers       | fabric          | group          | lookup_servers       | FABRIC_COMMAND |
| group.destroy              | fabric          | group          | destroy              | FABRIC_COMMAND |
| group.demote               | fabric          | group          | demote               | FABRIC_COMMAND |
| group.promote              | fabric          | group          | promote              | FABRIC_COMMAND |
| group.lookup_groups        | fabric          | group          | lookup_groups        | FABRIC_COMMAND |
| dump.fabric_nodes          | fabric          | dump           | fabric_nodes         | FABRIC_COMMAND |
| dump.shard_index           | fabric          | dump           | shard_index          | FABRIC_COMMAND |
| dump.sharding_information  | fabric          | dump           | sharding_information | FABRIC_COMMAND |
| dump.servers               | fabric          | dump           | servers              | FABRIC_COMMAND |
| dump.shard_tables          | fabric          | dump           | shard_tables         | FABRIC_COMMAND |
| dump.shard_maps            | fabric          | dump           | shard_maps           | FABRIC_COMMAND |
| manage.teardown            | fabric          | manage         | teardown             | FABRIC_COMMAND |
| manage.stop                | fabric          | manage         | stop                 | FABRIC_COMMAND |
| manage.setup               | fabric          | manage         | setup                | FABRIC_COMMAND |
| manage.ping                | fabric          | manage         | ping                 | FABRIC_COMMAND |
| manage.start               | fabric          | manage         | start                | FABRIC_COMMAND |
| manage.logging_level       | fabric          | manage         | logging_level        | FABRIC_COMMAND |
| server.set_mode            | fabric          | server         | set_mode             | FABRIC_COMMAND |
| server.clone               | fabric          | server         | clone                | FABRIC_COMMAND |
| server.list                | fabric          | server         | list                 | FABRIC_COMMAND |
| server.set_weight          | fabric          | server         | set_weight           | FABRIC_COMMAND |
| server.lookup_uuid         | fabric          | server         | lookup_uuid          | FABRIC_COMMAND |
| server.set_status          | fabric          | server         | set_status           | FABRIC_COMMAND |
| server.destroy             | fabric          | server         | destroy              | FABRIC_COMMAND |
| server.create              | fabric          | server         | create               | FABRIC_COMMAND |
| role.list                  | fabric          | role           | list                 | FABRIC_COMMAND |
| user.roles                 | fabric          | user           | roles                | FABRIC_COMMAND |
| user.usercommand           | fabric          | user           | usercommand          | FABRIC_COMMAND |
| user.list                  | fabric          | user           | list                 | FABRIC_COMMAND |
| user.add                   | fabric          | user           | add                  | FABRIC_COMMAND |
| user.password              | fabric          | user           | password             | FABRIC_COMMAND |
| user.delete                | fabric          | user           | delete               | FABRIC_COMMAND |
| threat.report_error        | fabric          | threat         | report_error         | FABRIC_COMMAND |
| threat.report_failure      | fabric          | threat         | report_failure       | FABRIC_COMMAND |
| provider.unregister        | fabric          | provider       | unregister           | FABRIC_COMMAND |
| provider.register          | fabric          | provider       | register             | FABRIC_COMMAND |
| provider.list              | fabric          | provider       | list                 | FABRIC_COMMAND |
| sharding.list_definitions  | fabric          | sharding       | list_definitions     | FABRIC_COMMAND |
| sharding.remove_definition | fabric          | sharding       | remove_definition    | FABRIC_COMMAND |
| sharding.move_shard        | fabric          | sharding       | move_shard           | FABRIC_COMMAND |
| sharding.disable_shard     | fabric          | sharding       | disable_shard        | FABRIC_COMMAND |
| sharding.remove_table      | fabric          | sharding       | remove_table         | FABRIC_COMMAND |
| sharding.split_shard       | fabric          | sharding       | split_shard          | FABRIC_COMMAND |
| sharding.create_definition | fabric          | sharding       | create_definition    | FABRIC_COMMAND |
| sharding.add_shard         | fabric          | sharding       | add_shard            | FABRIC_COMMAND |
| sharding.add_table         | fabric          | sharding       | add_table            | FABRIC_COMMAND |
| sharding.lookup_table      | fabric          | sharding       | lookup_table         | FABRIC_COMMAND |
| sharding.enable_shard      | fabric          | sharding       | enable_shard         | FABRIC_COMMAND |
| sharding.remove_shard      | fabric          | sharding       | remove_shard         | FABRIC_COMMAND |
| sharding.list_tables       | fabric          | sharding       | list_tables          | FABRIC_COMMAND |
| sharding.prune_shard       | fabric          | sharding       | prune_shard          | FABRIC_COMMAND |
| sharding.lookup_servers    | fabric          | sharding       | lookup_servers       | FABRIC_COMMAND |
| snapshot.destroy           | fabric          | snapshot       | destroy              | FABRIC_COMMAND |
| snapshot.create            | fabric          | snapshot       | create               | FABRIC_COMMAND |
| event.trigger              | fabric          | event          | trigger              | FABRIC_COMMAND |
| event.wait_for_procedures  | fabric          | event          | wait_for_procedures  | FABRIC_COMMAND |
+----------------------------+-----------------+----------------+----------------------+----------------+
66 rows in set (0.00 sec)


おお、ホントだ。
これ、バッキングストアやファームのMySQLサーバーの上ではなくFabricサーバーの上なので、たとえば他のテーブルとかは存在しない。というかたぶんクエリーを握りつぶしてる。

mysql> SELECT * FROM information_schema.tables;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW DATABASES;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL VARIABLES;
Query OK, 0 rows affected (0.00 sec)


SHOW CREATE PROCEDUREでプロシージャを見ると、

mysql> SHOW CREATE PROCEDURE group.health\G
*************************** 1. row ***************************
fabric_help: group health group_id

Check if any server within a group has failed and report health
information.

It returns a dictionary where keys are the servers' uuids and the
values are dictionaries which have the following keys:

* is_alive - whether it is possible to access the server or not.     *
status - PRIMARY, SECONDARY, SPARE or FAULTY.     * threads -
Information on the replication threads.
1 row in set (0.00 sec)


簡単なusageが表示される。

mysql> CALL group.health();
+--------------------------------------+-----+-----------------------------------------------+
| fabric_uuid                          | ttl | message                                       |
+--------------------------------------+-----+-----------------------------------------------+
| 5ca1ab1e-a007-feed-f00d-cab3fe13249e |   1 | execute() takes exactly 2 arguments (1 given) |
+--------------------------------------+-----+-----------------------------------------------+
1 row in set (0.01 sec)


ありゃ、引数の数が足りないらしい。や、それこそusageのSHOW CREATE PROCEDUREに書けよ。
取り敢えずたぶんFabric Groupが足りないんだと思うので渡してみようか。

mysql> CALL group.health('my_first_fabric');
+--------------------------------------+-----+---------+
| fabric_uuid                          | ttl | message |
+--------------------------------------+-----+---------+
| 5ca1ab1e-a007-feed-f00d-cab3fe13249e |   1 | NULL    |
+--------------------------------------+-----+---------+
1 row in set (0.14 sec)

+--------------------------------------+----------+-----------+----------------+-------------------+----------------+-----------------+----------+-----------+
| uuid                                 | is_alive | status    | is_not_running | is_not_configured | io_not_running | sql_not_running | io_error | sql_error |
+--------------------------------------+----------+-----------+----------------+-------------------+----------------+-----------------+----------+-----------+
| 47cf54df-63fc-11e4-942e-fa163e020fd0 |        1 | PRIMARY   |              0 |                 0 |              0 |               0 | False    | False     |
| 4b7036a9-63fc-11e4-942e-fa163e020fd0 |        1 | SECONDARY |              0 |                 0 |              0 |               0 | False    | False     |
+--------------------------------------+----------+-----------+----------------+-------------------+----------------+-----------------+----------+-----------+
2 rows in set (0.14 sec)

Empty set (0.14 sec)


うむ。
こんなこともできるんだ、ってことは認識したんだが、

ドキュメントはよ

MySQL Fabricつらい(AWS連携を試したかったけど挫折した編)

MySQL Utilities 1.5.3からMySQL FabricからAWSが叩けるよ! という噂があったので、1週間半くらいがんばっていたメモ。結論先に言うと、1.5.3では使えない気がする。
http://dev.mysql.com/doc/relnotes/mysql-utilities/en/wb-utils-news-1-5-3.html

この記事は2014/11/18現在の情報を元に書かれているので、「ちゃんと情報書いてあるんじゃん」という未来が来るかも知れません。というか来てくれないと困る。ドキュメントちゃんと書いてくれマジで。

まず、"MySQL Fabric AWS"や"MySQL Fabric EC2"でググっても何も出てこない。これがFabricクオリティ。
かろうじてOracle Open World 2014の "Elastic Scalability in MySQL Fabric with OpenStack" の資料に(教えてもらって)辿り着くものの、

Experiments with Amazon AWS
mysqlfabric provider register my_amazon
   AKIAIOSFODNN7EXAMPLE wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
   eu­west­1 ­­provider_type=AMAZON
mysqlfabric machine create my_amazon
   ­­image image­id=ami­892fe1fe
   ­­flavor name=t2.micro

Note!
Not in MySQL Fabric 1.5
Will be on MySQL Lab
ノートの部分がリリースノートと矛盾しててなんかどうなのかって感じがするけれど、providerサブコマンドを使うらしい。

$ mysqlfabric help provider

Commands available in group 'provider' are:
    provider unregister provider_id  [--synchronous]
    provider register provider_id username password url  [--tenant=NONE] [--provider_type=OPENSTACK] [--default_image=NONE] [--default_flavor=NONE] [--extra=NONE] [--synchronous]
    provider list  [--provider_id=NONE]


うん、構文だけ教えてもらっても、肝心な「何を指定すればいいのか」が全くわからない。
そんな時はマニュアル嫁ですよねわかりますん。

http://dev.mysql.com/doc/mysql-utilities/1.5/en/fabric-util.html

(  д ) ゚  ゚ 載ってねええええええ! そもそもproviderコマンドが載ってねええええええ!

さすがのFabricクオリティなので手探りでproviderコマンドを叩いてみる。

$ mysqlfabric provider list
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

provider_id type username url tenant default_image default_flavor extra
----------- ---- -------- --- ------ ------------- -------------- -----


$ mysqlfabric provider register my_amazon AKIAIOSFODNN7EXAMPLE wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY eu-west-1 --provider_type=AMAZON
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

ProviderError: Provider type (AMAZON) is not supported yet.




あ、ああ、うん、まだサポートされてないのね。うん。さすがFabricクオリティ。リリースノートなんて知ったこっちゃねぇぜ。

( ゚д゚) えっ

しかし更に待ち構える驚きのFabricクオリティ。
じゃあOPENSTACK(暗黙のデフォルト)ならサポートしてるのかっていうと

$ mysqlfabric provider register my_provider provider_user_name provider_passsword provider_url
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

ProviderError: Provider type (OPENSTACK) is not supported yet.



ちょwwwwwおまwwwwwデフォルトくらいサポートしろwwwww

なお、同じスライドの別のコマンドをコピペすると

$ mysqlfabric provider register my_stack mats xyzzy http://example.net:5000/v2.0/ my_project --provider_type=OPENSTACK
Password for admin:
Usage:  provider register provider_id username password url  [--tenant=NONE] [--provider_type=OPENSTACK] [--default_image=NONE] [--default_flavor=NONE] [--extra=NONE] [--synchronous]

mysqlfabric: error: Wrong number of parameters were provided for command 'provider register'.

ちょwwwww引数の数がおかしいってwwwww
まあほらこのスライド、Oracle Open World 2014のセッションのやつですからねー。。アテになんなくても仕方ないかしらFabricクオリティだし。

( ゚д゚) えっ

取り敢えず db tech showcase 2014 東京 に来ていた 日本一MySQL Fabricに詳しそうな人 を捕まえてみたところ、開発チームに問い合わせてくれました。

返ってきた返答は
You need to install the OpenStack libraries;
- python-novaclient for Compute node
- python-neutronclient for Network node
だそうで、


$ sudo pip install python-novaclient python-neutronclient
$ mysqlfabric manage stop
$ mysqlfabric manage start --daemonize

$ mysqlfabric provider register my_provider provider_user_name provider_passsword provider_url
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

                                uuid finished success result
------------------------------------ -------- ------- ------
dae961a0-4ec2-4e40-b554-88cb284a5a02        1       1      1

state success          when                                                   description
----- ------- ------------- -------------------------------------------------------------
    3       2   1.41628e+09 Triggered by <mysql.fabric.events.Event object at 0x260ebd0>.
    4       2   1.41628e+09                        Executing action (_register_provider).
    5       2   1.41628e+09                         Executed action (_register_provider).


$ mysqlfabric provider list
Password for admin:
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1

provider_id      type           username          url tenant default_image default_flavor extra
----------- --------- ------------------ ------------ ------ ------------- -------------- -----
my_provider OPENSTACK provider_user_name provider_url   None          None           None  None




取り敢えず、not supported yetだけは回避できた模様。

:(;゙゚'ω゚'): なんかドキュメント書けよ。。せめてエラーに出せよ。。

ここまでで1週間半。

しかしなんかどうも、

$ ll /usr/lib/python2.6/site-packages/mysql/fabric/providers
total 96
-rw-r--r-- 1 root root 10088 Oct 21 20:13 __init__.py
-rw-r--r-- 2 root root 12202 Oct 22 01:32 __init__.pyc
-rw-r--r-- 2 root root 12202 Oct 22 01:32 __init__.pyo
-rw-r--r-- 1 root root  2951 Oct 21 20:13 null.py
-rw-r--r-- 2 root root  3873 Oct 22 01:32 null.pyc
-rw-r--r-- 2 root root  3873 Oct 22 01:32 null.pyo
-rw-r--r-- 1 root root 14317 Oct 21 20:13 openstack.py
-rw-r--r-- 2 root root 13481 Oct 22 01:32 openstack.pyc
-rw-r--r-- 2 root root 13481 Oct 22 01:32 openstack.pyo


ここを見てる限り、OpenStack以外はまだ実装されてないんじゃないかなぁって気がする。。また今度。

2014年11月17日月曜日

topライクにperformance_schemaを眺めるらしいpstopとやら

pstop – a top-like program for MySQL (based on performance_schema) を見て試してみたメモ。

$ git clone https://github.com/sjmudd/pstop.git
$ cd pstop
$ ll
total 52
-rw-rw-r-- 1 ec2-user ec2-user  710 Nov 17 10:03 BUGS
-rw-rw-r-- 1 ec2-user ec2-user  738 Nov 17 10:03 keys.txt
drwxrwxr-x 2 ec2-user ec2-user 4096 Nov 17 10:03 lib
-rw-rw-r-- 1 ec2-user ec2-user 1315 Nov 17 10:03 LICENSE
-rw-rw-r-- 1 ec2-user ec2-user 3905 Nov 17 10:03 main.go
-rw-rw-r-- 1 ec2-user ec2-user 1986 Nov 17 10:03 NEW_FEATURES
drwxrwxr-x 6 ec2-user ec2-user 4096 Nov 17 10:03 performance_schema
-rw-rw-r-- 1 ec2-user ec2-user 1041 Nov 17 10:03 README.md
drwxrwxr-x 2 ec2-user ec2-user 4096 Nov 17 10:03 screen
-rw-rw-r-- 1 ec2-user ec2-user 5551 Nov 17 10:03 screen_samples.txt
drwxrwxr-x 2 ec2-user ec2-user 4096 Nov 17 10:03 state
drwxrwxr-x 2 ec2-user ec2-user 4096 Nov 17 10:03 version

おい、こいつgoだぞ…:(;゙゚'ω゚'): go弱な俺には起動の仕方すらわからない。。


$ go build
main.go:15:2: cannot find package "github.com/go-sql-driver/mysql" in any of:
        /usr/lib/golang/src/pkg/github.com/go-sql-driver/mysql (from $GOROOT)
        ($GOPATH not set)
main.go:16:2: cannot find package "github.com/nsf/termbox-go" in any of:
        /usr/lib/golang/src/pkg/github.com/nsf/termbox-go (from $GOROOT)
        ($GOPATH not set)
main.go:18:2: cannot find package "github.com/sjmudd/mysql_defaults_file" in any of:
        /usr/lib/golang/src/pkg/github.com/sjmudd/mysql_defaults_file (from $GOROOT)
        ($GOPATH not set)
main.go:19:2: cannot find package "github.com/sjmudd/pstop/lib" in any of:
        /usr/lib/golang/src/pkg/github.com/sjmudd/pstop/lib (from $GOROOT)
        ($GOPATH not set)
main.go:20:2: cannot find package "github.com/sjmudd/pstop/state" in any of:
        /usr/lib/golang/src/pkg/github.com/sjmudd/pstop/state (from $GOROOT)
        ($GOPATH not set)
main.go:21:2: cannot find package "github.com/sjmudd/pstop/version" in any of:
        /usr/lib/golang/src/pkg/github.com/sjmudd/pstop/version (from $GOROOT)
        ($GOPATH not set)

むーん。


$ export GOPATH=~/gopath
$ go get
go install: no install location for directory /home/ec2-user/pstop outside GOPATH
$ go build

よくわかってないけどこれでビルドできた。


$ ./pstop
2014/11/17 10:06:11 Could not load ini fileopen /home/ec2-user/.my.cnf: no such file or directory

どうも~/.my.cnfを読み込んで接続情報を取るらしい。


$ cat ~/.my.cnf
[client]
user= msandbox
password= msandbox
port= 5621
host= 127.0.0.1

$ ./pstop

起動できた。
TABキーを叩くとtable_io_waits_summary_by_table, table_io_waits_summary_by_table, file_summary_by_instance, table_lock_waits_summary_by_tableのタブを順繰りに表示してくれる。
"t"でp_sが収集し始めてからの累計と、最後に統計情報をリセット(たぶん、pstopの中で。いちども"z"を叩いてなければ、pstopが起動したときから)の累計切り替え。
"+", "-"で表示間隔の調整。"h"でヘルプだけど、ヘルプから戻るのはもう一度"h"。罠い。

今のところは荒削りにこれで機能が全てっぽい。innotopばりに発展していってくれるととてもうれしい。

Mroonga + MySQL 5.6とMariaDB 10.0だとスピードが結構違う?

あんま真面目に調べてないんですがメモ。


mysql> SELECT COUNT(*) FROM t1 WHERE MATCH(text_column) AGAINST('*D+ xxx' IN BOOLEAN MODE);
+----------+
| COUNT(*) |
+----------+
|    45402 |
+----------+
1 row in set (0.03 sec)

MariaDB [d1]> SELECT COUNT(*) FROM t1 WHERE MATCH(text_column) AGAINST('*D+ xxx' IN BOOLEAN MODE);
+----------+
| COUNT(*) |
+----------+
|    45402 |
+----------+
1 row in set (0.06 sec)

なんか倍くらい違う。


MariaDB [d1]> SHOW PROFILE cpu for query 1;
+-------------------------+----------+----------+------------+
| Status                  | Duration | CPU_user | CPU_system |
+-------------------------+----------+----------+------------+
| starting                | 0.000109 | 0.000000 |   0.000000 |
| checking permissions    | 0.000009 | 0.000000 |   0.000000 |
| Opening tables          | 0.000032 | 0.000000 |   0.000000 |
| After opening tables    | 0.000010 | 0.000000 |   0.000000 |
| System lock             | 0.000008 | 0.000000 |   0.000000 |
| Table lock              | 0.000008 | 0.000000 |   0.001000 |
| After table lock        | 0.000019 | 0.000000 |   0.000000 |
| init                    | 0.000045 | 0.000000 |   0.000000 |
| optimizing              | 0.000347 | 0.000000 |   0.000000 |
| statistics              | 0.000200 | 0.000000 |   0.000000 |
| preparing               | 0.000028 | 0.000000 |   0.000000 |
| FULLTEXT initialization | 0.040459 | 0.027995 |   0.014998 |
| executing               | 0.000024 | 0.000000 |   0.000000 |
| Sending data            | 0.020768 | 0.019997 |   0.000000 |
| end                     | 0.000011 | 0.000000 |   0.000000 |
| query end               | 0.000006 | 0.000000 |   0.000000 |
| closing tables          | 0.000839 | 0.000000 |   0.001000 |
| freeing items           | 0.000012 | 0.000000 |   0.000000 |
| updating status         | 0.000025 | 0.000000 |   0.000000 |
| cleaning up             | 0.000008 | 0.000000 |   0.000000 |
+-------------------------+----------+----------+------------+
20 rows in set (0.00 sec)

mysql> SHOW PROFILE cpu for query 2;
+----------------------+----------+----------+------------+
| Status               | Duration | CPU_user | CPU_system |
+----------------------+----------+----------+------------+
| starting             | 0.000057 | 0.000000 |   0.000000 |
| checking permissions | 0.000009 | 0.000000 |   0.000000 |
| Opening tables       | 0.000016 | 0.000000 |   0.000000 |
| init                 | 0.000018 | 0.000000 |   0.000000 |
| System lock          | 0.000007 | 0.000000 |   0.000000 |
| optimizing           | 0.031675 | 0.094985 |   0.011998 |
| executing            | 0.000027 | 0.000000 |   0.000000 |
| end                  | 0.000007 | 0.000000 |   0.000000 |
| query end            | 0.000005 | 0.000000 |   0.000000 |
| closing tables       | 0.000255 | 0.000000 |   0.000000 |
| freeing items        | 0.000344 | 0.002000 |   0.001000 |
| cleaning up          | 0.000005 | 0.000000 |   0.000000 |
+----------------------+----------+----------+------------+
12 rows in set, 1 warning (0.00 sec)

MariaDB 10.0はMySQL 5.5の時と同じような感じで、FULLTEXT initializationとSending Dataがほとんどの時間を占めてる。MySQL 5.6はoptimizingだけに集中している。これは、計上するステージが変わったからなのかなで済むけど、CPU_userを見るとMariaDBでは全て実時間以下のCPU使用時間に対し、MySQL 5.6のoptimizingステージは実時間の3倍くらいのCPU使用時間になっている。

MySQL 5.6、このへんなんかチューニングしてる? それともPROFILINGでやってるから取れてない(MySQL 5.6ではPROFILING非推奨でp_s使えってことになってるから)だけ?


【2014/11/17 16:10】
お客様の中に詳しい方がいらっしゃいました :)




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

mysql> SELECT COUNT(*) FROM t1 WHERE MATCH(text_column) AGAINST('*D+ xxx' IN BOOLEAN MODE);
+----------+
| COUNT(*) |
+----------+
|    45402 |
+----------+
1 row in set (0.04 sec)

確かにMySQL 5.6ではenable_optimization切ってもそんなに変わらない(= Mroongaのoptimizationでなく、MySQLのFTのレイヤーでごにょごにょしてMroongaのAPIを叩いてる?)


MariaDB [d1]> SET SESSION mroonga_enable_optimization= OFF;
Query OK, 0 rows affected (0.00 sec)

MariaDB [d1]> SELECT COUNT(*) FROM t1 WHERE MATCH(search_text) AGAINST('*D+ xxx' IN BOOLEAN MODE);
+----------+
| COUNT(*) |
+----------+
|    45402 |
+----------+
1 row in set (0.22 sec)

MariaDB 10.0はごりっと落ちる。