その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

mysql Connector/J の設定で効きそうなやつ

mysql Connector/J を使う時に色々効きそう (あるいは効かなそう) な設定項目を調べたのでまとめておくこととします.なお,対象としている mysql のバージョンは5.6 (以降),Connector/J のバージョンは5.1.34 (以降) です.
Connector/J は設定できる項目が多くて迷ってしまうんですが,道標となる資料も幾つかあるのでそれについてもまとめておきます.

何か追記すべき事項があったら適宜追加してゆきたいと思っています.「これも入れたほうが良いのでは無いか」というようなご指摘ありましたらご一報ください.

設定項目

characterEncoding

おなじみの文字コード.要設定.
ちなみに以下の様なテクがある.

Driver doesn't support utf8mb4 for servers 5.5.2 and newer. The driver now auto-detects servers
configured with character_set_server=utf8mb4 or treats the Java encoding "utf-8" passed via
"characterEncoding=..." as utf8mb4 in the "SET NAMES=" calls it makes when establishing the connection.

MySQL Bugs: #54175: Connector/J cannot handle supplemental characters supported by utf8mb4

mysqld のサーバサイドで "utf8mb4" の文字コード設定をしておいた上で,この項目 (characterEncoding) に "utf8" の設定を渡してやると,ドライバがサーバの設定を自動検出して,コネクションを確立するときに "SET NAMES=utf8mb4" を実行してくれるということみたい.

characterSetResults

なぜか忘れられがちな方の文字コードの設定だがこちらも設定した方が良さそう.

alwaysSendSetIsolation

この項目が true になっていると常にトランザクション分離レベルをサーバに送信することになる.
これは非効率的っぽいので false にすると良さそう.

elideSetAutoCommits

この項目が true になっている場合,Connection.setAutoCommit(boolean) が実行された場合に,サーバとドライバの autocommit の state が異なるときにだけ set autocommit=n を発行するようになる.
つまり,Connector/J はデフォルトだと set autocommit=n をついつい発行しがちなのだけれど,この項目が true になっているとむやみにそれを発行することを抑制することが出来るということのよう.

useServerPrepStmts

この項目が true になっていると server side prepared statement を使うようになる.
MySQL (Connector/J) の server side prepared statement はあんま意味が無さそうな上に正しく扱わないと危険 (memory leak にも似た症状が出る) なので有効にするなら自己責任で,という感じがある.
ちなみに Connector/J のデフォルト値は false (昔のデフォルト値は true だったが途中で「アカン」となって false になったという過去があるとのこと).

See Also:

なお, true にした場合,prepared statement が close されずに leak するというバグがあるとのこと.


cacheServerConfiguration

これを true ににすると,mysql サーバの設定をキャッシュするようになる (SHOW VARIABLESSHOW COLLATION の結果をキャッシュしている様子).
これが false だと connection を確立する時に何個かクエリを飛ばしてサーバの状態を確認するようになってしまうので,connection establish のコストを安く済ませる為に true にすると良さそう.
ちなみにこれを有効にするのは Java 界では常識とのこと!

See also:
MySQL Connector/J を利用するときは cacheServerConfiguration=true を設定する - tokuhirom blog

useLocalSessionState

true にすると autocommit やトランザクション分離レベルをサーバに問い合わせずにローカルの情報を元に判断するようになる.
コストを安くするために true にしておくと良さそう.

maintainTimeStats

サーバへの接続が失敗した時などに、より詳細なエラーメッセージを出力する為に,ドライバはアイドル時間の計算などをしており,そのためにさまざまな内部タイマなどが走っている.
この項目を false にするとそれらの処理をバイパスすることができ,スループットが向上するとのこと.

useUnbufferedInput

true にするとサーバからのレスポンスを読むときに BufferedInputStream を使わなくなる.
false にすると recv() システムコールが減るという記述があるが,ホイホイ false にしてしまって良いものかちょっと判断がつかない.攻めてる気がする.

useReadAheadInput

