MySQLで疑似的なマテリアライズド・ビューを作成

MySQLにはOracleで利用可能な、マテリアライズド・ビューは実装されてません。
その為、トリガーで対応したり、ワークテーブルを作成しておいて、定期的にデータを入れ替えるような処理で対応する必要があります。

過去の経験では、デイリー、ウイークリーランキングを作成する為に、以下のようにMySQLのEVENT SCHEDULE機能とREPLACE 構文を利用して変更されたデータを
定期的に入れ替える処理を行ないマテリアライズド・ビューの代わりに利用していました。
イベントは、イベントを作成してスケジュールします。但し、イベントスケジューラが有効になっていないかぎり実行されません。

メモ:EVENTの代わりに、LinuxのcronやWindows Task Schedulerでも良いかと思います。

■ 検証用にテーブルの作成
ここでは、City_MasterがオリジナルでCity_SnapShotはSNAPコピーとしています。
オリジナルのデータはMySQLのオフィシャルサイトからダウンロードしたWorldデータベースのCityテーブルのデータを利用しています。

root@localhost [world2]> CREATE TABLE `City_Master` (
    ->   `ID` int(11) NOT NULL AUTO_INCREMENT,
    ->   `Name` char(35) NOT NULL DEFAULT '',
    ->   `CountryCode` char(3) NOT NULL DEFAULT '',
    ->   `District` char(20) NOT NULL DEFAULT '',
    ->   `Population` int(11) NOT NULL DEFAULT '0',
    ->   `test` varchar(255) DEFAULT NULL,
    ->   PRIMARY KEY (`ID`),
    ->   KEY `idx_CountryCode` (`CountryCode`),
    ->   KEY `idx_City2_Covering` (`ID`,`Name`)
    -> ) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.08 sec)

root@localhost [world2]> insert into City_Master select * from City;
Query OK, 4079 rows affected (0.15 sec)
Records: 4079  Duplicates: 0  Warnings: 0


root@localhost [world2]> CREATE TABLE `City_SnapShot` (
    ->   `ID` int(11) NOT NULL AUTO_INCREMENT,
    ->   `Name` char(35) NOT NULL DEFAULT '',
    ->   `CountryCode` char(3) NOT NULL DEFAULT '',
    ->   `District` char(20) NOT NULL DEFAULT '',
    ->   `Population` int(11) NOT NULL DEFAULT '0',
    ->   `test` varchar(255) DEFAULT NULL,
    ->   PRIMARY KEY (`ID`),
    ->   KEY `idx_CountryCode` (`CountryCode`),
    ->   KEY `idx_City2_Covering` (`ID`,`Name`)
    -> ) ENGINE=InnoDB AUTO_INCREMENT=4080 DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.02 sec)

root@localhost [world2]> 

root@localhost [world2]> REPLACE INTO City_SnapShot select ID,Name,CountryCode,District,Population,test from City_Master;
Query OK, 4079 rows affected (0.19 sec)
Records: 4079  Duplicates: 0  Warnings: 0

root@localhost [world2]> select * from City_Master where ID = 100;
+-----+---------+-------------+------------+------------+------+
| ID  | Name    | CountryCode | District   | Population | test |
+-----+---------+-------------+------------+------------+------+
| 100 | Paraná  | ARG         | Entre Rios |     207041 | NULL |
+-----+---------+-------------+------------+------------+------+
1 row in set (0.00 sec)

root@localhost [world2]> select * from City_SnapShot where ID = 100;
+-----+---------+-------------+------------+------------+------+
| ID  | Name    | CountryCode | District   | Population | test |
+-----+---------+-------------+------------+------------+------+
| 100 | Paraná  | ARG         | Entre Rios |     207041 | NULL |
+-----+---------+-------------+------------+------------+------+
1 row in set (0.00 sec)


■ マスターテーブルでデータを変更してみます。(207041->207045)した後に、
REPLACEコマンドでSnapShotテーブルへ変更を反映してみます。

メモ:PKが同じであれば、データを問題無くリプレースするかどうかの動作確認。

root@localhost [world2]> update City_Master set Population = 207045 where ID = 100;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

root@localhost [world2]> select * from City_Master where ID = 100;
+-----+---------+-------------+------------+------------+------+
| ID  | Name    | CountryCode | District   | Population | test |
+-----+---------+-------------+------------+------------+------+
| 100 | Paraná  | ARG         | Entre Rios |     207045 | NULL |
+-----+---------+-------------+------------+------------+------+
1 row in set (0.00 sec)

root@localhost [world2]> select * from City_SnapShot where ID = 100;
+-----+---------+-------------+------------+------------+------+
| ID  | Name    | CountryCode | District   | Population | test |
+-----+---------+-------------+------------+------------+------+
| 100 | Paraná  | ARG         | Entre Rios |     207041 | NULL |
+-----+---------+-------------+------------+------------+------+
1 row in set (0.00 sec)


root@localhost [world2]> REPLACE INTO City_SnapShot select ID,Name,CountryCode,District,Population,test from City_Master;         
Query OK, 4080 rows affected (0.10 sec)
Records: 4079  Duplicates: 1  Warnings: 0

root@localhost [world2]> select * from City_Master where ID = 100;
+-----+---------+-------------+------------+------------+------+
| ID  | Name    | CountryCode | District   | Population | test |
+-----+---------+-------------+------------+------------+------+
| 100 | Paraná  | ARG         | Entre Rios |     207045 | NULL |
+-----+---------+-------------+------------+------------+------+
1 row in set (0.01 sec)

root@localhost [world2]> select * from City_SnapShot where ID = 100;
+-----+---------+-------------+------------+------------+------+
| ID  | Name    | CountryCode | District   | Population | test |
+-----+---------+-------------+------------+------------+------+
| 100 | Paraná  | ARG         | Entre Rios |     207045 | NULL |
+-----+---------+-------------+------------+------------+------+
1 row in set (0.00 sec)

root@localhost [world2]> 

■ ここから、EVENTを有効にして定期的にデータが自動反映されるスケジュールを設定します。
オプションファイルへ追加


event-scheduler=ON

■ オプションファイルへの設定変更が反映されているかどうか確認。
メモ:SETコマンドで、動的にも設定可能。
event_schedulerが実行されている事を確認したら、EVENTを作成しスケジュールを設定します。


root@localhost [world2]> show variables like 'event%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| event_scheduler | ON    |
+-----------------+-------+
1 row in set (0.00 sec)

root@localhost [world2]> show processlist;
+----+-----------------+-----------+--------+---------+------+------------------------+------------------+
| Id | User            | Host      | db     | Command | Time | State                  | Info             |
+----+-----------------+-----------+--------+---------+------+------------------------+------------------+
|  1 | event_scheduler | localhost | NULL   | Daemon  |   53 | Waiting on empty queue | NULL             |
|  4 | root            | localhost | world2 | Query   |    0 | starting               | show processlist |
+----+-----------------+-----------+--------+---------+------+------------------------+------------------+
2 rows in set (0.01 sec)

root@localhost [world2]> 



root@localhost [world2]> CREATE EVENT SNAP_SHOT_VIEW_City_Master_City_SnapShot
    ->    ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
    ->    DO 
    ->      REPLACE INTO City_SnapShot select ID,Name,CountryCode,District,Population,test from City_Master;
Query OK, 0 rows affected (0.00 sec)

root@localhost [world2]> show events;
+--------+------------------------------------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+---------+------------+----------------------+----------------------+--------------------+
| Db     | Name                                     | Definer        | Time zone | Type     | Execute at          | Interval value | Interval field | Starts | Ends | Status  | Originator | character_set_client | collation_connection | Database Collation |
+--------+------------------------------------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+---------+------------+----------------------+----------------------+--------------------+
| world2 | SNAP_SHOT_VIEW_City_Master_City_SnapShot | root@localhost | SYSTEM    | ONE TIME | 2016-06-10 15:23:01 | NULL           | NULL           | NULL   | NULL | ENABLED |          1 | utf8                 | utf8_general_ci      | utf8mb4_general_ci |
+--------+------------------------------------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+---------+------------+----------------------+----------------------+--------------------+
1 row in set (0.00 sec)

root@localhost [world2]> SELECT * FROM information_schema.EVENTS\G
*************************** 1. row ***************************
       EVENT_CATALOG: def
        EVENT_SCHEMA: world2
          EVENT_NAME: SNAP_SHOT_VIEW_City_Master_City_SnapShot
             DEFINER: root@localhost
           TIME_ZONE: SYSTEM
          EVENT_BODY: SQL
    EVENT_DEFINITION: REPLACE INTO City_SnapShot select ID,Name,CountryCode,District,Population,test from City_Master
          EVENT_TYPE: ONE TIME
          EXECUTE_AT: 2016-06-10 15:23:01
      INTERVAL_VALUE: NULL
      INTERVAL_FIELD: NULL
            SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
              STARTS: NULL
                ENDS: NULL
              STATUS: ENABLED
       ON_COMPLETION: NOT PRESERVE
             CREATED: 2016-06-10 14:23:01
        LAST_ALTERED: 2016-06-10 14:23:01
       LAST_EXECUTED: NULL
       EVENT_COMMENT: 
          ORIGINATOR: 1
CHARACTER_SET_CLIENT: utf8
COLLATION_CONNECTION: utf8_general_ci
  DATABASE_COLLATION: utf8mb4_general_ci
1 row in set (0.00 sec)

root@localhost [world2]> 



■ イベントを設定し終わったので、データを変更すれば、
次回のスケジュール(2016-06-10 15:23:01)にはデータが変更される事になります。

データを一件変更してみます。

root@localhost [world2]> select * from City_Master where ID = 1;
+----+-------+-------------+----------+------------+------+
| ID | Name  | CountryCode | District | Population | test |
+----+-------+-------------+----------+------------+------+
|  1 | Kabul | AFG         | Kabol    |    1780000 | NULL |
+----+-------+-------------+----------+------------+------+
1 row in set (0.00 sec)

root@localhost [world2]> select * from City_SnapShot where ID = 1;
+----+-------+-------------+----------+------------+------+
| ID | Name  | CountryCode | District | Population | test |
+----+-------+-------------+----------+------------+------+
|  1 | Kabul | AFG         | Kabol    |    1780000 | NULL |
+----+-------+-------------+----------+------------+------+
1 row in set (0.00 sec)

root@localhost [world2]> update City_Master set Population = 1780001 where ID = 1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

■ データ反映の確認
2016-06-10 15:23:01にEVENTが実行されたので、
SnapShot側のテーブルのデータも変更されている事が確認出来ます。


root@localhost [world2]> select *,now() from City_Master where ID = 1;
+----+-------+-------------+----------+------------+------+---------------------+
| ID | Name  | CountryCode | District | Population | test | now()               |
+----+-------+-------------+----------+------------+------+---------------------+
|  1 | Kabul | AFG         | Kabol    |    1780001 | NULL | 2016-06-10 14:25:30 |
+----+-------+-------------+----------+------------+------+---------------------+
1 row in set (0.00 sec)

root@localhost [world2]> select *,now() from City_SnapShot where ID = 1;
+----+-------+-------------+----------+------------+------+---------------------+
| ID | Name  | CountryCode | District | Population | test | now()               |
+----+-------+-------------+----------+------------+------+---------------------+
|  1 | Kabul | AFG         | Kabol    |    1780000 | NULL | 2016-06-10 14:25:36 |
+----+-------+-------------+----------+------------+------+---------------------+
1 row in set (0.00 sec)

root@localhost [world2]> 


root@localhost [world2]> select *,now() from City_SnapShot where ID = 1;
+----+-------+-------------+----------+------------+------+---------------------+
| ID | Name  | CountryCode | District | Population | test | now()               |
+----+-------+-------------+----------+------------+------+---------------------+
|  1 | Kabul | AFG         | Kabol    |    1780001 | NULL | 2016-06-10 15:25:16 |
+----+-------+-------------+----------+------------+------+---------------------+
1 row in set (0.00 sec)

root@localhost [world2]> 

■ 集計表の場合
まずは、JPNの人口を合計したテーブルを作成してデータを入力しておく。
メモ:テーブルにはPKが必要です。
メモ:実際の人口は1億2000万を超えています。

root@localhost [world2]> select CountryCode,SUM(Population) from City_Master where CountryCode='JPN' group by CountryCode;
+-------------+-----------------+
| CountryCode | SUM(Population) |
+-------------+-----------------+
| JPN         |        77965107 |
+-------------+-----------------+


