yoskhdia’s diary

DDDとかプログラミングとかアーキテクチャとか雑多に

MySQL ResultSetのStreamingとCursorの違い

MySQL Connector/J version 8.0の変更点を見ていたら、Configurationsの説明が5.1の時よりも増えていることを見つけました。 その中で、ようやくStreamingとCursorの違いが分かった気がした*1ので備忘メモです。

ResultSetのStreamingとCursor

MySQLでは大きなResultSetの取得にStreamingとCursorの2つの選択肢があります。 version 5.1のマニュアルversion 8.0のマニュアルも、ResultSet項の説明は一言一句同じです。

Streamingは以下のように、いくつかの属性と setFetchSize(Integer.MIN_VALUE) を指定することで1行ずつの結果取得を有効化することができます。

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
              java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);

この使い方は、Webを検索すれば沢山見つかるのですが、Cursorに関してはストアドプロシージャのカーソルの説明などに埋もれて公式の以下の文章くらいしか中々見つかりません。*2 該当箇所を引用します。

Another alternative is to use cursor-based streaming to retrieve a set number of rows each time. This can be done by setting the connection property useCursorFetch to true, and then calling setFetchSize(int) with int being the desired number of rows to be fetched each time:

説明はこれだけです。 先述のStreamingについては注意事項など、もう少し補足があるのですが、これだけだと何が違うのかよく分かりません。

Configurationsに答えが

ここで冒頭の話に戻って、Configurationsのページを確認すると useCursorFetch プロパティの説明が補足されていることが分かります。

version 5.1では、

If connected to MySQL > 5.0.2, and setFetchSize() > 0 on a statement, should that statement use cursor-based fetching to retrieve rows?

version 8.0では、

Should the driver use cursor-based fetching to retrieve rows? If set to “true” and “defaultFetchSize” > 0 (or setFetchSize() > 0 is called on a statement) then the cursor-based result set will be used. Please note that “useServerPrepStmts” is automatically set to “true” in this case because cursor functionality is available only for server-side prepared statements.

全然違いますね……。*3

ということで

ざっくりまとめると、次のような感じでしょうか。

  • Streaming
    • 1行ずつの取得
    • クライアントサイドで順送りの制御をする(ぽい)(DBサーバでResultSetを持ったまま、Iterator#nextの度に取得と思われる。そのためコネクションは常に接続したままで、結果を読み切るまでは他のクエリを投げることもできない。) *4
  • Cursor
    • 指定した件数ずつの取得
    • DBサーバサイドでプリペアードステートメントを使って制御をする

useServerPrepStmtsはデフォルトfalseとなっているようにtrueにする弊害もある?という感じなのですが、MySQLIssueでは5.1.38で(一部の?)メモリリークの修正がされたみたいでもあります。結局、プログラマがちゃんと検証しましょう、ということなのでしょう…。*5 基本的には前者のStreamingを使う方がベターな気がします(気がします)。

ちなみにMariaDB Connector/Jは

そもそも useCursorFetch なんてプロパティはありません。 setFetchSize で指定したサイズ分で動く分かりやすい挙動になっています。*6 see also: About MariaDB Connector/J - MariaDB Knowledge Base

結論

MariaDB Connector/Jを使いましょう。*7

*1:気がした

*2:ググラビリティが低いのかもしれませんが…

*3:v5.1の説明は、クエスチョンで聞かれても知らんがな、みたいな気持ちになります。

*4:技術的信頼性が低い情報なのでstrike

*5:詳しい人教えてください…

*6:AWS Auroraで試した範囲では

*7:MariaDB自体は今どうなってるの?という感じではありますが、AWS AuroraではMariaDB Connector/Jが紹介されていたり、Auroraに対して使ってみても罠は少ないように感じています。