2015年10月19日月曜日

MySQL 5.7.9でSHUTDOWN *ステートメント* が実装されたよ!

MySQL :: MySQL 5.7 Reference Manual :: 13.7.6.7 SHUTDOWN Syntax

まあ`mysqladmin shutdown`にしたって mysql_shutdown関数 を叩いて COM_SHUTDOWNパケット を送っているだけで、COM_SHUTDOWNはmysqld側のパーサー(sql/sql_parse.cc)が権限とかよしなに判定して終了処理を呼び出しているので、SQLからできたからってそこまで違うことではない。権限もちゃんとShutdown_Privが無ければ権限不足のエラーが返るので、SQLインジェクション= 即死という訳でもない。

が、単純に( ゚д゚) ファッ!? ってなる。


mysql> SHUTDOWN -- コマンドではなくSQLステートメントなので、";"で終端しないといけない。
    -> ;
Query OK, 0 rows affected (0.00 sec)

`FLUSH TABLES WITH READ LOCK`した端末でそのままシャットダウンできるのはいいのかも知れないけどね。。

MySQL 5.7のinnodb_default_row_format 影響範囲まとめ

日々の覚書: MySQL 5.7.9のinnodb_default_row_formatがまた何か企んでいるようです ではまだ5.7.9が手元になかったので推測でしたが、公開されたので試してみたまとめ。

|PRIMARY KEY|ALTER            |変換|メモ                                          |
|-----------|-----------------|----|----------------------------------------------|
|あり,なし  |ADD COLUMN       |Yes |                                              |
|あり,なし  |DROP COLUMN      |Yes |                                              |
|あり,なし  |ADD FOREIGN KEY  |Yes |                                              |
|あり,なし  |ADD KEY          |No  |ALGORITHM= INPLACE                            |
|あり,なし  |ADD KEY          |Yes |ALGORITHM= COPY                               |
|なし       |ADD PRIMARY KEY  |Yes |                                              |
|あり       |DROP PRIMARY KEY |Yes |                                              |
|あり,なし  |ADD UNIQUE KEY   |No  |                                              |
|あり,なし  |MODIFY, CHANGE   |No  |int => int(カラムリネームのみ)                |
|あり,なし  |MODIFY, CHANGE   |No  |varchar(32) => varchar(32)(カラムリネームのみ)|
|あり,なし  |MODIFY, CHANGE   |No  |varchar(32) => varchar(64)(latin1)            |
|あり,なし  |MODIFY, CHANGE   |Yes |varchar(32) => varchar(256)(latin1)           |
|あり,なし  |MODIFY, CHANGE   |Yes |int => bigint                                 |
|あり,なし  |MODIFY, CHANGE   |Yes |int => int unsigned                           |
|あり,なし  |MODIFY, CHANGE   |Yes |int => varchar(32)                            |
|あり,なし  |Engine= InnoDB   |Yes |OPTIMIZE TABLE                                |
|あり,なし  |ALTER SET DEFAULT|No  |                                              |
|あり,なし  |RENAME           |No  |テーブル名のリネーム                          |



メタデータだけをゴニョる場合(テーブルリネーム、カラムリネーム、デフォルト値の変更)と、ADD KEY(FOREIGN KEY, FULLTEXT KEYはダメ。SPACIALは試してない)だけが暗黙のROW_FORMAT変換が走らない。それ以外は走る。

しかしPKなしのUNIQUE KEY追加ってそれがクラスターキーになるんだけどROW_FORMAT変換は走らないのね。。


テストコードはこちら。mtrスタイルだけどステートメントを上から全部順番に実行すればフツーのmysqlでもいける。

https://gist.github.com/yoku0825/57ba8a8788b792e0ca39


実際、ROW_FORMATの変換によりどの程度の負荷が発生するのやら、簡単なベンチを取ってみる。


mysql> CREATE TABLE t1 (num serial, val varchar(32));
mysql> LOAD DATA INFILE '/var/lib/mysql-files/md5' INTO TABLE t1; -- 容量があんまりなかったので10万件ほど突っ込む

$ while true ; do mysql -e "INSERT INTO d1.t1 (val) VALUES ('dummy')"; done