root@localhost [world2]> create table City_Summary (
    -> ID int(11) NOT NULL, 
    -> CountryCode char(3) NOT NULL DEFAULT '',
    -> Population int(11) NOT NULL DEFAULT '0',
    -> PRIMARY KEY (`ID`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.02 sec)


root@localhost [world2]> REPLACE INTO City_Summary select 1,CountryCode,SUM(Population) from City_Master where CountryCode='JPN' group by CountryCode;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

root@localhost [world2]> select * from City_Summary;
+----+-------------+------------+
| ID | CountryCode | Population |
+----+-------------+------------+
|  1 | JPN         |   77965107 |
+----+-------------+------------+
1 row in set (0.00 sec)

root@localhost [world2]> 

■ 集計表へのデータ反映
東京の人口を10名増やして、EVENTを作成してスケジュールを設定しデータが反映されるか確認してみます。

root@localhost [world2]> update City_Master set Population = 7980240 where Name = 'tokyo';
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

root@localhost [world2]> select * from City_Summary;
+----+-------------+------------+
| ID | CountryCode | Population |
+----+-------------+------------+
|  1 | JPN         |   77965107 |
+----+-------------+------------+
1 row in set (0.00 sec)

root@localhost [world2]> CREATE EVENT SNAP_SHOT_VIEW_City_Master_City_Summary
    ->     ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 10 MINUTE 
    ->     DO 
    ->     REPLACE INTO City_Summary select 1,CountryCode,SUM(Population) from City_Master where CountryCode='JPN' group by CountryCode;
Query OK, 0 rows affected (0.00 sec)

root@localhost [world2]> 



root@localhost [world2]> SELECT * FROM information_schema.EVENTS 
    -> where EVENT_NAME = 'SNAP_SHOT_VIEW_City_Master_City_Summary'\G
*************************** 1. row ***************************
       EVENT_CATALOG: def
        EVENT_SCHEMA: world2
          EVENT_NAME: SNAP_SHOT_VIEW_City_Master_City_Summary
             DEFINER: root@localhost
           TIME_ZONE: SYSTEM
          EVENT_BODY: SQL
    EVENT_DEFINITION: REPLACE INTO City_Summary select 1,CountryCode,SUM(Population) from City_Master where CountryCode='JPN' group by CountryCode
          EVENT_TYPE: ONE TIME
          EXECUTE_AT: 2016-06-10 15:07:09
      INTERVAL_VALUE: NULL
      INTERVAL_FIELD: NULL
            SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
              STARTS: NULL
                ENDS: NULL
              STATUS: ENABLED
       ON_COMPLETION: NOT PRESERVE
             CREATED: 2016-06-10 14:57:09
        LAST_ALTERED: 2016-06-10 14:57:09
       LAST_EXECUTED: NULL
       EVENT_COMMENT: 
          ORIGINATOR: 1
CHARACTER_SET_CLIENT: utf8
COLLATION_CONNECTION: utf8_general_ci
  DATABASE_COLLATION: utf8mb4_general_ci
1 row in set (0.01 sec)

root@localhost [world2]> 

■ 集計データ結果確認
設定時間に確認してみると、集計データが反映されている事が確認出来ました。

root@localhost [world2]> select *,now() from City_Summary;
+----+-------------+------------+---------------------+
| ID | CountryCode | Population | now()               |
+----+-------------+------------+---------------------+
|  1 | JPN         |   77965107 | 2016-06-10 14:59:26 |
+----+-------------+------------+---------------------+
1 row in set (0.00 sec)

root@localhost [world2]> select *,now() from City_Summary;
+----+-------------+------------+---------------------+
| ID | CountryCode | Population | now()               |
+----+-------------+------------+---------------------+
|  1 | JPN         |   77965117 | 2016-06-10 15:07:31 |
+----+-------------+------------+---------------------+
1 row in set (0.00 sec)

root@localhost [world2]> 

■ 継続的、且つ定期的にEVENTを実行したい場合
上記の例は、1回のみ実行する方法でしたが、
一定間隔で,継続して定期的に実行するには、以下の様にEVENTを作ると良いです。


root@localhost [world2]> CREATE EVENT SNAP_SHOT_VIEW_City_Master_City_Summary
    -> ON SCHEDULE 
    -> EVERY 10 MINUTE 
    -> COMMENT 'サマリーテーブルを10分毎に更新します'
    -> DO
    -> REPLACE INTO City_Summary select 1,CountryCode,SUM(Population) from City_Master where CountryCode='JPN' group by CountryCode;
Query OK, 0 rows affected (0.00 sec)

root@localhost [world2]>  SELECT * FROM information_schema.EVENTS\G
*************************** 1. row ***************************
       EVENT_CATALOG: def
        EVENT_SCHEMA: world2
          EVENT_NAME: SNAP_SHOT_VIEW_City_Master_City_Summary
             DEFINER: root@localhost
           TIME_ZONE: SYSTEM
          EVENT_BODY: SQL
    EVENT_DEFINITION: REPLACE INTO City_Summary select 1,CountryCode,SUM(Population) from City_Master where CountryCode='JPN' group by CountryCode
          EVENT_TYPE: RECURRING
          EXECUTE_AT: NULL
      INTERVAL_VALUE: 10
      INTERVAL_FIELD: MINUTE
            SQL_MODE: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
              STARTS: 2016-06-10 15:46:29
                ENDS: NULL
              STATUS: ENABLED
       ON_COMPLETION: NOT PRESERVE
             CREATED: 2016-06-10 15:46:29
        LAST_ALTERED: 2016-06-10 15:46:29
       LAST_EXECUTED: 2016-06-10 15:46:29
       EVENT_COMMENT: サマリーテーブルを10分毎に更新します
          ORIGINATOR: 1
CHARACTER_SET_CLIENT: utf8
COLLATION_CONNECTION: utf8_general_ci
  DATABASE_COLLATION: utf8mb4_general_ci
1 row in set (0.00 sec)

root@localhost [world2]> 

Oracleのマテリアライズド・ビュー程の柔軟性は無いですが、
こんな対応方法もあるかと思いますので、使えそうでしたら是非試してみて下さい。

メモ: イベントの削除
———————-
DROP EVENT イベント名;

Triggerを利用する場合の参考)
http://www.fromdual.ch/mysql-materialized-views
http://d.hatena.ne.jp/IT7C/20100730/1280501734

参考)
13.2.8 REPLACE 構文
https://dev.mysql.com/doc/refman/5.6/ja/replace.html
13.2.5.3 INSERT … ON DUPLICATE KEY UPDATE 構文
https://dev.mysql.com/doc/refman/5.6/ja/insert-on-duplicate.html
13.1.11 CREATE EVENT 構文
https://dev.mysql.com/doc/refman/5.6/ja/create-event.html


SYSスキーマのstatement_analysisとperformance_schemaのevents_statements_historyを利用して、MySQLで簡単にSQLのパフォーマンス確認。

statement_analysis / x$statement_analysis
Description:
Lists a normalized statement view with aggregated statistics,
mimics the MySQL Enterprise Monitor Query Analysis view, ordered by the total execution time per normalized statement
https://github.com/mysql/mysql-sys

events_statements_history
events_statements_history テーブルには、スレッドごとの最新の N ステートメントイベントが格納されます。
N の値はサーバー起動時に自動サイズ設定されます。テーブルサイズを明示的に設定するには、サーバー起動時にperformance_schema_events_statements_history_sizeシステム変数を設定します。
ステートメントイベントは終了するまでテーブルに追加されません。新しいイベントが追加されたときに、テーブルがいっぱいである場合、古いイベントが破棄されます。
https://dev.mysql.com/doc/refman/5.6/ja/events-statements-history-table.html

以下、幾つかテストしてみました。データも十分に入っていないので、十分なテストでは無いですが、
挙動は把握出来るかと思いますので、実際の検証環境で確認して見てください。

【1】 JSONデータのFULLスキャンの状況確認
(1) 先ずは、既存のsys.statement_analysisの状況を確認してみます。

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis;
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db                 | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:16:47 | SHOW TABLES                                                       | sys                | *         |          1 |               0 |                 0 |
| 2016-06-03 22:16:47 | SELECT `sys` . `format_stateme ... cy` , `sys` . `format_time` (  | sys                | *         |          0 |               0 |                 0 |
| 2016-06-03 22:16:47 | SHOW SCHEMAS                                                      | performance_schema | *         |          1 |               0 |                 0 |
| 2016-06-03 22:16:47 | SHOW TABLES                                                       | performance_schema | *         |          1 |               0 |                 0 |
| 2016-06-03 22:16:47 | SELECT SYSTEM_USER , HOST , `d ... != ? AND `state` NOT IN (...)  | performance_schema | *         |          2 |               2 |                 0 |
| 2016-06-03 22:16:47 | SHOW SCHEMAS                                                      | sys                | *         |          1 |               0 |                 0 |
| 2016-06-03 22:16:47 | SELECT SCHEMA ( )                                                 | performance_schema |           |          0 |               0 |                 0 |
| 2016-06-03 22:16:47 | SELECT SCHEMA ( )                                                 | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:16:47 | SELECT SYSTEM_USER ( )                                            | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:16:47 | SELECT @@`version_comment` LIMIT ?                                | NULL               |           |          0 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
10 rows in set (0.00 sec)

root@localhost [sys]> 

(2) こちらのJSONデータを確認するQueryを実行してみます。

echo "INDEXの無いテーブルに対するSELECTを、TEXT型とJSONデータ型で比較します。"
echo "SELECT distinct json_extract(feature,'$.type') as feature FROM 各テーブル"
echo ""

echo "【TEXT型】"
time /usr/local/mysql/bin/mysql -u demo_user -ppassword -e "SELECT distinct json_extract(feature,'$.type') as feature FROM NEW57.features_txt;"

echo ""

echo "【JSON型】"
time /usr/local/mysql/bin/mysql -u demo_user -ppassword -e "SELECT distinct json_extract(feature,'$.type') as feature FROM NEW57.features_json;"

(3) 実行 (やっぱりJSONデータ型は、TEXT型と比較するとバイナリーで早いですね:206,000件のデータ参照)

[admin@misc01 SOD2015]$ ./json_and_text_without_index.sh 
INDEXの無いテーブルに対するSELECTを、TEXT型とJSONデータ型で比較します。
SELECT distinct json_extract(feature,'$.type') as feature FROM 各テーブル

【TEXT型】
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------+
| feature   |
+-----------+
| "Feature" |
+-----------+

real    0m8.147s
user    0m0.007s
sys     0m0.004s

【JSON型】
mysql: [Warning] Using a password on the command line interface can be insecure.
+-----------+
| feature   |
+-----------+
| "Feature" |
+-----------+

real    0m1.379s
user    0m0.009s
sys     0m0.005s
[admin@misc01 SOD2015]$ 

(4) 実行結果
INDEXも無い、206,000件のデータなのでどちらも、FULLスキャン,temp table,そしてディスクのtemp tableへ変換されてますね。

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis;
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db                 | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:19:02 | SELECT DISTINCTROW `json_extra ... FROM `NEW57` . `features_txt`  | NULL               | *         |          1 |               1 |                 0 | ※
| 2016-06-03 22:19:02 | SELECT DISTINCTROW `json_extra ... ROM `NEW57` . `features_json`  | NULL               | *         |          1 |               1 |                 0 | ※
| 2016-06-03 22:19:02 | SHOW TABLES                                                       | sys                | *         |          2 |               0 |                 0 |
| 2016-06-03 22:19:02 | SELECT `sys` . `format_stateme ... cy` , `sys` . `format_time` (  | sys                | *         |          0 |               0 |                 0 |
| 2016-06-03 22:19:02 | SHOW SCHEMAS                                                      | performance_schema | *         |          1 |               0 |                 0 |
| 2016-06-03 22:19:02 | SELECT SYSTEM_USER ( )                                            | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:19:02 | SHOW SCHEMAS                                                      | sys                | *         |          2 |               0 |                 0 |
| 2016-06-03 22:19:02 | SHOW TABLES                                                       | performance_schema | *         |          1 |               0 |                 0 |
| 2016-06-03 22:19:02 | SELECT SYSTEM_USER , HOST , `d ... != ? AND `state` NOT IN (...)  | performance_schema | *         |          2 |               2 |                 0 |
| 2016-06-03 22:19:02 | SELECT @@`version_comment` LIMIT ?                                | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:19:02 | SELECT SCHEMA ( )                                                 | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:19:02 | SELECT SCHEMA ( )                                                 | performance_schema |           |          0 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
12 rows in set (0.01 sec)

root@localhost [sys]> 

【2】 JSONデータにGENERATE COLUMN(生成列)でINDEXを付与した場合の状況確認

(1) 実行スクリプト

echo "JSONデータ型のサンプルを入れたテーブル"
/usr/local/mysql/bin/mysql -u demo_user -ppassword -e "select * from NEW57.features where feature_street = '\"MARKET\"' limit 1\G"

read -p "Press [Enter] key to resume."
echo "JSONデータ型とGenerated Columnを利用したテーブル"
/usr/local/mysql/bin/mysql -u demo_user -ppassword -e "show create table NEW57.features\G"

read -p "Press [Enter] key to resume."
echo "JSONドキュメントに対して、INDEX検索が利用出来るか確認 -> where feature_street = '\"MARKET\"'"
/usr/local/mysql/bin/mysql -u demo_user -ppassword -e "explain select feature from NEW57.features where feature_street = '\"MARKET\"'\G"

(2) 実行と実行結果

[admin@misc01 SOD2015]$ ./json_and_generated_column_index.sh 
JSONデータ型のサンプルを入れたテーブル
mysql: [Warning] Using a password on the command line interface can be insecure.
*************************** 1. row ***************************
            id: 12250
       feature: {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[-122.39836263491878, 37.79189388899312, 0], [-122.39845248797837, 37.79233030084018, 0], [-122.39768507706792, 37.7924280850133, 0], [-122.39836263491878, 37.79189388899312, 0]]]}, "properties": {"TO_ST": "388", "BLKLOT": "0265003", "STREET": "MARKET", "FROM_ST": "388", "LOT_NUM": "003", "ST_TYPE": "ST", "ODD_EVEN": "E", "BLOCK_NUM": "0265", "MAPBLKLOT": "0265003"}}
  feature_type: "Feature"
feature_street: "MARKET"
Press [Enter] key to resume.

JSONデータ型とGenerated Columnを利用したテーブル
mysql: [Warning] Using a password on the command line interface can be insecure.
*************************** 1. row ***************************
       Table: features
Create Table: CREATE TABLE `features` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `feature` json NOT NULL,
  `feature_type` varchar(30) GENERATED ALWAYS AS (json_extract(`feature`,'$.type')) VIRTUAL,
  `feature_street` varchar(30) GENERATED ALWAYS AS (json_extract(`feature`,'$.properties.STREET')) VIRTUAL,
  PRIMARY KEY (`id`),
  KEY `idx_feature_type` (`feature_type`),
  KEY `idx_feature_street` (`feature_street`)
) ENGINE=InnoDB AUTO_INCREMENT=206561 DEFAULT CHARSET=utf8mb4
Press [Enter] key to resume.

JSONドキュメントに対して、INDEX検索が利用出来るか確認 -> where feature_street = '"MARKET"'
mysql: [Warning] Using a password on the command line interface can be insecure.
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: features
   partitions: NULL
         type: ref
possible_keys: idx_feature_street
          key: idx_feature_street
      key_len: 123
          ref: const
         rows: 808
     filtered: 100.00
        Extra: NULL
[admin@misc01 SOD2015]$ 