true にすると,サーバからのレスポンスを読むときに最適化されたノンブロッキングな buffered input stream を使うようになる.
useUnbufferedInput と同じく false にすると recv() システムコールが減るという記述があるが,ホイホイ false にしてしまって良いものかちょっと判断がつかない.攻めてる気がする.

jdbcCompliantTruncation

JDBC Connection Overheads
ここに書いているような感じで,false に設定してやると要らんクエリ発行が減る様子.

jdbcCompliantTruncation のドキュメント読んでると *1,false にしてしまったが最後,ヤベークエリが飛んでもスルーされるようになってしまいそうだけれど,STRICT_TRANS_TABLES が有効になっているとその点は防げる感じで,結果的に要らんクエリが減るだけとなった.めでたしめでたし.
STRICT_TRANS_TABLES が有効になっている場合は false にしても良いんじゃないでしょうか.

rewriteBatchedStatements

true になっていると,マルチステートメントの INSERT/UPDATE をまとめるというやつ.
PreparedStatement の addBatch() や executeBatch() を使ってクエリを作っている時に役に立つ.それ以外の時は関係がない.
そうした機能を使うのであれば有効にすると良さそう.ちなみにこの機能を有効にするためには,同時に useServerPrepStmts が false である必要がある (Connector/J のレイヤで Prepared Statement を構築しなければならないため).

See also:
MySQL Connector/Jにおける大量INSERTのチューニング - SH2の日記

cachePrepStmts

HikariCP が有効にすることを推奨している.true に設定することで Prepared Statement のキャッシュを有効になる.

prepStmtCacheSize

HikariCP が設定することを推奨している.Prepared Statement を最大何個キャッシュするかを設定.HikariCP オススメは250.

prepStmtCacheSqlLimit

HikariCP が設定することを推奨している.Prepared Statement の SQL クエリの文字長 (バイト数) の最大.HikariCP オススメは2048.

参考になる資料

MySQL :: MySQL Connector/J Developer Guide :: 5.1 Driver/Datasource Class Names, URL Syntax and Configuration Properties for Connector/J

Connector/J の設定可能項目が全て載っているドキュメンテーション.基本的にこれを読んでいくことになる.

MySQL :: MySQL Connector/J Developer Guide :: 5.1.1 Properties Files for the useConfigs Option

同じく Connector/J のドキュメント.ざっくりとした設定例みたいなのが載ってて参考になる.鵜呑みにするとかなり攻めた設定になりそう.

MySQL Configuration · brettwooldridge/HikariCP Wiki · GitHub

HikariCP を使う上で有効にすべき設定が載っている.

JDBC Connection Overheads

Connector/J がウッカリ発行してしまう不要なクエリを減らす法などが書いている.

http://assets.en.oreilly.com/1/event/21/Connector_J%20Performance%20Gems%20Presentation.pdf

Connector/J を使う上でパフォーマンス改善に役立ちそうな情報がまとまっている pdf.Sun 時代の資料だが参考になる.

*1:Should the driver throw java.sql.DataTruncation exceptions when data is truncated as is required by the JDBC specification when connected to a server that supports warnings (MySQL 4.1.0 and newer)? This property has no effect if the server sql-mode includes STRICT_TRANS_TABLES.

HikariCP で leakDetectionThreshold を設定して connection leak を検出する

HikariCP のような connection pool を利用する上で犯しがちなミスとして,connection leak (pool から connection を borrow しっぱなしで pool に返却しないこと) があると思います.connection leak を放っておくと pool 内の connection が枯渇してにっちもさっちもいかなくなるため再優先で修正しなければなりません.


connection leak を修正するには,まず connection leak の発生を検出する必要があります.
以前紹介したような,JMX を使って connection の状態をモニタリングする方法 もありますが,この方法では leak が発生した瞬間を検出することが出来ませんし,どこで発生したかもわかりませんからデバッグが難しくなります.
そこでどうするかというと,HikariCP を使っている場合は leakDetectionThreshold を設定すればグッとデバッグが便利になります.

setLeakDetectionThreshold() を用いてしきい値を設定すると,「connection が borrow されてから pool に返却されるまでの時間」がそのしきい値を超えた場合に,HikariCP は error log としてスタックトレースを出力します *1.これによっていつ,どこで connection leak が発生したのかを検出することが可能になります.