mysql> ALTER TABLE t1 MODIFY val varchar(64); -- 暗黙のROW_FORMAT変換は走らない
mysql> ALTER TABLE t1 MODIFY val varchar(256); -- 暗黙のROW_FORMAT変換が走る




ちょっとズレがあるけど、ALTER TABLEは変換なしが横軸17のあたり、変換ありが横軸14のあたり(あ、縦軸の単位はbytesです)

変換なしのALTERは10msくらい、変換ありのALTERは100msくらい。rebuild_write(単位はbytes)がハネてるので、やっぱりオンラインはオンラインでもROW_FORMAT変換が走っちゃうと本番ではそれなりに行きそう。


本番では pt-online-schema-change 使ってるからあんま関係なさそうですなんですけどね ;)

MySQLのsysスキーマまとめ

MySQL 5.7で標準バンドルされるsysスキーマ。その実態はperformance_schemaやinformation_schemaから「それっぽい」情報を集めているview。

5.7の機能っぽく語られるけど、前身は ps_helper でMySQL 5.5から使える(けど、5.5のperformance_schemaは情報が少なすぎて役に立つ気配がしない。オーバーヘッドもでかいし)

* MySQL 5.7ではperformance_schemaもだいぶオーバーヘッド落ち着いてきたみたいだし、デフォルトのまま取り敢えず有効にしておいた方がいい。ウチはMySQL 5.6から有効にしてる。
* CPUバウンドの場合確かにオーバーヘッドが見えるけど、I/Oネックになる場合は全然気にならない程度のオーバーヘッドだから、自信がある時だけOFFにする方がいいと思う。
* performance_schema.setup_actorsをTRUNCATEしておけば、新規接続コネクションはトラッキングOFFになるのでそれを活用するのもアリ。
* 5.6で使うには↓のやり方。

$ git clone https://github.com/MarkLeith/mysql-sys.git
$ cd mysql-sys
$ mysql -uroot -p < ./sys_56.sql


## sysスキーマでオススメのView

### sys.metrics
* 一通り監視したくなりそうなやつをSELECTだけでアクセスできるようになってるView
* こんなスクリプト で黙ってfluent_loggerで食わせるだけでKibanaのグラフが出来る。素敵。

### sys.schema_index_statistics
* どのインデックスがどの程度呼ばれていて、どの程度のレスポンスタイムなのかを見られる

### sys.schema_unused_indexes
* schema_index_statisticsの応用で、一度も呼ばれてないインデックスをリストしてくれる

### sys.statement_analysis
* リアルタイムpt-query-digestっぽい感じ。
* performance_schema..events_statements_summary_by_diges t.digest_textとJOINしないとクエリーの完全なステートメントが見えない(勝手に切り詰められている)
* 前に書いた 日々の覚書: MySQLのperformance_schemaでどれくらいの情報が見られるのか のevents_statements_summary_by_digest のView

### sys.statements_with_errors_or_warnings
* これ便利。 ワーニングを握りつぶしている悪い子はいねが?
* gtid-enforce-consistency= WARNと組み合わせてGTID有効化前の準備ができる


## sysスキーマでオススメのプロシージャ

### sys.create_synonym_db('base_database', 'synonym_database')
* やってることはCREATE DATABASE synonym_databaseして、SHOW TABLES FROM base_databaseしてテーブル名を引っこ抜いて、CREATE VIEW synonym_databaseで同じ名前のビューを作ってくれるだけ。
  * CALL sys.create_synonym_db('performance_schema', 'ps')ってしておくと、performance_schemaのタイプ数が減ってすごく捗る。今までどうして考え付かなかったのかってくらい。
  * CALL sys.create_synonym_db('informaiton_schema', 'i_s')も捗る。
* 感動したので書いてみた。sysでなきゃできないなんてことはない。

### sys.ps_setup_disable_background_threads
* バックグラウンドスレッドの統計情報を一発でOFFにする。UPDATE performance_schema.threads SET instrumented= 'No' WHERE type= 'BACKGROUND'とほぼ一緒だと思う。
* init-commandが空いてるならこれをCALLしてもいいかも