(3) 実行後のsys.statement_analysisを確認すると、full_scanでも無く,temp tableでの処理も無いことが確認出来る。

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis;
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db                 | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:22:03 | SELECT DISTINCTROW `json_extra ... FROM `NEW57` . `features_txt`  | NULL               | *         |          1 |               1 |                 0 |
| 2016-06-03 22:22:03 | SELECT DISTINCTROW `json_extra ... ROM `NEW57` . `features_json`  | NULL               | *         |          1 |               1 |                 0 |
| 2016-06-03 22:22:03 | SELECT * FROM `NEW57` . `featu ... _extract` ( `feature` , ? ) )  | NULL               |           |          0 |               0 |                 0 | ※
| 2016-06-03 22:22:03 | SHOW TABLES                                                       | sys                | *         |          3 |               0 |                 0 |
| 2016-06-03 22:22:03 | SELECT `sys` . `format_stateme ... cy` , `sys` . `format_time` (  | sys                | *         |          0 |               0 |                 0 |
| 2016-06-03 22:22:03 | SHOW SCHEMAS                                                      | performance_schema | *         |          1 |               0 |                 0 |
| 2016-06-03 22:22:03 | SELECT SYSTEM_USER ( )                                            | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:22:03 | SHOW SCHEMAS                                                      | sys                | *         |          3 |               0 |                 0 |
| 2016-06-03 22:22:03 | SELECT @@`version_comment` LIMIT ?                                | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:22:03 | SHOW TABLES                                                       | performance_schema | *         |          1 |               0 |                 0 |
| 2016-06-03 22:22:03 | SELECT SYSTEM_USER , HOST , `d ... != ? AND `state` NOT IN (...)  | performance_schema | *         |          2 |               2 |                 0 |
| 2016-06-03 22:22:03 | EXPLAIN SELECT `feature` FROM  ... _extract` ( `feature` , ? ) )  | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:22:03 | SHOW CREATE TABLE `NEW57` . `f ... _extract` ( `feature` , ? ) )  | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:22:03 | SELECT SCHEMA ( )                                                 | NULL               |           |          0 |               0 |                 0 |
| 2016-06-03 22:22:03 | SELECT SCHEMA ( )                                                 | performance_schema |           |          0 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+--------------------+-----------+------------+-----------------+-------------------+
15 rows in set (0.00 sec)

root@localhost [sys]> 

(4) 強制的にデータのソート処理を発生させて、sys.statement_analysisにてsort_merge_passesが確認出来るか念の為確認。
group byの処理により、tmp_disk_tables、sort_merge_passes共に発生しています。


root@localhost [NEW57]> SELECT json_extract(feature,'$.properties.STREET'),count(json_extract(feature,'$.properties.STREET')) as feature FROM NEW57.features group by json_extract(feature,'$.properties.STREET');
<SNIP>
1717 rows in set (4.61 sec)

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis where db = 'NEW57';
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db    | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| 2016-06-04 08:40:56 | SELECT `json_extract` ( `featu ... on_extract` ( `feature` , ? )  | NEW57 | *         |          1 |               1 |                 1 | ※DISK処理発生
| 2016-06-04 08:40:56 | SELECT `json_extract` ( `featu ... re` FROM `NEW57` . `features`  | NEW57 | *         |          0 |               0 |                 0 |
| 2016-06-04 08:40:56 | SHOW SCHEMAS                                                      | NEW57 | *         |          1 |               0 |                 0 |
| 2016-06-04 08:40:56 | SHOW TABLES                                                       | NEW57 | *         |          1 |               0 |                 0 |
| 2016-06-04 08:40:56 | SELECT `json_extract` ( `featu ... re` FROM `NEW57` . `features`  | NEW57 |           |          0 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
5 rows in set (0.01 sec)

root@localhost [sys]> select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+---------------+
| SQL_TEXT                                                                                                                                                                                  | t (ms)    | ROWS_EXAMINED |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+---------------+
| NULL                                                                                                                                                                                      |    0.0197 |             0 |
| NULL                                                                                                                                                                                      |    0.0080 |             0 |
| NULL                                                                                                                                                                                      |    0.0048 |             0 |
| NULL                                                                                                                                                                                      |    0.0039 |             0 |
| select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis where db = 'NEW57'                                                               |    3.2530 |            25 |
| SELECT json_extract(feature,'$.properties.STREET'),count(json_extract(feature,'$.properties.STREET')) as feature FROM NEW57.features group by json_extract(feature,'$.properties.STREET') | 4606.0133 |        209994 |
| SELECT json_extract(feature,'$.properties.STREET') as feature FROM NEW57.features                                                                                                         |  559.5791 |        206560 |
| SELECT json_extract(feature,'$.properties.STREET'),count(json_extract(feature,'$.properties.STREET')) as feature FROM NEW57.features                                                      |    0.2148 |             0 |
| NULL                                                                                                                                                                                      |    0.0981 |             0 |
| SET @slave_uuid= 'b1f2d384-009f-11e6-9aa6-0800275fa837'                                                                                                                                   |    0.0996 |             0 |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------+---------------+
10 rows in set (0.00 sec)

root@localhost [sys]> 


【3】 ここからは、MySQLのオフィシャルサンプルDB(World)を利用してMySQLの挙動とパフォーマンスを確認してみます。

パターン1) Country STRAIGHT_JOIN CountryLanguage ON CountryLanguage.CountryCode

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis where db = 'world';
+---------------------+---------------+-------+-----------+------------+-----------------+-------------------+
| now()               | query         | db    | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+---------------+-------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:25:41 | SHOW TABLES   | world | *         |          2 |               0 |                 0 |
| 2016-06-03 22:25:41 | SHOW SCHEMAS  | world | *         |          1 |               0 |                 0 |
+---------------------+---------------+-------+-----------+------------+-----------------+-------------------+
2 rows in set (0.01 sec)

root@localhost [sys]>


root@localhost [world]> SELECT Language, COUNT(1) FROM Country STRAIGHT_JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE;
+---------------------------+----------+
| Language                  | COUNT(1) |
+---------------------------+----------+
| Abhyasi                   |        1 |
| Acholi                    |        1 |
| Adja                      |        1 |
<SNIP>
| Zhuang                    |        1 |
| Zulu                      |        3 |
| [South]Mande              |        1 |
+---------------------------+----------+
457 rows in set (0.04 sec)

root@localhost [world]> 


root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis where db = 'world';
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db    | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:25:52 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |(1回目)
| 2016-06-03 22:25:52 | SHOW TABLES                                                       | world | *         |          2 |               0 |                 0 |
| 2016-06-03 22:25:52 | SHOW SCHEMAS                                                      | world | *         |          1 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
3 rows in set (0.00 sec)

root@localhost [sys]> 

上記、Queryの実行プランと実行時間を確認してみます。

root@localhost [world]> explain SELECT Language, COUNT(1) FROM Country STRAIGHT_JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE;
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------+------+----------+----------------------------------------------+
| id | select_type | table           | partitions | type   | possible_keys       | key         | key_len | ref                   | rows | filtered | Extra                                        |
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------+------+----------+----------------------------------------------+
|  1 | SIMPLE      | Country         | NULL       | ALL    | PRIMARY             | NULL        | NULL    | NULL                  |  239 |   100.00 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | City            | NULL       | eq_ref | PRIMARY             | PRIMARY     | 4       | world.Country.Capital |    1 |   100.00 | Using index                                  |
|  1 | SIMPLE      | CountryLanguage | NULL       | ref    | PRIMARY,CountryCode | CountryCode | 3       | world.Country.Code    |    4 |   100.00 | Using index                                  |
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------+------+----------+----------------------------------------------+
3 rows in set, 1 warning (0.00 sec)

root@localhost [world]> 



root@localhost [sys]> select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
| SQL_TEXT                                                                                                                                                                    | t (ms) | ROWS_EXAMINED |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
| SELECT Language, COUNT(1) FROM Country STRAIGHT_JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE | 7.3615 |          2368 |(1回目)
| select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10                       | 0.5577 |             2 |
| truncate table performance_schema.events_statements_history                                                                                                                 | 0.9153 |             0 |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
3 rows in set (0.01 sec)

root@localhost [sys]>

パターン2) CountryLanguage STRAIGHT_JOIN Country ON CountryLanguage.CountryCode = Country.Code

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis where db = 'world';
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db    | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:28:15 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |
| 2016-06-03 22:28:15 | EXPLAIN SELECT LANGUAGE , COUN ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |
| 2016-06-03 22:28:15 | SHOW TABLES                                                       | world | *         |          2 |               0 |                 0 |
| 2016-06-03 22:28:15 | SHOW SCHEMAS                                                      | world | *         |          1 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
4 rows in set (0.01 sec)

root@localhost [sys]>


root@localhost [world]> SELECT Language, COUNT(1) FROM CountryLanguage STRAIGHT_JOIN Country ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE;
+---------------------------+----------+
| Language                  | COUNT(1) |
+---------------------------+----------+
| Abhyasi                   |        1 |
| Acholi                    |        1 |
| Adja                      |        1 |
<SNIP>
| Zhuang                    |        1 |
| Zulu                      |        3 |
| [South]Mande              |        1 |
+---------------------------+----------+
457 rows in set (0.01 sec)

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis where db = 'world';
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db    | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:29:13 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |(1回目)
| 2016-06-03 22:29:13 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world |           |          1 |               0 |                 0 |(2回目)
| 2016-06-03 22:29:13 | EXPLAIN SELECT LANGUAGE , COUN ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |
| 2016-06-03 22:29:13 | SHOW TABLES                                                       | world | *         |          2 |               0 |                 0 |
| 2016-06-03 22:29:13 | SHOW SCHEMAS                                                      | world | *         |          1 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
5 rows in set (0.01 sec)

root@localhost [sys]> 

上記、Queryの実行プランと実行時間を確認してみます。
行数は、多いですがFULL SCANは発生していません、また時間も若干早いです。(但し、何回も実施していないので確かではありません、但しこのサイズではそれ程大きく変わらない事は確かです。)

root@localhost [world]> explain SELECT Language, COUNT(1) FROM CountryLanguage STRAIGHT_JOIN Country ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE;
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------------------+------+----------+----------------------------------------------+
| id | select_type | table           | partitions | type   | possible_keys       | key         | key_len | ref                               | rows | filtered | Extra                                        |
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------------------+------+----------+----------------------------------------------+
|  1 | SIMPLE      | CountryLanguage | NULL       | index  | PRIMARY,CountryCode | CountryCode | 3       | NULL                              |  984 |   100.00 | Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | Country         | NULL       | eq_ref | PRIMARY             | PRIMARY     | 3       | world.CountryLanguage.CountryCode |    1 |   100.00 | Using where                                  |
|  1 | SIMPLE      | City            | NULL       | eq_ref | PRIMARY             | PRIMARY     | 4       | world.Country.Capital             |    1 |   100.00 | Using index                                  |
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------------------+------+----------+----------------------------------------------+
3 rows in set, 1 warning (0.00 sec)

root@localhost [world]> 

root@localhost [sys]> select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
| SQL_TEXT                                                                                                                                                                    | t (ms) | ROWS_EXAMINED |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
| SELECT Language, COUNT(1) FROM CountryLanguage STRAIGHT_JOIN Country ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE | 7.0868 |          3865 |(2回目)
| select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10                       | 1.5986 |             6 |
| SELECT Language, COUNT(1) FROM Country STRAIGHT_JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE | 7.3615 |          2368 |(1回目)
| select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10                       | 0.5577 |             2 |
| truncate table performance_schema.events_statements_history                                                                                                                 | 0.9153 |             0 |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
5 rows in set (0.00 sec)

root@localhost [sys]>

パターン3) Country JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code
ヒント無しなので、オプティマイザー次第

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis where db = 'world';
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db    | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:33:16 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |
| 2016-06-03 22:33:16 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world |           |          1 |               0 |                 0 |
| 2016-06-03 22:33:16 | EXPLAIN SELECT LANGUAGE , COUN ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |
| 2016-06-03 22:33:16 | SHOW TABLES                                                       | world | *         |          2 |               0 |                 0 |
| 2016-06-03 22:33:16 | EXPLAIN SELECT LANGUAGE , COUN ... OM `City` ) GROUP BY LANGUAGE  | world |           |          1 |               0 |                 0 |
| 2016-06-03 22:33:16 | SHOW SCHEMAS                                                      | world | *         |          1 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
6 rows in set (0.01 sec)

root@localhost [sys]>


root@localhost [world]> SELECT Language, COUNT(1) FROM Country JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE;
+---------------------------+----------+
| Language                  | COUNT(1) |
+---------------------------+----------+
| Abhyasi                   |        1 |
| Acholi                    |        1 |
| Adja                      |        1 |
<SNIP>
| Zhuang                    |        1 |
| Zulu                      |        3 |
| [South]Mande              |        1 |
+---------------------------+----------+
457 rows in set (0.01 sec)

root@localhost [world]> 

root@localhost [sys]> select now(),query,db,full_scan,tmp_tables,tmp_disk_tables,sort_merge_passes from sys.statement_analysis where db = 'world';
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| now()               | query                                                             | db    | full_scan | tmp_tables | tmp_disk_tables | sort_merge_passes |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
| 2016-06-03 22:33:29 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |(1回目)
| 2016-06-03 22:33:29 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world |           |          1 |               0 |                 0 |(2回目)
| 2016-06-03 22:33:29 | SELECT LANGUAGE , COUNT (?) FR ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |(3回目)
| 2016-06-03 22:33:29 | EXPLAIN SELECT LANGUAGE , COUN ... OM `City` ) GROUP BY LANGUAGE  | world | *         |          1 |               0 |                 0 |
| 2016-06-03 22:33:29 | SHOW TABLES                                                       | world | *         |          2 |               0 |                 0 |
| 2016-06-03 22:33:29 | EXPLAIN SELECT LANGUAGE , COUN ... OM `City` ) GROUP BY LANGUAGE  | world |           |          1 |               0 |                 0 |
| 2016-06-03 22:33:29 | SHOW SCHEMAS                                                      | world | *         |          1 |               0 |                 0 |
+---------------------+-------------------------------------------------------------------+-------+-----------+------------+-----------------+-------------------+
7 rows in set (0.01 sec)

root@localhost [sys]> 

上記、Queryの実行プランと実行時間を確認してみます。(今回はヒントを付けてませんが、1回目と同じ実行プランになりました)
よって、EXPLAINも時間も殆ど同じです。

root@localhost [world]> explain SELECT Language, COUNT(1) FROM Country JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE;
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------+------+----------+----------------------------------------------+
| id | select_type | table           | partitions | type   | possible_keys       | key         | key_len | ref                   | rows | filtered | Extra                                        |
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------+------+----------+----------------------------------------------+
|  1 | SIMPLE      | Country         | NULL       | ALL    | PRIMARY             | NULL        | NULL    | NULL                  |  239 |   100.00 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | City            | NULL       | eq_ref | PRIMARY             | PRIMARY     | 4       | world.Country.Capital |    1 |   100.00 | Using index                                  |
|  1 | SIMPLE      | CountryLanguage | NULL       | ref    | PRIMARY,CountryCode | CountryCode | 3       | world.Country.Code    |    4 |   100.00 | Using index                                  |
+----+-------------+-----------------+------------+--------+---------------------+-------------+---------+-----------------------+------+----------+----------------------------------------------+
3 rows in set, 1 warning (0.01 sec)

root@localhost [world]> 