コード的にはこんな感じ.

final HikariDataSource dataSource = new HikariDataSource();
...
dataSource.setLeakDetectionThreshold(5000);

こうしておくと,borrow された connection がsetLeakDetectionThreshold() で設定した 5000ms (5sec) を超えても pool に返却されない時にスタックトレースが出力されるようになります.便利! これで connection leak も直したい放題ですね.

ちなみに実環境では,

final HikariDataSource dataSource = new HikariDataSource();
...
if (env.isDevelopment()) {
    dataSource.setLeakDetectionThreshold(5000);
}

という風にしていて,開発環境下でのみ検出を有効にしています.Java で素朴にスタックトレースを取るのは割とコストが高いというのと,leak を潰すのは開発中にあらかた出来るだろう,加えて JMX で connection の状態をモニタリングしているので実際の運用ではそれで十分だろうという判断からです.


connection leak というのは connection pool を利用する上で起こしやすいバグですし,かつデバッグが難しい部類のバグだと感じています.
採用する connection pool を選定する上で, そうした leak の検出やデバッグを容易にする機能が含まれているかどうかは割と重要な要因だと思います.HikariCP にはそういった connection leak のデバッグを助けるオプションが存在するので便利で良いですね.

*1:This property controls the amount of time that a connection can be out of the pool before a message is logged indicating a possible connection leak. A value of 0 means leak detection is disabled. Lowest acceptable value for enabling leak detection is 2000 (2 secs). Default: 0 https://github.com/brettwooldridge/HikariCP

HikariCP で kamipo TRADITIONAL を有効にする

さて2015年も暮れになってきましたので,各位に於かれましては常識の如く kamipo TRADITIONAL を有効にしていることと存じます.
kamipo TRADITIONAL とはなんぞや,という方は以下のエントリが参考になりますからご一読下さい.


さて Java で kamipo TRADITIONAL を有効にするテクについては

上記のエントリで紹介したわけですが,21世紀の我々には文明があるので connection pooling というものを使う (あるいは使えと言われる) ケースが多くなっております.

ということで HikariCP を使うわけですが,この時に kamipo TRADITIONAL をどう設定するのかという話題があります.connection を borrow する度に sql mode を set するというのも渋いので,以下のようにしてやるとよろしい.

final HikariDataSource dataSource = new HikariDataSource();
...
// This is "kamipo TRADITIONAL". More strict, healthy, nice.
// https://github.com/kamipo/etcfiles/blob/b8d7f2dc93567cb3de486197952ac8b048641d31/etc/my.cnf#L28
dataSource.setConnectionInitSql("SET SESSION sql_mode = 'TRADITIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY';");

HikariDataSource#setConnectionInitSql() を使うと,connection を initial state でセットアップする時にだけ発行する sql を指定できるので *1,ここで kamipo TRADITIONAL を設定するようにしてやると connection が作られるタイミングで kamipo TRADITIONAL が有効になり,以降はその kamipo connection *2 を使い回すことが可能となります.


簡単ですね!

*1:This property sets a SQL statement that will be executed after every new connection creation before adding it to the pool. If this SQL is not valid or throws an exception, it will be treated as a connection failure and the standard retry logic will be followed. Default: none https://github.com/brettwooldridge/HikariCP#infrequently-used

*2:kamipo TRADITIONAL が有効になった connection という意味です.流行させたい語です

HikariCP のステータスを JMX 経由で取得する

HikariCP を使っていると,connection の状態をモニタリングしておきたくなると思います.例えば active な connection と idle 中の connection の比率を見たかったり,あるいは connection がリーク (アプリケーションが connection を borrow するだけして返却してない状態) していないかをモニタリングしたい,みたいなケースが挙げられるでしょう.
こうしたものはモニタリングしたくなるのが心情というものですし,モニタリングすべきだとも思っています.