### sys.ps_setup_enable_thread
* 特定のスレッドのp_sを有効化できる。
* p_s.setup_actorsを空っぽにしておくと、その後接続してきたコネクションのトラッキングがOFFになるので、空っぽにしておいて、使いたくなった時だけ(5.5のSET profiling= 1みたいなノリで)有効にできる。
  * profilingと違って、自分以外のスレッドも有効化できる。
* 引数はSHOW PROCESSLISTのID
  * 自分自身のスレッドを指定する場合、CALL ps_setup_enable_thread(@@pseudo_thread_id); が便利。

### sys.ps_setup_save, ps_setup_reload_saved, ps_setup_reset_to_default
* 現在のperformance_schema.setup_*の中身をperformance_schema.tmp_setup_*テーブルに保管/リストアしてくれる。
*テンポラリーテーブルに保存するので、コネクションを切ると折角保管したtmp_setup_*は失われる*
* ps_setup_saveは引数が必要。タイムアウトまでの秒数。1でいい。
* ps_setup_reload_savedは引数なし。
* ps_setup_reset_to_defaultは0または1の引数。1を指定すると、実行したクエリーを表示してくれる。
* MySQL 5.7.8のps_setup_reset_to_defaultにはバグがあって上手く動かない。5.7.9なら動く。
  * MySQL Bugs: #77927: sys.ps_setup_reset_to_default fails due to ENABLE/HISTORY column in setup_actors

### sys.ps_truncate_all_tables
* 現在までの統計情報(performance_schemaの*summary*と*history*)をクリアする。
* 引数は0または1、1にすると実行されたTRUNCATE文が表示される。


最近作ったMySQL 5.6はperformance_schemaを有効にしてて、結構便利に使ってます。sys。

2015年10月16日金曜日

MySQLのrootのパスワードを忘れてしまった…やその類似ケースを、mysqldを停止せずに何とかするメモ

MySQLのrootパスワード忘れた、をググると、--skip-grant-tables を有効にして再起動せよ、というのにぶち当たるのが普通なんですが、カジュアルに再起動する訳にいかないことってあるじゃないですか。

そんなときのTIPS。


まず、ダミーのデータディレクトリをmysql_install_dbで作ります。これはrpmで入れた環境なので、/usrはbasedirです。

$ cd /usr
$ bin/mysql_install_db --no-defaults --datadir=/home/mysql/dummy


ここに、パスワードを変えたいMySQLのmysql.userテーブルをコピーします。少なくともMySQL 5.7.8現在、mysql.userはまだMyISAMなのでコピーできます。
rootが使えないはずなのにmysql.userテーブルがゴリゴリ更新されているような環境があるはずはないと信じているので、取り敢えずはシンプルなcpで大丈夫です。

コピーしたらダミーのデータディレクトリを使ってMySQLを起動します。--socketオプションを忘れると本番のソケットファイルが握りつぶされる可能性があるので注意。。

$ cp -ip /var/lib/mysql/mysql/user.MYD /home/mysql/dummy/mysql/user2.MYD
$ cp -ip /var/lib/mysql/mysql/user.MYI /home/mysql/dummy/mysql/user2.MYI
$ cp -ip /var/lib/mysql/mysql/user.frm /home/mysql/dummy/mysql/user2.frm
$ bin/mysqld_safe --no-defaults --datadir=/home/mysql/dummy --port=23000 --socket=/home/mysql/dummy/mysql.sock &
$ bin/mysql -uroot -S/home/mysql/dummy/mysql.sock

mysql> SELECT user, host, password FROM mysql.user2;
+-------------+-----------------+-------------------------------------------+
| user        | host            | password                                  |
+-------------+-----------------+-------------------------------------------+
| root        | localhost       | *........................................ |
| xxxxxxxxx   | localhost       | *........................................ |
| root        | 127.0.0.1       | *........................................ |
| xxxxxxxx    | xxx.xxx.xxx.%   | *........................................ |
| xxxxxxxxxxx | localhost       | *........................................ |
| xxxx        | localhost       | *........................................ |
| xxxxx       | xxx.xxx.xxx.%   | *........................................ |
| xxxxx       | xxx.xxx.xxx.xxx | *........................................ |
+-------------+-----------------+-------------------------------------------+
8 rows in set (0.00 sec)