root@localhost [sys]> select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10;
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
| SQL_TEXT                                                                                                                                                                    | t (ms) | ROWS_EXAMINED |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
| SELECT Language, COUNT(1) FROM Country JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE          | 7.3393 |          2368 |(3回目)
| select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10                       | 0.8451 |            10 |
| SELECT Language, COUNT(1) FROM CountryLanguage STRAIGHT_JOIN Country ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE | 7.0868 |          3865 |(2回目)
| select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10                       | 1.5986 |             6 |
| SELECT Language, COUNT(1) FROM Country STRAIGHT_JOIN CountryLanguage ON CountryLanguage.CountryCode = Country.Code WHERE Capital IN (SELECT Id FROM City) GROUP BY LANGUAGE | 7.3615 |          2368 |(1回目)
| select SQL_TEXT,(timer_wait)/1000000000.0 "t (ms)",ROWS_EXAMINED from performance_schema.events_statements_history order by TIMER_START desc limit 10                       | 0.5577 |             2 |
| truncate table performance_schema.events_statements_history                                                                                                                 | 0.9153 |             0 |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+---------------+
7 rows in set (0.00 sec)

root@localhost [sys]> 

【参考】
■ この動画とPDFは参考になる良い資料です。
https://community.oracle.com/docs/DOC-997564

■ こちらのツールを利用すると複数MySQLのシステムとSQLを監視する事が出来ます。
簡単にGUIベースで確認して管理コスト削減したいたい場合はお勧めします。
https://www-jp.mysql.com/products/enterprise/monitor.html


MySQL Shell経由でPythonで書いた処理をそのまま流し込めるので、
Twitter APIからJSONデータを取得して100件程データベースに流し込むデモを行いました。
PS: ネットワークがなぜか、共有WIFI、プライベートWIFI共に調子が悪く微妙な感じになってしまいましたが。。。

JSONデータ型、Generated Column(生成列)、MySQL Shellの挙動を簡易的にデモするものなので内容的には微妙ですが、
もしAPI経由でJSONデータを取得して、バッチ処理で定期的にサービスで使えそうなデータを取得して、
データを蓄積したり検索したい場合などに使えるかもしれません。

demo

————————————————————————
1) Create a Sample Table for Importing Data from Twitter API
————————————————————————
name列は、Tweetした人の名前が入ります。
text列は、Tweetしたコメントが入ります。