開発中の環境だと HikariCP のデバッグログを出すことでそこから connection の情報を取得することが出来ますが,本番の環境だとそんなのは出さないし (HikariCP のデバッグログは結構量が多い) ,出したとしてもいちいちログを見てやっていくのはダルいので,やはり JMX で見たいという事になります.
そのやり方は至って簡単で,

final HikariDataSource dataSource = new HikariDataSource();
...
dataSource.setPoolName("pool-name");
dataSource.setRegisterMbeans(true);

という風に,dataSource.setRegisterMbeans(true); としてやることで HikariCP のメトリクスが JMX で取れるようになります *1


あとは jolokia なんかを使っている場合だと,

http://server.example.com/jolokia/read/com.zaxxer.hikari:type=Pool%20(pool-name)

という風にアクセスしてやると HikariCP の connection のステータスを取得することができるようになります.


あとは数値を取ってこういうグラフを描画することが可能.

f:id:moznion:20151213013152p:plain

Total の connection の個数を見つつ,その上に active,idle,awaiting な connection の状態を描画することで,connection のステータスが一目瞭然となります (ちなみに18:00 を超えた辺りで total の connection がガクッと落ちているのは再起動したからです).
便利ですね.


See Also: MBean (JMX) Monitoring and Management · brettwooldridge/HikariCP Wiki · GitHub

*1:registerMbeans: This property controls whether or not JMX Management Beans ("MBeans") are registered or not. Default: false https://github.com/brettwooldridge/HikariCP

ねぎま鍋、ほうれん草とツナのマヨネーズあえ

今日も定時退社することに成功しました.嘘です,実際には45分ほど残業をしましたがこれは定時と言っても遜色はありません.最高の事態です.

というわけで自炊にも成功しています.

マグロの切り落としが300gで340円という破格でしたので調達しました.季節も季節なのでねぎま鍋とします.
醤油大さじ3,酒大さじ3,みりん大さじ1,白だし適量という感じで漬けます.ある程度放置.その間に飯でも炊きましょう.

昨日のほうれん草が残っているので,飯が炊けるのを待つ間に酒の友を作りましょう.幸いなことに良いツナ缶もあります.このツナ缶は id:papix さんからもらったものです.

ほうれん草を茹でたものにツナ缶を1缶,マヨネーズ適量,粗挽き胡椒を想像を遥かに超えるくらいガッツリ,醤油を隠し味程度放り込みます.そして頑張って混ぜる.

成果物.

最高に美味い……奇妙にビールが進む,今日はインドの青鬼です.

各界からの反響.

ねぎまに戻ります.長ねぎに焼き色を付けていきます.これは1本分です.

鰹出汁に醤油,塩,酒,みりん,隠し味に味噌を僅かに加えて味を付けた出汁です.

一煮立ちしたら,漬けていたマグロを漬け汁と共に投入し,長ねぎを追います.とろ火でじっくり煮る.煮る時間は適当で良いと思います.あくが出たら適宜取ります.

味がしみたと確信した時が食べごろなので椀などによそいます.

美味い!!!! 飯だけでなく日本酒も進むのです.


明日はこうです.


[追記]

間違えて技術的なブログの方に書いていた……

普段はこういった事はこっちに書いています.

http://moznion.hatenablog.com



情報共有システム (Wiki みたいなやつ) に求めること

いま所属している組織で使っている情報共有システムが僕はいまいち好きではなくて,気に食わないところを twitter にガーッと書いたんだけど,さてここで「良い情報共有システムとは」と考えた時にスッと言語化出来なかったので,僕の思う情報共有システムに求めることをここでまとめておくことにする.

エディタが腐ってない