この通りコピーできたので、UPDATEステートメントでパスワードを書き換えます。

mysql> UPDATE mysql.user2 SET password= PASSWORD('Do_you_love_Perl6?') WHERE (user, host)= ('root', 'localhost');


更新できたらダミー側のMySQLを落とし、落とせない方のMySQLのmysql.userテーブルを上書きします(といってもバックアップは取っておいた方が良かろうかと)


$ bin/mysqladmin -uroot -S/home/mysql/dummy/mysql.sock shutdown
$ cp -ip /home/mysql/dummy/mysql/user2.MYD /var/lib/mysql/mysql/user.MYD
$ cp -ip /home/mysql/dummy/mysql/user2.MYI /var/lib/mysql/mysql/user.MYI

さてこの状態だと、「mysql.userテーブルにUPDATEはかかっているけどFLUSH PRIVILEGESが必要な状態」であり、「でもFLUSH PRIVILEGESするためのrootに接続できない」状態になるかと思います。
そこでkill -HUPですよ奥さん。


$ pkill -HUP mysqld
$ pkill -HUP mysqld

SIGHUPを受け取ると このへんのコード を通って、"FLUSH PRIVILEGES"や"FLUSH HOSTS", "FLUSH LOGS"その他と同じ処理が流れるので、MySQL上のアカウントにアクセスできなくても無事"FLUSH PRIVILEGES"できる。

…コード上通ってそうなのに、何故か2回SIGHUP送らないと反映されない(5.1でも5.6でも5.7でも…なんだろう。同じif文の中のmysql_print_statusはちゃんと呼ばれてエラーログにデバッグ情報吐いてるのに。。)ので取り敢えず2回やっている。深く考えてはいない。


この手段を一番よく使うのはアレですね。
ソケットファイルが何故か消えてて、--skip-name-resolveでroot@localhostはあるけどroot@127.0.0.1がないから--protocol=tcpで逃げられない時に、root@127.0.0.1を作るのに使います。。


【2015/10/16 15:43】



その通りでした! ありがとうございます!!
https://github.com/mysql/mysql-server/blob/5.6/sql/sql_reload.cc#L56-L69

2015年10月15日木曜日

日本MySQLユーザ会の15周年パーティーのおさそい

来る2015/10/30(金)、日本MySQLユーザ会(MyNA)の15周年パーティーがあります。

日本MySQLユーザ会会15周年記念パーティー - connpass


15年に至るまでの流れとかは とみたさん

OSS貢献者賞を頂いたので昔話をします - @tmtms のメモ

とか




が詳しいです。俺も自分が参加するまで(4年くらい前?)の歴史はこれらで知る限りです。

というわけで、今回の幹事の 坂井さん に、「折角節目の年なので、おじさんたちを並べて思い出話(あの頃はあーだったこーだった)を聞きたいです!」という話をしていたら、 俺はおじさんの方にカウントされていた という衝撃的なことになりました。

イベントページ には当日どんなセッションがあるのかイマイチ書いてないのですが、わたしが把握している範囲ではこんなイベントになりそうです。

* 飲食出るよ
* セッションを聞くためのエリア(前の方に椅子)と歓談エリア(後ろ側で立食?)があるよ
* スピーカーは「思い出話」っぽいものをするよ(邪魔さえしなければ後ろの方で他の人と話しててもいいよ)
* 時間に余裕があればLTとかもやれるといいなーって話はしてるよ(できるかわからんですが)

なんか自分で書いててもどんなのになるんだかちょっとよくわかりません。
スピーカーの持ち時間は20分だと聞いていたので、後半時間空くのかなーと思ってたら「全然そんなことはない。時間配分超がんばってる。持ち時間15分になるかも」との返答。
どんな風に進行するのか、他人事のように楽しみにしておこうと思います。

取り敢えず、技術寄りよりは人に会う会になるのかなぁ。。という雰囲気を感じるので、わたしは技術的な話は封印して、「MySQLユーザ会のこれまでとこれから 2015」っぽいものとかやろうかなと思っています。が、順番最後だからカブったら悲惨だな。。