Create Table: CREATE TABLE `X_PYTHON` (
  `doc` json DEFAULT NULL,
  `_id` varchar(32) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,'$._id'))) STORED NOT NULL,
  `name` varchar(64) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,'$.user.name'))) STORED,
  `text` varchar(512) GENERATED ALWAYS AS (json_unquote(json_extract(`doc`,'$.text'))) STORED,
  UNIQUE KEY `_id` (`_id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

————————————————————————
2) Create python file ( demo_python_twitter.py )
————————————————————————
デモ用サンプルなので色々とコメントアウトしてます。またエラー処理も入れてません。
動作確認のみでご利用下さい。
※ Twitterキー、スキーマ、アカウントに関しては、適宜変更して下さい。
※ 必要に応じて、”pip install requests-oauthlib”でOauthもインストールしておいてください。


# coding: utf-8

print "======================================================"
print "MySQL5.7.12 Basic CRUD Operations by Python"
print "MySQL Innovation Daysデモ"
print "======================================================"

from requests_oauthlib import OAuth1Session
import json
import mysqlx

##############################################################
#    This section is just used for my private account.
##############################################################

api_key      = "Please put your own key"
api_secret   = "Please put your own key"
token        = "Please put your own key"
token_secret = "Please put your own key"

##############################################################
#    This section is API and Auth.
##############################################################

url = "https://api.twitter.com/1.1/statuses/home_timeline.json"
params = {'count':'100',}

auth = OAuth1Session(api_key, api_secret, token, token_secret)
res = auth.get(url, params = params)

##############################################################
#     Connect Session to the MySQL Instance.
##############################################################

if res.status_code == 200: # In case of successfully connect to API

 mySession = mysqlx.getSession({
 'host': 'localhost', 'port': 33060,
 'dbUser': 'demo_user', 'dbPassword': 'password'} )
 myDb = mySession.getSchema('NEW57')


### Create a new collection 'my_collection' ###
### This section is turned off for demo.    ###
# myColl = myDb.createCollection('X_PYTHON')


### Insert documents ###

 timeline = json.loads(res.text)
 for tweet in timeline:
   myDb.X_PYTHON.add(tweet).execute()
#  myDb.X_PYTHON.add(tweet).executeAsync() # ASYNCはmysqlshでは対象外.
#  print(tweet["text"])


####################################################
###         Drop the collection(Table)
####################################################
# mySession.dropCollection('NEW57','X_PYTHON')

else: # 失敗した場合
        print ("Error: %d" % req.status_code)

————————————————————————
3) Please Execute the Script through mysqlsh
————————————————————————
こちらは、Pythonモードで処理しています。


mysqlsh --py < demo_python_twitter.py

————————————————————————
4) You can find who is the most active user in twitter.
————————————————————————
mysqlshではパイプでつないで、リダイレクトする事が出来ます。


echo "select name,count(name) from X_PYTHON group by name order by count(name) desc limit 10;" | mysqlsh -u demo_user -ppassword --sql --schema=NEW57

実行結果(直近100件のTweet件数毎のデータ確認)


mysqlx: [Warning] Using a password on the command line interface can be insecure.
+---------------------------------------+-------------+
| name                                  | count(name) |
+---------------------------------------+-------------+
<snip>
| Tony Darnell                          |          13 |
| Giuseppe Maxia                        |           2 |
| planetmysql                           |           2 |
<snip>

name以外にtextにも生成列を設定しているので、コメントをフィルターする事も出来ます。
簡易デモなので何ですが、実用的な用途があれば簡単にJSONデータ処理出来るので色々なケースで使えるかもしれません。


root@localhost [NEW57]> desc X_PYTHON;
+-------+--------------+------+-----+---------+------------------+
| Field | Type         | Null | Key | Default | Extra            |
+-------+--------------+------+-----+---------+------------------+
| doc   | json         | YES  |     | NULL    |                  |
| _id   | varchar(32)  | NO   | PRI | NULL    | STORED GENERATED |
| name  | varchar(64)  | YES  | MUL | NULL    | STORED GENERATED |
| text  | varchar(512) | YES  |     | NULL    | STORED GENERATED |
+-------+--------------+------+-----+---------+------------------+
4 rows in set (0.00 sec)

root@localhost [NEW57]> 

【補足資料】
先日、MyNA(MySQL User Group)のイベントでMySQL AS Document Storeのデモした時の資料です。


先日は、mysqlxプラグインをMySQL Shellから処理して基本動作確認しました。
mysqlxとMySQL Shell初期設定と基本動作確認
本日は、mysqlxに対応しているConnector/Node.jsからMySQLにJSONデータを処理を確認してみました。
基本的には、JohannesさんがMySQL Server Blogに書いた記事をベースに検証しています。
http://mysqlserverteam.com/mysql-5-7-12-part-5-connectornode-js/

【mysqlxとConnector/Node.jsを利用する為の前提条件】
MySQL 5.7.12 or higher, with the X plugin enabled
Node.JS 4.2

Download Connector/Node.js
~概要~
Connector/Node.js is the official Node.js driver for MySQL.
MySQL Connector/Node.js is an native asychronous promise-based client library for the of
MySQL 5.7.12+ providing New CRUD APIs for Document and Relational development

http://dev.mysql.com/downloads/connector/nodejs/

設定前の各種バージョン確認


[root@misc01 nodejs]# node -v
v4.4.3
[root@misc01 nodejs]# npm -v
2.15.1
[root@misc01 nodejs]# 

admin@192.168.56.113 [NEW57]> select @@version;
+-------------------------------------------+
| @@version                                 |
+-------------------------------------------+
| 5.7.12-enterprise-commercial-advanced-log |
+-------------------------------------------+
1 row in set (0.01 sec)

admin@192.168.56.113 [NEW57]> select PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS,PLUGIN_TYPE_VERSION from information_schema.plugins
    -> where PLUGIN_NAME = 'mysqlx';
+-------------+----------------+---------------+---------------------+
| PLUGIN_NAME | PLUGIN_VERSION | PLUGIN_STATUS | PLUGIN_TYPE_VERSION |
+-------------+----------------+---------------+---------------------+
| mysqlx      | 1.0            | ACTIVE        | 50712.0             |
+-------------+----------------+---------------+---------------------+
1 row in set (0.01 sec)

admin@192.168.56.113 [NEW57]> 

ダウンロード
node.jsもMySQLもコネクター対応している事が確認出来たので、コネクターをダウンロードしてインストールしてみます。


[root@misc01 nodejs]# wget http://dev.mysql.com/get/Downloads/Connector-Nodejs/mysql-connector-nodejs-1.0.2.tar.gz
--2016-04-18 11:00:12--  http://dev.mysql.com/get/Downloads/Connector-Nodejs/mysql-connector-nodejs-1.0.2.tar.gz
dev.mysql.com (dev.mysql.com) をDNSに問いあわせています... 137.254.60.11
dev.mysql.com (dev.mysql.com)|137.254.60.11|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 302 Found
場所: http://cdn.mysql.com//Downloads/Connector-Nodejs/mysql-connector-nodejs-1.0.2.tar.gz [続く]
--2016-04-18 11:00:18--  http://cdn.mysql.com//Downloads/Connector-Nodejs/mysql-connector-nodejs-1.0.2.tar.gz
cdn.mysql.com (cdn.mysql.com) をDNSに問いあわせています... 104.78.21.123
cdn.mysql.com (cdn.mysql.com)|104.78.21.123|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 121705 (119K) [application/x-tar-gz]
`mysql-connector-nodejs-1.0.2.tar.gz' に保存中
100%[==============================================================================================>] 121,705      157KB/s 時間 0.8s   
2016-04-18 11:00:19 (157 KB/s) - `mysql-connector-nodejs-1.0.2.tar.gz' へ保存完了 [121705/121705]
[root@misc01 nodejs]# 

npmを使用してインストール
http://dev.mysql.com/doc/dev/connector-nodejs/
http://dev.mysql.com/doc/dev/connector-nodejs/tutorial-Getting_Started.html


[root@misc01 nodejs]# npm install mysql-connector-nodejs-1.0.2.tar.gz
mysqlx@1.0.2 node_modules/mysqlx
[root@misc01 nodejs]# 

シェルのサンプルを利用した基本動作確認

[root@misc01 nodejs]# cat sample_node_X_API.js 
const mysqlx = require('mysqlx');

mysqlx.getSession({
    host: 'localhost',
    port: 33060,
    dbUser: 'demo_user',
    dbPassword: 'password'
}).then(function (session) {
    return session.createSchema("test_schema").then(function (schema) {
        return schema.createCollection("myCollection");
    }).then(function (collection) {
        return Promise.all([
            collection.add(
                {baz: { foo: "bar"}},
                {foo: { bar: "baz"}}
            ).execute(),
            collection.find("$.baz.foo == 'bar'").execute(function (row) {
                console.log("Row: %j", row);
            }).then(function (res) {
                console.log("Collection find done!");
            }),
            collection.remove("($.foo.bar) == 'baz'").execute().then(function () {
                console.log("Document deleted");
            }),
            collection.drop()
        ]);
    }).then(function () {
        return session.dropSchema("test_schema");
    }).then(function () {
        return session.close();
    });
}).catch(function (err) {
    console.log(err.stack);
    process.exit();
});

上記スクリプトを実行


[root@misc01 nodejs]# node sample_X_API.js 
Row: {"_id":"d0ef5b1d-86e6-34b2-5794-3ebcab02","baz":{"foo":"bar"}}
Collection find done!
Document deleted
[root@misc01 nodejs]# 

実行すると、以下のオブジェクトとデータがmysqlx経由(33060)でjavascriptから登録されている

admin@192.168.56.113 [test_schema]> show tables;
+-----------------------+
| Tables_in_test_schema |
+-----------------------+
| myCollection          |
+-----------------------+
1 row in set (0.01 sec)

admin@192.168.56.113 [test_schema]> select * from myCollection;
+--------------------------------------------------------------------+----------------------------------+
| doc                                                                | _id                              |
+--------------------------------------------------------------------+----------------------------------+
| {"_id": "21dddfdd-2d4e-3cf1-fc5f-05a0e9ea", "foo": {"bar": "baz"}} | 21dddfdd-2d4e-3cf1-fc5f-05a0e9ea |
| {"_id": "7a7a7497-95c8-b7e7-fa15-064a8aba", "baz": {"foo": "bar"}} | 7a7a7497-95c8-b7e7-fa15-064a8aba |
+--------------------------------------------------------------------+----------------------------------+
2 rows in set (0.00 sec)

admin@192.168.56.113 [test_schema]> 

MySQLXのClassは以下のページを参照してください。
http://dev.mysql.com/doc/dev/connector-nodejs/NodeSession.html

その他、幾つかのパターンを検証してみましたが、node.js経由でJSONドキュメントデータをプラグイン経由で処理する事が可能なので、
JSONドキュメント処理をXプロトコル経由で高速に処理出来そうです。ただ、SQL処理とSQLX経由の処理に関しては、自分の方では実際のベンチマークは行っていないので、
Dimitriさんがブログで結果を発表してくれるのを待とうかと思います。
まだリリース間もないので、色々と情報を集めて紹介出来ればと思います。


先日、リリースされたMySQL5.7.12に含まれるmysqlxプラグインと、
同時にリリースされた、MySQL Shellの初期設定と基本動作確認を確認してみました。
基本的にこれらは、MySQL5.7で追加されたJSONデータ型を含むスキーマレスなドキュメントデータを、
mysqlxプラグインを利用する事でより柔軟に処理出来るようにする為に追加され、
MySQLをドキュメントデータベースとしてより利用し易くする為に追加された機能となっています。

MySQL Shellは、MySQLサーバの開発と管理をサポートする、
JavaScript、Python、SQLの対話型のインタフェースをサポートするMySQLの新しいコンポーネント。
データのクエリと更新処理だけでなく、様々な管理操作を実行する為にMySQLのシェルを使用することができます。
※MySQL Shellを利用する為には、事前にMySQL5.7.12以降に含まれているXプラグインをインストールしておく必要があります。

Xプラグイン
Xプロトコルを使用して通信を可能にするMySQLサーバプラグイン。
X DevAPIを実装するクライアントをサポートし、ドキュメントストアとしてMySQLを使用することができます。

Xプロトコル
Xプロトコルは、Xプラグインを実行しているMySQLサーバーと通信します。
Xプロトコルは、SSL経由でCRUDとSQL操作、認証の両方をサポートしていて、
コマンドのストリーミング処理を可能にし、プロトコルとメッセージレイヤ上に拡張可能。

root@localhost [information_schema]> select PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_DESCRIPTION from plugins
    -> where PLUGIN_NAME = 'mysqlx';
+-------------+----------------+--------------------+
| PLUGIN_NAME | PLUGIN_VERSION | PLUGIN_DESCRIPTION |
+-------------+----------------+--------------------+
| mysqlx      | 1.0            | X Plugin for MySQL |
+-------------+----------------+--------------------+
1 row in set (0.00 sec)

root@localhost [information_schema]> 

【X Pluginインストール】
参照:http://dev.mysql.com/doc/refman/5.7/en/x-plugin-installation.html

通常通り、INSTALL PLUGINコマンドでインストール

root@localhost [mysql]> INSTALL PLUGIN mysqlx SONAME 'mysqlx.so';
Query OK, 0 rows affected (0.25 sec)

root@localhost [mysql]> show plugins;
+----------------------------+----------+--------------------+-------------------+-------------+
| Name                       | Status   | Type               | Library           | License     |
+----------------------------+----------+--------------------+-------------------+-------------+
| binlog                     | ACTIVE   | STORAGE ENGINE     | NULL              | PROPRIETARY |
| mysql_native_password      | ACTIVE   | AUTHENTICATION     | NULL              | PROPRIETARY |
| sha256_password            | ACTIVE   | AUTHENTICATION     | NULL              | PROPRIETARY |
| InnoDB                     | ACTIVE   | STORAGE ENGINE     | NULL              | PROPRIETARY |
| INNODB_TRX                 | ACTIVE   | INFORMATION SCHEMA | NULL              | PROPRIETARY |
| INNODB_LOCKS               | ACTIVE   | INFORMATION SCHEMA | NULL              | PROPRIETARY |
| INNODB_LOCK_WAITS          | ACTIVE   | INFORMATION SCHEMA | NULL              | PROPRIETARY |
| INNODB_CMP                 | ACTIVE   | INFORMATION SCHEMA | NULL              | PROPRIETARY |
<SNIP>
| MyISAM                     | ACTIVE   | STORAGE ENGINE     | NULL              | PROPRIETARY |
| MEMORY                     | ACTIVE   | STORAGE ENGINE     | NULL              | PROPRIETARY |
| CSV                        | ACTIVE   | STORAGE ENGINE     | NULL              | PROPRIETARY |
| BLACKHOLE                  | ACTIVE   | STORAGE ENGINE     | NULL              | PROPRIETARY |
| partition                  | ACTIVE   | STORAGE ENGINE     | NULL              | PROPRIETARY |
| FEDERATED                  | DISABLED | STORAGE ENGINE     | NULL              | PROPRIETARY |
| ARCHIVE                    | ACTIVE   | STORAGE ENGINE     | NULL              | PROPRIETARY |
| ngram                      | ACTIVE   | FTPARSER           | NULL              | PROPRIETARY |
| mecab                      | ACTIVE   | FTPARSER           | libpluginmecab.so | PROPRIETARY |
| MYSQL_FIREWALL             | ACTIVE   | AUDIT              | firewall.so       | PROPRIETARY |
| MYSQL_FIREWALL_USERS       | ACTIVE   | INFORMATION SCHEMA | firewall.so       | PROPRIETARY |
| MYSQL_FIREWALL_WHITELIST   | ACTIVE   | INFORMATION SCHEMA | firewall.so       | PROPRIETARY |
| mysqlx                     | ACTIVE   | DAEMON             | mysqlx.so         | PROPRIETARY |
+----------------------------+----------+--------------------+-------------------+-------------+
49 rows in set (0.00 sec)

root@localhost [mysql]> 

【X Plugin オプションと変数について】
設定パラメータに関しては、此方を参照下さい。
http://dev.mysql.com/doc/refman/5.7/en/x-plugin-option-variable-reference.html

【接続方法】
既に、mysqlshをインストール済みなので –sqlオプションを仕様してSQLを流し込んでみる。
mysqlxのPort33060(Default:mysqlx_port)から、ローカルのMySQLにアクセスしてSQLを実行しています。

【サンプルデータベース作成】
参照:
http://downloads.mysql.com/docs/world_x-db.zip
http://dev.mysql.com/doc/refman/5.7/en/mysql-shell-tutorial-javascript-download.html

mysqlsh経由でサンプルデータベースの作成して、mysqlクライアントで接続してスキーマとテーブルの確認

[root@misc01 MID2016]# mysqlsh -u admin -p --sql --recreate-schema world_x < world_x.sql
Enter password: 
Recreating schema world_x...

[root@misc01 MID2016]# mysql -u root -p -e "show databases like 'world%'"
Enter password: 
+-------------------+
| Database (world%) |
+-------------------+
| world             |
| world2            |
| world_x           |
+-------------------+
[root@misc01 MID2016]# mysql -u root -p -e "show tables from world_x"
Enter password: 
+-------------------+
| Tables_in_world_x |
+-------------------+
| City              |
| Country           |
| CountryInfo       |
| CountryLanguage   |
+-------------------+

【接続モード】
参考) 接続方法としては、現状では、以下のモードを選択できるようです。

# mysqlsh --help
MySQL Shell 1.0.3 Development Preview

<SNIP>

  --sql                    Start in SQL mode using a node session.
  --sqlc                   Start in SQL mode using a classic session.
  --js                     Start in JavaScript mode.
  --py                     Start in Python mode.

<SNIP>

こちらでは、mysqlsh経由でjava scriptモードの状態でJSONデータを処理してみます。


[root@misc01 MID2016]# mysqlsh -u admin -p 
Creating an X Session to admin@localhost:33060
Enter password: 
No default schema selected.

Welcome to MySQL Shell 1.0.3 Development Preview

Copyright (c) 2016, 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', '\h' or '\?' for help.

Currently in JavaScript mode. Use \sql to switch to SQL mode and execute queries.
mysql-js> news_collection = session.getSchema('NEW57').createCollection("X_JSON");
<Collection:X_JSON>
mysql-js> news_collection.add({"id": 1, "name": "Document Data", "price": 60000, "Conditions": ["NEW", 2016]});
Query OK, 1 item affected (0.01 sec)

mysql-js> news_collection.find('name like :n').bind('n','Document%');
[
    {
        "Conditions": [
            "NEW",
            2016
        ],
        "_id": "26c8af795703e611630c0800279cea3c",
        "id": 1,
        "name": "Document Data",
        "price": 60000
    }
]
1 document in set (0.03 sec)

mysql-js>  

MySQLに接続してデータを確認した状態


root@localhost [NEW57]> desc X_JSON;
+-------+-------------+------+-----+---------+------------------+
| Field | Type        | Null | Key | Default | Extra            |
+-------+-------------+------+-----+---------+------------------+
| doc   | json        | YES  |     | NULL    |                  |
| _id   | varchar(32) | NO   | PRI | NULL    | STORED GENERATED |
+-------+-------------+------+-----+---------+------------------+
2 rows in set (0.00 sec)

root@localhost [NEW57]> select * from X_JSON\G
*************************** 1. row ***************************
doc: {"id": 1, "_id": "26c8af795703e611630c0800279cea3c", "name": "Document Data", "price": 60000, "Conditions": ["NEW", 2016]}
_id: 26c8af795703e611630c0800279cea3c
1 row in set (0.00 sec)

root@localhost [NEW57]> 

【Beta Draft】
X DevAPI User Guide / Overview
http://dev.mysql.com/doc/x-devapi-userguide/en/devapi-users-introduction.html
その他、X DevAPI経由でコーディング出来そう。但し、現時点ではベータリリースなのでもう少ししたら確認。

【その他:Status変数】
こちらで、mysqlx経由の処理を確認する事が可能です。


[root@misc01 MID2016]# mysql -u root -p -e "show status like 'mysqlx%'"
Enter password: 
+-------------------------------------+--------------------------+
| Variable_name                       | Value                    |
+-------------------------------------+--------------------------+
| Mysqlx_bytes_received               | 389773                   |
| Mysqlx_bytes_sent                   | 3466                     |
| Mysqlx_connection_accept_errors     | 0                        |
| Mysqlx_connection_errors            | 0                        |
| Mysqlx_connections_accepted         | 2                        |
| Mysqlx_connections_closed           | 2                        |
| Mysqlx_connections_rejected         | 0                        |
| Mysqlx_crud_delete                  | 0                        |
| Mysqlx_crud_find                    | 0                        |
| Mysqlx_crud_insert                  | 0                        |
| Mysqlx_crud_update                  | 0                        |
| Mysqlx_errors_sent                  | 1                        |
| Mysqlx_expect_close                 | 0                        |
| Mysqlx_expect_open                  | 0                        |
| Mysqlx_init_error                   | 1                        |
| Mysqlx_notice_other_sent            | 76                       |
| Mysqlx_notice_warning_sent          | 2                        |
| Mysqlx_rows_sent                    | 18                       |
| Mysqlx_sessions                     | 0                        |
| Mysqlx_sessions_accepted            | 1                        |
| Mysqlx_sessions_closed              | 1                        |
| Mysqlx_sessions_fatal_error         | 0                        |
| Mysqlx_sessions_killed              | 0                        |
| Mysqlx_sessions_rejected            | 1                        |
| Mysqlx_ssl_accepts                  | 0                        |
| Mysqlx_ssl_active                   |                          |
| Mysqlx_ssl_cipher                   |                          |
| Mysqlx_ssl_cipher_list              |                          |
| Mysqlx_ssl_ctx_verify_depth         | 18446744073709551615     |
| Mysqlx_ssl_ctx_verify_mode          | 5                        |
| Mysqlx_ssl_finished_accepts         | 0                        |
| Mysqlx_ssl_server_not_after         | Oct 19 05:28:15 2025 GMT |
| Mysqlx_ssl_server_not_before        | Oct 22 05:28:15 2015 GMT |
| Mysqlx_ssl_verify_depth             |                          |
| Mysqlx_ssl_verify_mode              |                          |
| Mysqlx_ssl_version                  |                          |
| Mysqlx_stmt_create_collection       | 0                        |
| Mysqlx_stmt_create_collection_index | 0                        |
| Mysqlx_stmt_disable_notices         | 0                        |
| Mysqlx_stmt_drop_collection         | 0                        |
| Mysqlx_stmt_drop_collection_index   | 0                        |
| Mysqlx_stmt_enable_notices          | 0                        |
| Mysqlx_stmt_execute_sql             | 69                       |
| Mysqlx_stmt_execute_xplugin         | 2                        |
| Mysqlx_stmt_kill_client             | 0                        |
| Mysqlx_stmt_list_clients            | 0                        |
| Mysqlx_stmt_list_notices            | 0                        |
| Mysqlx_stmt_list_objects            | 2                        |
| Mysqlx_stmt_ping                    | 0                        |
| Mysqlx_worker_threads               | 2                        |
| Mysqlx_worker_threads_active        | 0                        |
+-------------------------------------+--------------------------+
[root@misc01 MID2016]# 

参考までに、MySQL ShellのSQLモードの場合とjavascriptモードの場合では接続が3306か33060からの接続かどうかは以下のステータス変数でも確認する事が可能です。

【SQLモードの場合】


[root@misc01 MID2016]# echo "INSERT INTO T_UC01 VALUES (1,'mysql shell'),(2,'マイエスキュウエルシェル');" | mysqlsh -u demo_user -ppassword --sql --schema=world_x2
mysqlx: [Warning] Using a password on the command line interface can be insecure.
[root@misc01 MID2016]# 


root@localhost [sys]> show status like 'Mysqlx_crud%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Mysqlx_crud_delete | 0     |
| Mysqlx_crud_find   | 0     |
| Mysqlx_crud_insert | 0     |
| Mysqlx_crud_update | 0     |
+--------------------+-------+
4 rows in set (0.00 sec)

root@localhost [sys]> root@localhost [sys]> show status like 'Mysqlx_crud%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Mysqlx_crud_delete | 0     |
| Mysqlx_crud_find   | 0     |
| Mysqlx_crud_insert | 0     |
| Mysqlx_crud_update | 0     |
+--------------------+-------+
4 rows in set (0.00 sec)

root@localhost [sys]> 


【JavaScriptモードの場合】


[root@misc01 MID2016]# mysqlsh --uri demo_user@localhost/NEW57 -ppassword
mysqlx: [Warning] Using a password on the command line interface can be insecure.
Creating an X Session to demo_user@localhost:33060/NEW57
Default schema `NEW57` accessible through db.

Welcome to MySQL Shell 1.0.3 Development Preview

Copyright (c) 2016, 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', '\h' or '\?' for help.

Currently in JavaScript mode. Use \sql to switch to SQL mode and execute queries.
mysql-js> db.createCollection("x_posts");
<Collection:x_posts>
mysql-js> db.x_posts.add({"title":"Hello World", "text":"This is the first post via mysqlx"});
Query OK, 1 item affected (0.01 sec)

mysql-js> db.x_posts.find("title = 'Hello World'").sort(["title"]);
[
    {
        "_id": "d2c6eb188b08e6113d110800279cea3c",
        "text": "This is the first post via mysqlx",
        "title": "Hello World"
    }
]
1 document in set (0.00 sec)

mysql-js> 

mysqlx経由のCRUDの実行数が確認出来る。
INSERT x 1回
SELECT x 1回


root@localhost [sys]> show status like 'Mysqlx_crud%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Mysqlx_crud_delete | 0     |
| Mysqlx_crud_find   | 0     |
| Mysqlx_crud_insert | 0     |
| Mysqlx_crud_update | 0     |
+--------------------+-------+
4 rows in set (0.00 sec)

root@localhost [sys]> show status like 'Mysqlx_crud%';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| Mysqlx_crud_delete | 0     |
| Mysqlx_crud_find   | 1     |
| Mysqlx_crud_insert | 1     |
| Mysqlx_crud_update | 0     |
+--------------------+-------+
4 rows in set (0.01 sec)

root@localhost [sys]> 


【参考】
http://dev.mysql.com/doc/refman/5.7/en/mysql-shell.html
http://dev.mysql.com/doc/refman/5.7/en/document-store.html
http://dev.mysql.com/doc/dev/connector-nodejs/
http://mysqlserverteam.com/mysql-5-7-12-part-3-more-than-just-sql/
http://mysqlserverteam.com/mysql-5-7-12-part-4-a-new-mysql-command-line-shell/

メモ:Node.jsのサンプルもあるので、後日、別途JSONドキュメント処理を実施してみる。
http://dev.mysql.com/doc/dev/connector-nodejs/


最近、MySQL5.7の形態素解析エンジンのmecabに関して辞書をカスタマイズ出来るかどうか
聞かれる事が増えた為、mecabの辞書をカスタマイズしてきちんと動作確認してみました。
結論としては、問題無くカスタマイズした辞書も利用出来るようです。
但し、MySQLのマニュアルページを見る限り、MySQL Binaryから提供されているmecab辞書の利用を推奨しているようです。

【以下、抜粋】
Installing MeCab From Source (Optional)
https://dev.mysql.com/doc/refman/5.7/en/fulltext-search-mecab.html
The mecab and mecab-ipadic packages distributed with the MySQL binary are recommended but
if you want to build mecab and mecab-ipadic from source, basic installation steps are provided below.
For additional information, refer to the MeCab documentation.

前回のmecab基本検証
http://variable.jp/2015/04/10/creating-index-with-utf8mb4-on-mecab-with-5-7-7/

基本的には、MySQLのmecab辞書は特に手を加えていないようです
MySQLのコンパイル済みTarからインストールした場合のファイル

 
[root@misc02 admin]# ls -l /usr/local/mysql/lib/mecab/dic/ipadic_utf-8
合計 51828
-rw-r--r--. 1 root mysql   262496 11月  6 23:18 char.bin
-rw-r--r--. 1 root mysql      693 11月  6 23:18 dicrc
-rw-r--r--. 1 root mysql    55910 11月  6 23:18 left-id.def
-rw-r--r--. 1 root mysql  3463716 11月  6 23:18 matrix.bin
-rw-r--r--. 1 root mysql     1477 11月  6 23:18 pos-id.def
-rw-r--r--. 1 root mysql     6241 11月  6 23:18 rewrite.def
-rw-r--r--. 1 root mysql    55910 11月  6 23:18 right-id.def
-rw-r--r--. 1 root mysql 49199027 11月  6 23:18 sys.dic
-rw-r--r--. 1 root mysql     5690 11月  6 23:18 unk.dic

MecabサイトからIPA辞書をダウンロードしてインストールした場合(手を加えていない場合)

[root@misc02 admin]# ls -l /usr/local/lib/mecab/dic/ipadic
合計 51828
-rw-r--r--. 1 root root   262496  4月  4 11:36 char.bin
-rw-r--r--. 1 root root      693  4月  4 11:36 dicrc
-rw-r--r--. 1 root root    55910  4月  4 11:36 left-id.def
-rw-r--r--. 1 root root  3463716  4月  4 11:36 matrix.bin
-rw-r--r--. 1 root root     1477  4月  4 11:36 pos-id.def
-rw-r--r--. 1 root root     6241  4月  4 11:36 rewrite.def
-rw-r--r--. 1 root root    55910  4月  4 11:36 right-id.def
-rw-r--r--. 1 root root 49199027  4月  4 11:36 sys.dic
-rw-r--r--. 1 root root     5690  4月  4 11:36 unk.dic
[root@misc02 admin]# 

ここから、実際に辞書を登録して新規ワードが追加された辞書を登録しています。
※ 事前にnkf等で辞書をUTF-8に変換していますが、ここでは説明を割愛してあります。

【辞書の追加】2つの名詞のみを登録
今回は、”日本オラクル株式会社”と”mecabプラグイン”の二つのみ登録して確認

[root@misc02 mecab]# cat user_defined_dictionary_utf8.csv 
日本オラクル株式会社,1285,1285,5078,名詞,一般,*,*,*,*,日本オラクル株式会社,ニホンオラクルカブシキガイシャ,ニホンオラクルカブシキガイシャ
mecabプラグイン,1285,1285,5699,名詞,一般,*,*,*,*,mecabプラグイン,メカブプラグイン,メカブプラグイン,和布蕪プラグイン
[root@misc02 mecab]# cp -rp user_defined_dictionary_utf8.csv mecab-ipadic-2.7.0-20070801/
[root@misc02 mecab]#

user_defined_dictionary_utf8.csvが作成した追加の辞書ファイル
2文字のみ登録しているので、2文字インデックシングされた事が以下のアウトプットから確認出来る。

[root@misc02 mecab-ipadic-2.7.0-20070801]# /usr/local/libexec/mecab/mecab-dict-index -f utf-8 -t utf-8
reading ./unk.def ... 40
emitting double-array: 100% |###########################################| 
./model.def is not found. skipped.
reading ./Adj.csv ... 27210
reading ./Adnominal.csv ... 135
reading ./Adverb.csv ... 3032
reading ./Auxil.csv ... 199
reading ./Conjunction.csv ... 171
reading ./Filler.csv ... 19
reading ./Interjection.csv ... 252
reading ./Noun.adjv.csv ... 3328
reading ./Noun.adverbal.csv ... 795
reading ./Noun.csv ... 60477
reading ./Noun.demonst.csv ... 120
reading ./Noun.nai.csv ... 42
reading ./Noun.name.csv ... 34202
reading ./Noun.number.csv ... 42
reading ./Noun.org.csv ... 16668
reading ./Noun.others.csv ... 151
reading ./Noun.place.csv ... 72999
reading ./Noun.proper.csv ... 27327
reading ./Noun.verbal.csv ... 12146
reading ./Others.csv ... 2
reading ./Postp-col.csv ... 91
reading ./Postp.csv ... 146
reading ./Prefix.csv ... 221
reading ./Suffix.csv ... 1393
reading ./Symbol.csv ... 208
reading ./Verb.csv ... 130750
reading ./user_defined_dictionary_utf8.csv ... 2
emitting double-array: 100% |###########################################| 
reading ./matrix.def ... 1316x1316
emitting matrix      : 100% |###########################################| 

done!
[root@misc02 mecab-ipadic-2.7.0-20070801]# make install
make[1]: ディレクトリ `/home/admin/mecab/mecab-ipadic-2.7.0-20070801' に入ります
make[1]: `install-exec-am' に対して行うべき事はありません.
/bin/sh ./mkinstalldirs /usr/local/lib/mecab/dic/ipadic
 /bin/install -c -m 644 ./matrix.bin /usr/local/lib/mecab/dic/ipadic/matrix.bin
 /bin/install -c -m 644 ./char.bin /usr/local/lib/mecab/dic/ipadic/char.bin
 /bin/install -c -m 644 ./sys.dic /usr/local/lib/mecab/dic/ipadic/sys.dic
 /bin/install -c -m 644 ./unk.dic /usr/local/lib/mecab/dic/ipadic/unk.dic
 /bin/install -c -m 644 ./left-id.def /usr/local/lib/mecab/dic/ipadic/left-id.def
 /bin/install -c -m 644 ./right-id.def /usr/local/lib/mecab/dic/ipadic/right-id.def
 /bin/install -c -m 644 ./rewrite.def /usr/local/lib/mecab/dic/ipadic/rewrite.def
 /bin/install -c -m 644 ./pos-id.def /usr/local/lib/mecab/dic/ipadic/pos-id.def
 /bin/install -c -m 644 ./dicrc /usr/local/lib/mecab/dic/ipadic/dicrc
make[1]: ディレクトリ `/home/admin/mecab/mecab-ipadic-2.7.0-20070801' から出ます
[root@misc02 mecab-ipadic-2.7.0-20070801]# 

mecab辞書を追加前
辞書追加前は、文字が分割されて辞書登録されている

[root@misc02 mecab-ipadic-2.7.0-20070801]# echo '日本オラクル株式会社' | /usr/local/bin/mecab
日本    名詞,固有名詞,地域,国,*,*,日本,ニッポン,ニッポン
オラクル        名詞,一般,*,*,*,*,*
株式会社        名詞,一般,*,*,*,*,株式会社,カブシキガイシャ,カブシキガイシャ
EOS
[root@misc02 mecab-ipadic-2.7.0-20070801]# 

[root@misc02 mecab-ipadic-2.7.0-20070801]# echo 'mecabプラグイン' | /usr/local/bin/mecab
mecab   名詞,固有名詞,組織,*,*,*,*
プラグ  名詞,一般,*,*,*,*,プラグ,プラグ,プラグ
イン    名詞,一般,*,*,*,*,イン,イン,イン
EOS
[root@misc02 mecab-ipadic-2.7.0-20070801]# 

mecab辞書を追加後
きちんと追加したWORDが1つの単語として認識されている事が分かる

[root@misc02 mecab]# echo '日本オラクル株式会社' | /usr/local/bin/mecab
日本オラクル株式会社    名詞,一般,*,*,*,*,日本オラクル株式会社,ニホンオラクルカブシキガイシャ,ニホンオラクルカブシキガイシャ
EOS
[root@misc02 mecab]# echo 'mecabプラグイン' | /usr/local/bin/mecab
mecabプラグイン 名詞,一般,*,*,*,*,mecabプラグイン,メカブプラグイン,メカブプラグイン,和布蕪プラグイン
EOS
[root@misc02 mecab]# 

検索対象文字列
Server1とServer2で同じデータを登録してIndexingしてあります。
また、mecab辞書はmy.cnfで新規作成したmecab辞書を参照するように設定済み。

root@localhost [mecab]> select description from articles where description like '%mecab%';
+-------------------------------------------------------------------------+
| description                                                             |
+-------------------------------------------------------------------------+
| MySQL mecabプラグインの辞書を追加した場合の動作確認                     |
| MySQL mecab                                                             |
+-------------------------------------------------------------------------+
2 rows in set (0.00 sec)

root@localhost [mecab]> select description from articles where description like '%日本%';
+---------------------------------------------------------------------------+
| description                                                               |
+---------------------------------------------------------------------------+
| 日本オラクル株式会社 辞書を追加した場合の動作確認                         |
| 日本オラクル                                                              |
+---------------------------------------------------------------------------+
2 rows in set (0.00 sec)

root@localhost [mecab]> 

オリジナルのmecab辞書
新規登録した単語では認識されていない。オリジナル辞書にあるmecabや日本と言った辞書で確認出来ている。

root@localhost [mecab]> OPTIMIZE TABLE articles;
+----------------+----------+----------+----------+
| Table          | Op       | Msg_type | Msg_text |
+----------------+----------+----------+----------+
| mecab.articles | optimize | status   | OK       |
+----------------+----------+----------+----------+
1 row in set (0.01 sec)

root@localhost [mecab]> SELECT word, doc_count, doc_id, position FROM information_schema.INNODB_FT_INDEX_TABLE where word like 'meca%';
+-------+-----------+--------+----------+
| word  | doc_count | doc_id | position |
+-------+-----------+--------+----------+
| mecab |         1 |     33 |        5 |
| mecab |         1 |     35 |        5 |
+-------+-----------+--------+----------+
2 rows in set (0.00 sec)

root@localhost [mecab]> SELECT word, doc_count, doc_id, position FROM information_schema.INNODB_FT_INDEX_TABLE where word like 'mecabプラグイン';
Empty set (0.01 sec)

root@localhost [mecab]> SELECT word, doc_count, doc_id, position FROM information_schema.INNODB_FT_INDEX_TABLE where word like '日本%';
+--------+-----------+--------+----------+
| word   | doc_count | doc_id | position |
+--------+-----------+--------+----------+
| 日本   |         1 |     34 |        0 |
| 日本   |         1 |     36 |        0 |
+--------+-----------+--------+----------+
2 rows in set (0.00 sec)

root@localhost [mecab]> SELECT word, doc_count, doc_id, position FROM information_schema.INNODB_FT_INDEX_TABLE where word like '日本オラクル%';
Empty set (0.01 sec)

root@localhost [mecab]> 
 

WORDを追加したmecab辞書
追加で単語が登録されているので、追加登録した辞書で文字列が検索出来ている。
サービスのニーズや流行りの言葉を登録させておく事により、ビジネスのニーズに柔軟に対応出来そうです。
大規模なサービスはSolr等の方が良いかもしれませんが、小規模~中規模のサービスには使えそうです。

root@localhost [mecab]> OPTIMIZE TABLE articles;
+----------------+----------+----------+----------+
| Table          | Op       | Msg_type | Msg_text |
+----------------+----------+----------+----------+
| mecab.articles | optimize | status   | OK       |
+----------------+----------+----------+----------+
1 row in set (0.01 sec)

root@localhost [mecab]>  SELECT word, doc_count, doc_id, position FROM information_schema.INNODB_FT_INDEX_TABLE where word like 'meca%';
+----------------------+-----------+--------+----------+
| word                 | doc_count | doc_id | position |
+----------------------+-----------+--------+----------+
| mecab                |         1 |     20 |        5 |
| mecabプラグイン      |         1 |     18 |        5 |
+----------------------+-----------+--------+----------+
2 rows in set (0.01 sec)

root@localhost [mecab]> SELECT word, doc_count, doc_id, position FROM information_schema.INNODB_FT_INDEX_TABLE where word like 'mecabプラグイン';
+----------------------+-----------+--------+----------+
| word                 | doc_count | doc_id | position |
+----------------------+-----------+--------+----------+
| mecabプラグイン      |         1 |     18 |        5 |
+----------------------+-----------+--------+----------+
1 row in set (0.00 sec)

root@localhost [mecab]> SELECT word, doc_count, doc_id, position FROM information_schema.INNODB_FT_INDEX_TABLE where word like '日本%';
+--------------------------------+-----------+--------+----------+
| word                           | doc_count | doc_id | position |
+--------------------------------+-----------+--------+----------+
| 日本                           |         1 |     21 |        0 |
| 日本オラクル株式会社           |         1 |     19 |        0 |
+--------------------------------+-----------+--------+----------+
2 rows in set (0.01 sec)

root@localhost [mecab]> SELECT word, doc_count, doc_id, position FROM information_schema.INNODB_FT_INDEX_TABLE where word like '日本オラクル%';
+--------------------------------+-----------+--------+----------+
| word                           | doc_count | doc_id | position |
+--------------------------------+-----------+--------+----------+
| 日本オラクル株式会社           |         1 |     19 |        0 |
+--------------------------------+-----------+--------+----------+
1 row in set (0.01 sec)

root@localhost [mecab]> 

search_output

参考:
12.9.9 MeCab Full-Text Parser Plugin
MeCab: Yet Another Part-of-Speech and Morphological Analyzer
単語の追加方法


MySQL5.7から追加されたGenerated Column(生成列)を使って、色々と便利な事が出来るようになりました。
先日、Generated Columnを利用してMySQLの照合順を説明したのでその時に利用した生成列をサンプル兼メモです。

Default: utf8_general_ci or utf8mb4_general_ciの場合
英語の大文字、小文字は同じ文字として扱う。


root@localhost [CONFIRM]> show create table T_Character\G
*************************** 1. row ***************************
       Table: T_Character
Create Table: CREATE TABLE `T_Character` (
  `pid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `string1` char(1) DEFAULT NULL,
  `string2` char(1) DEFAULT NULL,
  `string1_w_string` char(4) GENERATED ALWAYS AS (hex(weight_string(`string1`))) VIRTUAL,
  `string2_w_string` char(4) GENERATED ALWAYS AS (hex(weight_string(`string2`))) VIRTUAL,
  `compare` char(1) GENERATED ALWAYS AS ((`string1` = `string2`)) VIRTUAL,
  PRIMARY KEY (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4
1 row in set (0.01 sec)

root@localhost [CONFIRM]> select * from T_Character;
+-----+---------+---------+------------------+------------------+---------+
| pid | string1 | string2 | string1_w_string | string2_w_string | compare |
+-----+---------+---------+------------------+------------------+---------+
|   1 | A       | a       | 0041             | 0041             | 1       |
|   2 | あ      | ぁ      | 3042             | 3041             | 0       |
|   3 | か      | が      | 304B             | 304C             | 0       |
|   4 | あ      | ア      | 3042             | 30A2             | 0       |
|   5 | ア      | ア       | 30A2             | FF71             | 0       |
|   6 | は      | ぱ      | 306F             | 3071             | 0       |
|   7 | ハ      | パ      | 30CF             | 30D1             | 0       |
+-----+---------+---------+------------------+------------------+---------+
7 rows in set (0.00 sec)

root@localhost [CONFIRM]> 

Default: utf8_bin or utf8mb4_binの場合
英語の大文字、小文字は異なる文字として扱う。


root@localhost [CONFIRM]> show create table T_Character_COLLATE_utf8mb4_bin\G
*************************** 1. row ***************************
       Table: T_Character_COLLATE_utf8mb4_bin
Create Table: CREATE TABLE `T_Character_COLLATE_utf8mb4_bin` (
  `pid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `string1` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
  `string2` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
  `string1_w_string` char(8) GENERATED ALWAYS AS (hex(weight_string(`string1`))) VIRTUAL,
  `string2_w_string` char(8) GENERATED ALWAYS AS (hex(weight_string(`string2`))) VIRTUAL,
  `compare` char(1) GENERATED ALWAYS AS ((`string1` = `string2`)) VIRTUAL,
  PRIMARY KEY (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

root@localhost [CONFIRM]> select * from T_Character_COLLATE_utf8mb4_bin;
+-----+---------+---------+------------------+------------------+---------+
| pid | string1 | string2 | string1_w_string | string2_w_string | compare |
+-----+---------+---------+------------------+------------------+---------+
|   1 | A       | a       | 000041           | 000061           | 0       |
|   2 | あ      | ぁ      | 003042           | 003041           | 0       |
|   3 | か      | が      | 00304B           | 00304C           | 0       |
|   4 | あ      | ア      | 003042           | 0030A2           | 0       |
|   5 | ア      | ア       | 0030A2           | 00FF71           | 0       |
|   6 | は      | ぱ      | 00306F           | 003071           | 0       |
|   7 | ハ      | パ      | 0030CF           | 0030D1           | 0       |
+-----+---------+---------+------------------+------------------+---------+
7 rows in set (0.00 sec)

root@localhost [CONFIRM]> 

BIT型やINT型の変換等の確認にも利用可能


root@localhost [CONFIRM]> show create table T_BIT\G
*************************** 1. row ***************************
       Table: T_BIT
Create Table: CREATE TABLE `T_BIT` (
  `pid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `string1` bit(8) DEFAULT NULL,
  `string2` char(8) GENERATED ALWAYS AS ((`string1` + 0)) VIRTUAL,
  `string3_BIT` char(8) GENERATED ALWAYS AS (conv((`string1` + 0),10,2)) VIRTUAL,
  `string4_OCT` char(8) GENERATED ALWAYS AS (conv((`string1` + 0),10,8)) VIRTUAL,
  `string5_HEX` char(8) GENERATED ALWAYS AS (hex((`string1` + 0))) VIRTUAL,
  PRIMARY KEY (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

root@localhost [CONFIRM]> select * from T_BIT;
+-----+---------+---------+-------------+-------------+-------------+
| pid | string1 | string2 | string3_BIT | string4_OCT | string5_HEX |
+-----+---------+---------+-------------+-------------+-------------+
|   1 |        | 1       | 1           | 1           | 1           |
|   2 |        | 2       | 10          | 2           | 2           |
|   3 |        | 3       | 11          | 3           | 3           |
|   4 |        | 4       | 100         | 4           | 4           |
|   5 |        | 5       | 101         | 5           | 5           |
+-----+---------+---------+-------------+-------------+-------------+
5 rows in set (0.00 sec)

root@localhost [CONFIRM]> show create table T_BIGINT\G
*************************** 1. row ***************************
       Table: T_BIGINT
Create Table: CREATE TABLE `T_BIGINT` (
  `pid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `string1` bigint(64) DEFAULT NULL,
  `string2_2` char(8) GENERATED ALWAYS AS (conv(`string1`,10,2)) VIRTUAL,
  `string3_8` char(8) GENERATED ALWAYS AS (conv(`string1`,10,8)) VIRTUAL,
  `string2_16` char(8) GENERATED ALWAYS AS (conv(`string1`,10,16)) VIRTUAL,
  PRIMARY KEY (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

root@localhost [CONFIRM]> select * from T_BIGINT;
+-----+---------+-----------+-----------+------------+
| pid | string1 | string2_2 | string3_8 | string2_16 |
+-----+---------+-----------+-----------+------------+
|   1 |       1 | 1         | 1         | 1          |
|   2 |       2 | 10        | 2         | 2          |
|   3 |       3 | 11        | 3         | 3          |
|   4 |       4 | 100       | 4         | 4          |
|   5 |       5 | 101       | 5         | 5          |
|   6 |     255 | 11111111  | 377       | FF         |
|   7 |     100 | 1100100   | 144       | 64         |
+-----+---------+-----------+-----------+------------+
7 rows in set (0.00 sec)

root@localhost [CONFIRM]> 

色々と簡素化出来る事が出来るので、良く使う集計などは上記の様に、
GENERATED COLUMN(生成列)でまとめておくと楽かもしれません。

その他
生成列はJSONデータ型との相性が良いので、是非JSONデータ型と合わせて利用してみて下さい。
MySQLによるJSONデータ型処理
JSONデータとGenerated_Columnを使う場合の考慮事項


MySQL FabricにてSharding構成を組んだ環境において、MySQL Routerを利用する場合の設定確認。

Shardingされた環境においても、MySQL Fabric対応コネクターを利用するとアプリケーションからDBへの接続先はFabric管理サーバーで良い。
但し、MySQL Fabric対応コネクターを利用する為には既存アプリケーション書き換えなどが必要になり。導入が大変な為なかなか導入に踏み切れない。
MySQL Routerを利用するとアプリケーションの書き換えは殆ど必要無く、単純に接続先をMySQL Routerに向けるだけで済む為、導入が非常にシンプルになります。
データベースをMySQL FabricにてShardingしていない環境では、MySQL Routerの導入はアプリケーション側もインフラ側もハードルは高くない。
但し、MySQL FabricでShardingされている環境に対応させるには、MySQL Router設定ファイルにそれぞれのShardingグループ毎にMaster(Read-Write)とSlave(Read-Only)を追加する必要がある。
※ ここでは、MySQL Router2.0.2をベースに説明しています。

DB_Con_Shard

Fabricコネクターの場合(接続先はFabric管理ノード)
例)

import mysql.connector 
from mysql.connector import fabric
import time

省略 ...

       conn.set_property(tables=["test.employees"], key=str(emp_no), mode=fabric.MODE_READWRITE, scope=fabric.SCOPE_LOCAL)
省略 ...
       conn.set_property(tables=["test.employees"], key=str(emp_no), mode=fabric.MODE_READONLY, scope=fabric.SCOPE_LOCAL)
省略 ...
       conn=mysql.connector.connect(
          fabric={"host" : "localhost", "port" : 32274, "username": "admin", "password": "admin"}, user="app", database="test", password="app", autocommit=True

MySQL Routerの場合

Router設定ファイルに関してのマニュアル
● Fabricを利用しない場合のRouter設定 (Routerのみの利用の場合)
必要に応じて、connect_timeoutやmax_connectionsを調整して下さい。
https://dev.mysql.com/doc/mysql-router/en/mysql-router-configuration-setup-connection-routing.html

● FabricとRouterを連携する場合のRouter設定
https://dev.mysql.com/doc/mysql-router/en/mysql-router-plugins-fabric-cache.html

MySQL Routerの接続定義ファイルに、以下のようにそれぞれのSharding Groupに対しての接続を作成する。
面倒ですが、アプリケーションからはCaseやIfなどによってSharding Keyの値によって接続先を適宜切り替える必要がある。

以下のような環境の場合の設定

-bash-4.2$ mysqlfabric dump sharding_information
Fabric UUID:  5ca1ab1e-a007-feed-f00d-cab3fe13249e
Time-To-Live: 1
schema_name table_name column_name lower_bound shard_id type_name group_id global_group
----------- ---------- ----------- ----------- -------- --------- -------- ------------
       test  employees      emp_no           1        1     RANGE   shard1       global
       test  employees      emp_no         500        2     RANGE   shard2       global


sharding_def

上記のような環境では、MySQLFabricの定義ファイルは以下のように設定

[fabric_cache:ha1]
address = localhost:32275
user = admin

[routing:Read_Write_Global]
bind_port = 7001
destinations = fabric+cache://ha1/group/global
mode = read-write

[routing:Read_Only_Global]
bind_port = 7002 
destinations = fabric+cache://ha1/group/global
mode = read-only

[routing:Read_Write_shard1]
bind_port = 7003
destinations = fabric+cache://ha1/group/shard1
mode = read-write

[routing:Read_Only_shard1]
bind_port = 7004
destinations = fabric+cache://ha1/group/shard1
mode = read-only

[routing:Read_Write_shard2]
bind_port = 7005
destinations = fabric+cache://ha1/group/shard2
mode = read-write

[routing:Read_Only_shard2]
bind_port = 7006
destinations = fabric+cache://ha1/group/shard2
mode = read-only
[keepalive]
interval = 60

例えば、Shardingグループshard1に対して、書き込みする場合は以下のようにMySQL RouterのRead-Writeに対して接続し、読み込む場合は、shard1のRead-Onlyに対して接続します。書き込みは通常のRouterの動作として、マスター障害が発生するまでは、同じサーバーにアクセスします。読み込みは、以下のようにアクセスする度にラウンドロビンで異なるサーバーに接続されます。


-bash-4.2$ mysql -h 127.0.0.1 -P 7004 -u root --password=root -e "select count(*),@@port FROM test.employees"
+----------+--------+
| count(*) | @@port |
+----------+--------+
|       22 |  63305 |
+----------+--------+

-bash-4.2$ mysql -h 127.0.0.1 -P 7003 -u root --password=root -e "insert into test.employees(emp_no,first_name,last_name) values(23,'Shard1','Insert from P7003')"

-bash-4.2$ mysql -h 127.0.0.1 -P 7004 -u root --password=root -e "select count(*),@@port FROM test.employees"
+----------+--------+
| count(*) | @@port |
+----------+--------+
|       23 |  63304 |
+----------+--------+

-bash-4.2$ mysql -h 127.0.0.1 -P 7004 -u root --password=root -e "select count(*),@@port FROM test.employees"
+----------+--------+
| count(*) | @@port |
+----------+--------+
|       23 |  63305 |
+----------+--------+

MySQL Routerは2015年にリリースされたばかりですが、要件定義と事前検証をしっかり行い利用する事で、
サイト運用者の負荷を軽減する事が可能かと思います。また、システム開発する上での、システムデザインの幅も広げる事が可能です。

Routerを導入したり、その他サポートを受けて自社の工数を削減して生産性を上げたい場合はEnterprise Editionを検討すると良いかと思います。
https://www-jp.mysql.com/products/

その他、もっとシンプルにShardingを導入したい場合はMySQL Clusterを検討しても良いかと思います。
データを各データノードに分散するので、PKベースの処理が殆どの割合を占める場合には良い選択肢かと思います。
以下のページが参考になるかと思います。

奥野氏
MySQL Cluster 7.4で楽しむスケールアウト
山崎氏
MySQL Clusterの特徴とアーキテクチャ
@RDBMS
MySQL ClusterによるNoSQL処理(ClusterJ)

その他、関連メモ
MySQL RouterとMySQL Fabric連携による可用性の向上
MySQL Fabricにおけるsharding Table定義の変更
mysqlfabric shardingで分割したデータの読み込み


レプリケーションフレームワークのMySQL Fabricについては、昨年概要をブログに書いたので本日は、
MySQL FabricをMySQL Routerの連携による可用性や柔軟なスケールアウト方法について確認して見ます。

MySQL Fabricは便利なフレームワークですが、Fabric対応コネクターを利用しないと利用出来ず、
アプリケーションの書き換え労力が必要な為、あまりまだ広まっていませんでしたが、
MySQL Routerのリリースにより今後利用されるケースも増えてくるかと思います。

1. 障害発生時にマスターとスレーブ(新マスター)を自動切り替えし、アプリケーション接続変更は必要が無い。
2. アクセスが増えSlaveサーバーを追加した場合に、アプリケーションを変更せずに動的に新Slaveは参照用Round-Robinに追加される。
3. その他、サーバー入れ替え等のメンテナンスもFabricを利用する事で柔軟性が大幅に向上。

実際に障害発生した場合は、自動的に切り替えを行ってくれるので。
マスター障害発生時も機会損失を最小限にすると同時に緊急対応が不要にする事も可能なので運用負荷を軽減する事が可能です。

router_2

メモ
※ MySQL RouterはMySQL Fabricと連携しないでも利用する事は可能ですが、Fabricを利用した方がより柔軟な対応が可能です。
※ Fabricを利用しない場合は、設定ファイルのread-write、read-only項目にそれぞれアクセスするホストを書きます。マスター障害発生時は、記載されているホストを左から順に接続、スレーブはラウンドロビンです。

MySQL Fabricについて
http://variable.jp/2014/12/05/mysql-fabric%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/

復習:MySQL Fabricの管理ノードではレプリケーションステータスを管理しています。(上記、過去ログ抜粋)
Fabric2

MySQL Router概要
https://www-jp.mysql.com/products/enterprise/router.html

MySQL Routerドキュメント
https://dev.mysql.com/doc/mysql-router/en/

MySQL Router FAQ
https://dev.mysql.com/doc/mysql-router/en/mysql-router-faq.html

MySQL Routerダウンロード
https://dev.mysql.com/downloads/router/
Oracle Linux, RedHat, CentOS6でも利用出来ますが、必要なソフトを事前にインストールしてコンパイルする必要があります。
ちなみに、検証で利用したところ7系のコンパイル済みTarをそのまま展開して6系のLinux上で動かしたところ動きました。

インストール必要条件
An operating system with a compiler that supports C++11.
Example systems that include this support are Ubuntu 14.04 and later, Oracle Linux 7, and OS X 10.10 and later.
Oracle Linux 6 works as well, but you have to install the Software Collection Library 1.2. For RedHat and CentOS, see Docs and Downloads.
MySQL Client Libraries development packages. For example, on Ubuntu this is the libmysqlclient-dev package.
CMake 2.8.9 or later.

MySQL Routerのインストールがおわったら、MySQL Router設定ファイルを作成して実行してください。
MySQL Routerの設定ファイルはシンプルです。
詳細はこちらのサンプルを参照下さい。

■ Static
https://dev.mysql.com/doc/mysql-router/en/mysql-router-configuration-file-example.html
■ Fabric利用
https://dev.mysql.com/doc/mysql-router/en/mysql-router-use-cases.html
■ Static & Fabric利用
http://mysqlhighavailability.com/mysql-router-on-labs-the-newest-member-of-the-mysql-family/

[MySQL Router起動例]
/home/mysql/mysql-router/bin/mysqlrouter –config=/etc/mysql/mysqlrouter.ini

設定ファイルを指定しないで立ち上げると以下のロケーションからファイルを読み込みます。
Generic Linux (standalone‐layout) : ./mysqlrouter.ini
Default, installing under /usr/local : /usr/local/etc/mysqlrouter.ini
RPM and Debian : /etc/mysqlrouter/mysqlrouter.ini

RouterからFabricに接続してマスター、スレーブの切り替わりなどを以下のようなSQLで確認してみる事が可能です。
ここでは、localhost:7001がマスター、localhost:7002がスレーブになっています。


[admin@Fabric01 mysql-router]$ cat mysql_router_balancing.sh 
#!/bin/sh

echo "[Current Slave Connection]"
/usr/local/mysql/bin/mysql -h 127.0.0.1 -P 7002 -u root --password=root -e "SELECT '現在のSlave',@@hostname,@@port,count(*) FROM test.employees"

echo "[Current Master Server]"
# read -p "Press [Enter] key to resume."

# Port 7001 is defined as read/write master server
/usr/local/mysql/bin/mysql -h 127.0.0.1 -P 7001 -u root --password=root -e "select '現在のMaster',@@hostname,@@port"

### Fail Over Demo ###
# /usr/local/mysql/bin/mysqladmin -h 127.0.0.1 -P63301 -u root -p shutdown

実際に実行してみると以下のようにスレーブへのアクセスが実行する度に、ラウンドロビンされている事が確認出来ます。
date

参照:
MySQL Router

補足:
サポートやコンサルテーティブサポートが必要な場合は、Enterprise Editionでサポートを受ける事も可能。
https://www-jp.mysql.com/products/


昨年からMySQLチームがオフィシャルレポジトリーをDockerに公開し、
最近少しずつ利用している方も増えてきて、
開発環境などでは簡単にDBが準備出来て工数削減も出来、
大活躍出来る仕組みなので簡単にレビューしておきます。

[Dockerレポジトリ]
https://hub.docker.com/_/mysql/ (Docker Team)
https://hub.docker.com/r/mysql/mysql-server/ (MySQL team at Oracle)

Tagに関しては、此方を確認下さい。(指定しない場合は、最新のイメージがダウンロードされます)
https://hub.docker.com/r/mysql/mysql-server/tags/

レポジトリーにはDockerのオフィシャルレポジトリーとMySQLチームが公開しているオフィシャルレポジトリーがあります。
イメージをPULLすると以下のような感じになります。

既に、docker pull mysql:5.7.10コマンドでDockerオフィシャルレポジトリからイメージをダウンロード済みの状態です。
追加で、MySQL@OracleのオフィシャルレポジトリーからDockerのイメージをダウンロードしてみます。
サイズだけ見ると、MySQL@Oracleのオフィシャルレポジトリーからダウンロードしたイメージの方がサイズは小さいです。


[root@DockerHost docker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mysql               5.7.10              ea0aca21950d        3 weeks ago         360.3 MB
[root@DockerHost docker]# docker pull mysql/mysql-server:5.7.10
5.7.10: Pulling from mysql/mysql-server
f359075ce4d8: Pull complete 
df9c05f5df07: Pull complete 
bea04efc3319: Pull complete 
14204cc431b4: Pull complete 
249d5b7b31c1: Pull complete 
70a368114c59: Pull complete 
c5b90de0636a: Pull complete 
59e355dc43f6: Pull complete 
f03e996fb5a3: Pull complete 
69497ffa3023: Pull complete 
e472f1765697: Pull complete 
Digest: sha256:c9266386460cfb302d21615bbe8f6a16166b2376eb371095df7cc9a1c9cf6da1
Status: Downloaded newer image for mysql/mysql-server:5.7.10
[root@DockerHost docker]# docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mysql                5.7.10              ea0aca21950d        3 weeks ago         360.3 MB
mysql/mysql-server   5.7.10              e472f1765697        4 weeks ago         294.6 MB
[root@DockerHost docker]# 

Dockerオフィシャルイメージを利用してMySQLを構築してみます。
インスタンス作成時に、データディレクトリーを指定、オプションファイルを指定、インスタンス作成と同時に
データベース作成する為に以下のスクリプトを配置してあります。ソースを確認すると*.sqlか*.shを配置しておくと、
DB作成時に読み込んでSQL文を実行してくれます。(初期共通アカウントやDB作成に使えます)

run


[root@DockerHost docker]# ls -l /docker/init_script/sakila-db/init-docker-sakila.sql 
-rw-r--r-- 1 root root 3421501 Jan  8 01:01 /docker/init_script/sakila-db/init-docker-sakila.sql
[root@DockerHost docker]# 


[root@DockerHost docker]# docker ps -a 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
[root@DockerHost docker]# docker run --name my_docker01 -v /docker/docker01:/var/lib/mysql -v /docker/option1:/etc/mysql/conf.d -v /docker/init_script/sakila-db:/docker-entrypoint-initdb.d -e MYSQL_ROOT_PASSWORD=mysql -d mysql:5.7.10
5152a22b2aa0c724bd260552205ebf566a21b9d854a54921f02a96e40817a930
[root@DockerHost docker]# docker ps -a 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
5152a22b2aa0        mysql:5.7.10        "/entrypoint.sh mysql"   11 seconds ago      Up 9 seconds        3306/tcp            my_docker01
[root@DockerHost docker]# 

Docker Inspectコマンドでパスのマッピングを確認


[root@DockerHost docker]# docker inspect my_docker01 | egrep 'Source|Destination'
            "Source": "/docker/docker01",
            "Destination": "/var/lib/mysql",
            "Source": "/docker/option1",
            "Destination": "/etc/mysql/conf.d",
            "Source": "/docker/init_script/sakila-db",
            "Destination": "/docker-entrypoint-initdb.d",
[root@DockerHost docker]# 

作成したインスタンスに接続して、初期設定状態を確認してみます。
確認: インスタンス接続、オプションファイルの読み込み、初期DBの作成状態。

[root@DockerHost docker]# docker exec -it my_docker01 mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.10-log MySQL Community Server (GPL)

Copyright (c) 2000, 2015, 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.

root@localhost [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sakila             |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

root@localhost [(none)]> show tables from sakila;
+----------------------------+
| Tables_in_sakila           |
+----------------------------+
| actor                      |
| actor_info                 |
| address                    |
| category                   |
| city                       |
| country                    |
| customer                   |
| customer_list              |
| film                       |
| film_actor                 |
| film_category              |
| film_list                  |
| film_text                  |
| inventory                  |
| language                   |
| nicer_but_slower_film_list |
| payment                    |
| rental                     |
| sales_by_film_category     |
| sales_by_store             |
| staff                      |
| staff_list                 |
| store                      |
+----------------------------+
23 rows in set (0.07 sec)

root@localhost [(none)]> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.04 sec)

root@localhost [(none)]> 

次に、MySQL@Oracleチームがオフィシャルに提供しているオフィシャルイメージを利用してインスタンスを作成してみます。
※こちらのイメージを利用する場合は、オプションファイルの指定方法が少し異なります。(/etc/my.cnf)


[root@DockerHost docker]# docker ps -a 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
5152a22b2aa0        mysql:5.7.10        "/entrypoint.sh mysql"   53 minutes ago      Up 53 minutes       3306/tcp            my_docker01
[root@DockerHost docker]# docker run --name my_docker02 -v /docker/docker02:/var/lib/mysql -v /docker/init_script/sakila-db:/docker-entrypoint-initdb.d -e MYSQL_ROOT_PASSWORD=mysql -d mysql/mysql-server:5.7.10
f59d1fd222d7b15ff0f0dfa3879254730734e17451fde56aeb402279f355941e
[root@DockerHost docker]# docker ps -a 
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS               NAMES
f59d1fd222d7        mysql/mysql-server:5.7.10   "/entrypoint.sh mysql"   2 seconds ago       Up 1 seconds        3306/tcp            my_docker02
5152a22b2aa0        mysql:5.7.10                "/entrypoint.sh mysql"   53 minutes ago      Up 53 minutes       3306/tcp            my_docker01
[root@DockerHost docker]# 

作成したインスタンスとDBの確認。(こちらは、OptionファイルはしていないのでDefaultです)


[root@DockerHost docker]# docker exec -it my_docker02 mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.10 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, 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> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sakila             |
| sys                |
+--------------------+
5 rows in set (0.02 sec)

mysql> show tables from sakila;
+----------------------------+
| Tables_in_sakila           |
+----------------------------+
| actor                      |
| actor_info                 |
| address                    |
| category                   |
| city                       |
| country                    |
| customer                   |
| customer_list              |
| film                       |
| film_actor                 |
| film_category              |
| film_list                  |
| film_text                  |
| inventory                  |
| language                   |
| nicer_but_slower_film_list |
| payment                    |
| rental                     |
| sales_by_film_category     |
| sales_by_store             |
| staff                      |
| staff_list                 |
| store                      |
+----------------------------+
23 rows in set (0.00 sec)

mysql> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     |
| character_set_connection | latin1                     |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | latin1                     |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.02 sec)

mysql> 

イメージは停止しても、削除(rm)しなければ継続利用出来るので以下のようにサービスを停止して再開しても、
作成したデータなどは無くならず利用する事が可能です。


[root@DockerHost docker]# docker ps -a 
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS               NAMES
f59d1fd222d7        mysql/mysql-server:5.7.10   "/entrypoint.sh mysql"   10 minutes ago      Up 10 minutes       3306/tcp            my_docker02
5152a22b2aa0        mysql:5.7.10                "/entrypoint.sh mysql"   About an hour ago   Up About an hour    3306/tcp            my_docker01
[root@DockerHost docker]# docker stop f59d1fd222d7
f59d1fd222d7
[root@DockerHost docker]# docker stop 5152a22b2aa0
5152a22b2aa0
[root@DockerHost docker]# docker ps -a 
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS                      PORTS               NAMES
f59d1fd222d7        mysql/mysql-server:5.7.10   "/entrypoint.sh mysql"   11 minutes ago      Exited (0) 13 seconds ago                       my_docker02
5152a22b2aa0        mysql:5.7.10                "/entrypoint.sh mysql"   About an hour ago   Exited (0) 3 seconds ago                        my_docker01
[root@DockerHost docker]# docker start f59d1fd222d7
f59d1fd222d7
[root@DockerHost docker]# docker ps -a 
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS                      PORTS               NAMES
f59d1fd222d7        mysql/mysql-server:5.7.10   "/entrypoint.sh mysql"   11 minutes ago      Up 2 seconds                3306/tcp            my_docker02
5152a22b2aa0        mysql:5.7.10                "/entrypoint.sh mysql"   About an hour ago   Exited (0) 20 seconds ago                       my_docker01
[root@DockerHost docker]# docker exec -it my_docker02 mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.10 MySQL Community Server (GPL)

Copyright (c) 2000, 2015, 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> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sakila             |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql> 

補足: MySql@oracleチームのオフィシャルイメージの場合にオプションファイルを指定する場合は、以下のように実行します。


[root@DockerHost docker]# docker run --name my_docker02 -v /docker/docker02:/var/lib/mysql -v /docker/option2/my.cnf:/etc/my.cnf -v /docker/init_script/sakila-db:/docker-entrypoint-initdb.d -e MYSQL_ROOT_PASSWORD=mysql -d mysql/mysql-server:5.7.10
4fde03dc4cb5966bbfee79de92c2351f2b04812df77bf763b0e14755c3d51261
[root@DockerHost docker]# docker ps -a 
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS                      PORTS               NAMES
4fde03dc4cb5        mysql/mysql-server:5.7.10   "/entrypoint.sh mysql"   22 seconds ago      Up 21 seconds               3306/tcp            my_docker02
5152a22b2aa0        mysql:5.7.10                "/entrypoint.sh mysql"   About an hour ago   Exited (0) 24 minutes ago                       my_docker01
[root@DockerHost docker]# 

[root@DockerHost docker]# docker inspect my_docker02 | egrep 'Source|Destination'
            "Source": "/docker/docker02",
            "Destination": "/var/lib/mysql",
            "Source": "/docker/option2/my.cnf",
            "Destination": "/etc/my.cnf",
            "Source": "/docker/init_script/sakila-db",
            "Destination": "/docker-entrypoint-initdb.d",
[root@DockerHost docker]# 

参考:
https://hub.docker.com/r/mysql/mysql-server/
http://datacharmer.blogspot.jp/2015/10/mysql-docker-operations-part-1-getting.html