これは本当に重要で,エディタが腐っていると「Wiki を書こう」という気がそもそも起きないし,起きたとしてもエディタがストレスフルだと文章を書き始めてすぐに嫌になってしまうので最低限エディタはまともである必要がある.さもなくば Wiki は廃墟と化す.
WYSIWYG なエディタを利用するのは難しいと思っていて,本当に使いやすい WYSIWYG エディタを作るというのはかなりコストが高い (WYSIWYG エディタはある程度まで完成度が高まっていないと使い物にならない気がする) ので,システム側がマトモなエディタを用意してくれている可能性は高くはない.あと WYSIWYG の場合は見たままが成果物に反映されるから,誰かにとって使いやすいものでも他の誰かにとっては使いにくいという事が起きやすいと思っていて,組織全体にとっての使いやすさを最大化するのが難しい気がする.
そうなってくると,何らかの記法 (Markdown やそうした類のもの) に対応してそれを用いて書くのが良いのかなと思うし,僕はそれで良いんだけど,エンジニア以外の人にとってそれは本当に使いやすいのかな? という懸念はあって,難しいところだと思う.だからと言って,1つのページについて或る人は WYSIWYG で書いて,また或る人は Markdown で書く,みたいなハイブリッドな方法を採用すると大体カオスな事になって不幸が起きるという印象しかない.しかしこのへんに上手い解決法が提供されると幸せそう.
あとブラウザ内にエディタがあるというのは,エディタがブラウザのキーボードショートカットを奪ったり,或いはブラウザに奪われたりするから難しい気もしていて,そうなると既にある文章を API 経由か何かで送りつけられるのが良いのか,もしくはネイティヴなアプリがあると良いのだろうか? とか思うけどここらへんは良く分からない.一方でブラウザから編集できないと,資料を見ながら「ここ間違えているからスッと直そう」みたいな感じでカジュアルに編集しようとする意識をサポート出来ないから,ブラウザで編集可能である必要自体はありそう.でも GitHub を使っている時にブラウザから README.md を直接編集したくなる事はあまり無い気がする.文化の違いだろうか?

まあ,ストレス無くブラウザでページを編集できれば問題は無さそう.

ページ内の配置がある程度規約に縛られている

Wiki のページはその人の芸術性とかを反映する必要はあまりないはずなので,ある程度規約で縛られていたほうが良いと思う.
「ページ内のここを見たら必ずこれがある」だとか「時系列はページの上の方が新しくて下に行くほど古い」だとか,そういう基本的なことは全ページで共通していたほうが良くて,それは規約のようなものになっているべきだと思う.そうした規約があることで全てのページで「これが欲しい時はここを見ると良い」という共通の認識ができるので,欲しい情報へのアクセスが高速に行えて良いと思う.

自由に HTML を記述できて,自由な場所に自由に要素を置ける,みたいな機能は Wiki だと地獄を生むと思う.

URL の構造が予想しやすい

これは良い API の設計にも似ているけれど,各ページへの URL があまりに自由だと使いにくいと思う.
例えばプロジェクトのトップページは URL パスの親になっていて,その下にそのプロジェクトの各ページや子プロジェクトのページがぶら下がっていて,子プロジェクトはさらにその構造を持っているみたいな感じが使いやすいと思う.全部がフラットな構造になっていて,ページのタイトルがそのままパーマリンクのパスになっているとかはファンキーだけど使いにくいという感想しか無い.

僕はブラウザの URL 窓にプロジェクト ID を打ち込んで,そこから検索履歴に基いて見たいページを絞り込むという方法で特定のページにアクセスすることが多いので,URL に規則性が無いとそれが出来なくて大変な気持ちになる.

検索可能

説明不要だと思う.21世紀の Wiki全文検索できないと使いものにならない.
あとは「このプロジェクト内の文書だけ検索」みたいな,検索対象を絞れればあまり不満がないけど,検索オプションが多いと便利そう.

添付ファイルをまとめて管理できる

いま所属している組織では PowerPoint や PDF といった様々なファイルが Wiki にアップロードされ,日夜それらのファイルが更新されていきながら,それを見ながら,というスタイルで仕事が行われているので,添付ファイルを一括で管理できると便利.
例えば「企画.pptx」はプロジェクト内のあのページにあるけれど,「デザインガイド.pdf」はプロジェクト内の別のあっちのページにある,みたいな感じになっていてファイルがガンガン増えてくると,いざ「あのファイルが欲しい」となった時にどこに行けば良いのか分からなくて非常にストレスが溜まる.プロジェクト内の添付ファイルを一覧できる機能があれば,とにかくそこに行ってファイルを GET すれば良いので楽だと思う.

他サービスとの連携がしやすい