みなさまのご来場をお待ちしております :)


【2015/10/15 13:06】
おっと…肝心なことを。

MySQLのおじさんたちと何らかのつながりを持ちたい人の参加を募集しています :)
MySQLおじさんズと知り合いになると

* イベントのCfP紹介してくれたりする(かもしれない)
* MySQL関連イベントでぼっち回避率が上がる(といいな)
* Twitterでの個人的なつぶやきに マサカリが㌧でくるようになる 間違いがあれば訂正のツッコミを入れてくれる(ような世界線もある)
* 飲み会は割り勘

などなど、いいことがあるかも知れないしないかも知れないです。
興味がある人は、Twitterのアイコンを名札にしようかと思っているので 「ピンク色の ウーパールーパー とうふ」 っぽい奴を探していただければ幸いです :)

2015年10月8日木曜日

Amazon RDS for MariaDBでできそうなこと、できなさそうなこと

TL;DR

Amazon RDS の MariaDB - Amazon Relational Database Service の方が詳しいですたぶん。
* InnoDBじゃなくてXtraDBが使いたい場合はアリです。
* スレッドプールが使いたい場合もアリです。
* サードパーティーなプラグインは使えなくなっています。残念。
* 単にユーザーとして観測できる範囲で調べて推測しているだけなので、違ったらごめんなさい。


MariaDB 10.0で使えるプラグインの一覧はinformation_schema.all_pluginsで参照できる。

mysql> SELECT plugin_name FROM ALL_PLUGINS order by 1;
+-----------------------------+
| plugin_name                 |
+-----------------------------+
| ARCHIVE                     |
| Aria                        |
| AUDIT_NULL                  |
| auth_0x0100                 |
| binlog                      |
| BLACKHOLE                   |
| cleartext_plugin_server     |
| CSV                         |
| daemon_example              |
| FEDERATED                   |
| FEEDBACK                    |
| InnoDB                      |
| InnoDB                      |
| INNODB_BUFFER_PAGE          |
| INNODB_BUFFER_PAGE          |
| INNODB_BUFFER_PAGE_LRU      |
| INNODB_BUFFER_PAGE_LRU      |
| INNODB_BUFFER_POOL_STATS    |
| INNODB_BUFFER_POOL_STATS    |
| INNODB_CHANGED_PAGES        |
| INNODB_CMP                  |
| INNODB_CMP                  |
| INNODB_CMPMEM               |
| INNODB_CMPMEM               |
| INNODB_CMPMEM_RESET         |
| INNODB_CMPMEM_RESET         |
| INNODB_CMP_PER_INDEX        |
| INNODB_CMP_PER_INDEX        |
| INNODB_CMP_PER_INDEX_RESET  |
| INNODB_CMP_PER_INDEX_RESET  |
| INNODB_CMP_RESET            |
| INNODB_CMP_RESET            |
| INNODB_FT_BEING_DELETED     |
| INNODB_FT_BEING_DELETED     |
| INNODB_FT_CONFIG            |
| INNODB_FT_CONFIG            |
| INNODB_FT_DEFAULT_STOPWORD  |
| INNODB_FT_DEFAULT_STOPWORD  |
| INNODB_FT_DELETED           |
| INNODB_FT_DELETED           |
| INNODB_FT_INDEX_CACHE       |
| INNODB_FT_INDEX_CACHE       |
| INNODB_FT_INDEX_TABLE       |
| INNODB_FT_INDEX_TABLE       |
| INNODB_LOCKS                |
| INNODB_LOCKS                |
| INNODB_LOCK_WAITS           |
| INNODB_LOCK_WAITS           |
| INNODB_METRICS              |
| INNODB_METRICS              |
| INNODB_SYS_COLUMNS          |
| INNODB_SYS_COLUMNS          |
| INNODB_SYS_DATAFILES        |
| INNODB_SYS_DATAFILES        |
| INNODB_SYS_FIELDS           |
| INNODB_SYS_FIELDS           |
| INNODB_SYS_FOREIGN          |
| INNODB_SYS_FOREIGN          |
| INNODB_SYS_FOREIGN_COLS     |
| INNODB_SYS_FOREIGN_COLS     |
| INNODB_SYS_INDEXES          |
| INNODB_SYS_INDEXES          |
| INNODB_SYS_TABLES           |
| INNODB_SYS_TABLES           |
| INNODB_SYS_TABLESPACES      |
| INNODB_SYS_TABLESPACES      |
| INNODB_SYS_TABLESTATS       |
| INNODB_SYS_TABLESTATS       |
| INNODB_TRX                  |
| INNODB_TRX                  |
| MEMORY                      |
| MRG_MyISAM                  |
| MyISAM                      |
| mysql_native_password       |
| mysql_old_password          |
| partition                   |
| PERFORMANCE_SCHEMA          |
| qa_auth_interface           |
| qa_auth_server              |
| simple_parser               |
| test_plugin_server          |
| unix_socket                 |
| XTRADB_INTERNAL_HASH_TABLES |
| XTRADB_READ_VIEW            |
| XTRADB_RSEG                 |
+-----------------------------+
85 rows in set (13.21 sec)

InnoDB関連が2つずつあるのは、片方がInnoDB(ただしNot Installed)でもう片方がXtraDB(Active)だから。
Mroonga, TokuDB, Sphinx, handlersocketなどのサードパーティープラグインは *この一覧にそもそも出ていない* ので利用できないようになっているっぽい。残念。

吊るしのMariaDBをインストールしてある(と思われる)ConoHaのMariaDBを試した時のall_pluginsの中身は 日々の覚書: 新ConoHaのMariaDBを試してみた にあるので割愛。結構減ってるのがわかる。

userstat は使えない。performance_schema有効にしろってことかしらん。

とはいえMariaDBは スレッドプール が組み込まれているので、パラメーターグループいじるだけで使えるのは素敵。thread_handling= pool-of-threads だけで有効にできるはず(試してはいない)

あとはInnoDBではなくXtraDBなので、XtraDB由来の機能は結構使えるはず。



って言ってる最中にここの存在を知る。
Amazon RDS の MariaDB - Amazon Relational Database Service

Progress Reportってクライアント側の実装も必要だから、踏み台になるEC2にはMariaDBのクライアントを用意しておいた方が良さげ。
MySQL 5.7のgenerated columnに相当する virtual column も使えるらしい。というかこれ5.2から使えたの…知らなかった…。Dynamic Column の方はよく紹介されるけど、これは知らなかった。Dynamic Columnはもちろん使える。

普通に使うなら使えそうですねー(普通に使うならMySQLでもいいという感じはするけど)

2015年10月7日水曜日

CentOS 6.6ベースでMySQL 5.6 + Mroongaを簡単に試すDockerfile

書きました。--no-cacheでビルドすれば、ビルドした時点の最新のMySQL 5.6と最新のMroongaでビルドされるはず。


FROM yoku0825/cent66:init

RUN echo "NETWORKING=yes" > /etc/sysconfig/network
RUN yum install -y which
RUN rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
RUN rpm -ivh http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm
RUN yum install -y mysql-community-mroonga groonga-tokenizer-mecab
RUN service mysqld start && mysql -e "GRANT ALL ON *.* TO root@'%' WITH GRANT OPTION"

EXPOSE 3306
ENTRYPOINT /usr/sbin/mysqld --user=mysql

https://gist.github.com/yoku0825/9f76b7f112bc89903999


yoku0825/cent66:init は my_script/make_centos_baseimage_for_docker.sh at master · yoku0825/my_script を使って作った必要最小限(のように見える)CentOS 6.6のイメージで、whichとか入ってないのでこのDockerfileの中で入れてる。

CentOS 6.xならFROMの部分を変えればそのまま動くので、好きなベースイメージにしてあげればいい。
CentOS 7.xの場合、mysql-community-releaseのrpmファイルが変わるのでここ からRHEL 7用のやつに書き換えないといけないのと、serviceコマンドが動かない(CentOS 6.5の上のカーネルで動かしているので、systemctlも動かせなかった)ので、なんかもうちょっと書き換える必要がある。