ページを更新したりした時に,なぜだかメールを飛ばしたがる Wiki が多いように思うけど,メールで飛ばされてもどうせ見ないし,どうしようもないと思う.それだったらチャットやら他のツールやらに飛ばせる方がどう考えても便利だし,その口があらかじめ用意されていると気持ちが良いと思う.更に自分で拡張も書きやすかったらなお良い.

ここまで書いてから気づいたんだけれど,これはイベントに対応する API がちゃんと提供されていれば良いということになった.

その他

と色々書いたのだけれど,Wiki のようなものはエンジニアだけでなく,あらゆる人が使うし,むしろエンジニアでない人の方が多く使う為,エンジニアが使いやすさを追求してもエンジニアでない人にとっては使いにくくなってしまって,全体的な作業効率は悪くなってしまうかもしれない *1.しかし導入する Wiki システムや BTS システムを選定する人は大体エンジニアが中心になっていて,そうした時にどのようにしてフラットな目線で,かつ明確な判断基準を持って選定すべきなのかというところが気になる.

個人的に,情報共有システムについて特に重視すべき点は「記事の作成・更新にストレスが無いかどうか」と「情報へのアクセスがストレス無くスムーズに行えるかどうか」だと思っている.

とにかく僕はここに列挙したような機能が情報共有ツールに付いていてくれると嬉しい.気づいていないだけで,あるいは忘れているだけで,他にもあった方が良い機能はあるかもしれないので,他の人の意見についても大変興味がある.

あと,最高の情報共有システムがあったら教えて下さい.よろしくお願いします.

追記

マジで重要な奴を忘れていた

*1:エンジニアでもない限り,イベント API を使ってアレやコレやしたい,みたいなことは思わないと思うし……

perl6 の環境を手っ取り早く構築してサクッと Hello world する

本記事はサクッと perl6 (rakudo) が動く環境を作って,ズバッと Hello world してみるというやつです.
早速行ってみましょう.

1. rakudobrew をインストールする

rakudobrew をインストールします

rakudobrew というのは perl5 で言うところの perlbrew/plenv,ruby で言うところの rvm/rbenv のようなものです.
なぜこうした物を使うかというと,perl6 は開発が非常に活発な言語であるためにデイリーで変更が入っていて,直っていたり壊れていたりするためです.
2015年10月現在,perl6 をやるときは rakudo と MoarVM は HEAD を使うというのが紳士の嗜みとなっています *1

というわけで入れましょう.

git clone https://github.com/tadzik/rakudobrew ~/.rakudobrew
echo 'export PATH=~/.rakudobrew/bin:~/.rakudobrew/moar-nom/install/share/perl6/site/bin:$PATH' >> ~/.bash_profile
echo 'eval "$(rakudobrew init -)"' >> ~/.bash_profile
exec $SHELL -l
2. MoarVMをビルドする

MoarVM というのは perl6 の VM です.これをビルドするとその VM 上で rakudo を走らせられるようになります.

rakudobrew build moar

そこそこ時間がかかります.待ちましょう.

3. panda をインストールする

panda というのは perl5 で言うところの cpan/cpanm,ruby で言うところの gem のようなものです.
別にこれを入れなくても Hello world 程度なら可能ですが,現実的にライブラリが使えないと話にならないので入れます.

rakudobrew build-panda
4. プログラムを書く

ここまで来るともう perl6 が実行可能となっていますから,あとは Hello world するだけです.
プログラムを書きましょう.

use v6;

say 'Hello world!';

use v6; というのにテンションの上昇を禁じえませんね!
それでは満を持して実行.

perl6 hello.p6

動きましたか? 動きましたね.やりました! 実績解除です.


これであなたも立派な perl6 野郎です.
あとは Perl 6 Documentation を覗いたり,Perl 6 Design Documents を愛でたりして頑張りましょう!
perl6-users-jp/perl6-examples · GitHub を眺めると,perl6 の全体を俯瞰できて良いかもしれませんね.


以上です.

*1:バージョンの切り替えとかも出来るっぽいけど使ったことがない.常に HEAD で生きているから……