しかし、Dockerfile作ったって言うこともなさそうなほどお手軽にインストールできるようになったなぁ。。MroongaがMySQLの公式yumリポジトリーに対応してくれたのが大きい。


Docker Hubにpushしてみたので、


$ sudo docker run -d yoku0825/cent66:mroonga508

とやるとおもむろにMroongaを試せるようになっております。

2015年10月2日金曜日

MySQL 4.0からあるMAX_QUERIES_PER_HOURという機能について

MySQL CasualのSlack でふと まえあつさん が発した一言から、気になったので調べてみました。意外と知られてないんですね。

MAX_QUERIES_PER_HOUR count、MAX_UPDATES_PER_HOUR count、および MAX_CONNECTIONS_PER_HOUR count 制限は、いずれかの特定の 1 時間の間にこのアカウントに対して許可されるサーバーへのクエリー、更新、および接続の数を制限します。(結果がクエリーキャッシュから得られたクエリーは、MAX_QUERIES_PER_HOUR 制限に対してカウントされません。) count が 0 (デフォルト) である場合、これは、このアカウントに対する制限が存在しないことを示します。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.7.1.4 GRANT 構文


5.6まではGRANTステートメントで指定する。ユーザー固有の属性(mysql.userにカラムがある)なので、グローバル権限(GRANT .. ON *.*)でしかつけられない。

mysql56> SHOW GRANTS;
+-------------------------------------------------------------------+
| Grants for yoku0825@%                                             |
+-------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'yoku0825'@'%' WITH MAX_QUERIES_PER_HOUR 10 |
| GRANT ALL PRIVILEGES ON `d1`.* TO 'yoku0825'@'%'                  |
+-------------------------------------------------------------------+
2 rows in set (0.00 sec)


で、こうなる。mysqlコマンドラインクライアントは接続する時に内部的に`SELECT @@version;`を投げているので、1コネクションに対して2クエリー、5回接続してクエリーを投げたら終了(てことは、O/Rマッパー使ってる環境でこれやるともっと少なくなるだろうなあ。。)

$ while true ; do mysql56 -uyoku0825 -e "SELECT 1" d1 || break; done
+---+
| 1 |
+---+
| 1 |
+---+
+---+
| 1 |
+---+
| 1 |
+---+
+---+
| 1 |
+---+
| 1 |
+---+
+---+
| 1 |
+---+
| 1 |
+---+
+---+
| 1 |
+---+
| 1 |
+---+
ERROR 1226 (42000) at line 1: User 'yoku0825' has exceeded the 'max_questions' resource (current value: 10)

いくつかメモ。

* この制約はアカウント単位(user@host)でかかるので、yoku0825@%とyoku0825@localhostは別人でカウントされるのにエラーメッセージには`User 'yoku0825'`としか出てくれない。
* `FLUSH PRIVILEGES`ステートメントでクリアできる。
* GRANTステートメント上はMAX_QUERIES_PER_HOURなににエラーメッセージには'max_questions'ってかかれてて、GRANTにmax_quiestions_per_hourって書いてアレーってなった。
* ↑に書いた通り、接続時に内部的に暗黙のクエリーを投げてることが多い(接続元の実装依存)ので、綺麗に計れないかも知れない


判定のロジックはcheck_mqhにあって、
mysql-server/sql_connect.cc at 253084f78a60816ce19430cff00f861c520830c5 · mysql/mysql-server

check_mqhを呼んでいるのはmysql_parse(MySQLコマンドパケットのパース)
mysql-server/sql_parse.cc at 253084f78a60816ce19430cff00f861c520830c5 · mysql/mysql-server

check_mqhの中でtime_out_user_resource_limitsを呼んでいて、コイツはreset_utimeと現在時刻を比較して、3600秒経っていたら、カウンターをクリアして、reset_utimeに新しく現在時刻を指定する。
mysql-server/sql_class.cc at 253084f78a60816ce19430cff00f861c520830c5 · mysql/mysql-servertime_out_user_resource_limits

という訳で、一律00分にクリアーみたいなものではない。あんまり使い方思いつかないけど、調べてるのは楽しかった。ごちそうさまでした。