MySQLPlusのユースケース

MySQLPlusのユースケースについて、つらつらと考えてみた。


MySQLPlusはMySQLに非同期でクエリを投げられるドライバで、ちょっと見ただけだと「パフォーマンスが上がるかも」と思いそうだが、使いどころが難しい。
使いどころが難しい理由は

  1. ResultSetの取得でブロックされる
  2. 同じコネクションではResultSetを同期的に取得しないとおかしなことになる

と考えている。

1.については、例えば以下のようなコードがあったとして

require 'mysqlplus'

my = Mysql.connect('localhost', 'user', 'pass', 'db')

my.send_query("select now(), sleep(5)")

Thread.fork {
  rs = my.get_result
  rs.each {|i| p i }
}

my.query("select * from emps") do |rs|
  rs.each {|i| p i }
end

my.close

一見すると、"select now(), sleep(5)"がブロックされずに"select * from emps"が走りそうだが、get_resultでブロックされるのでうまくいかない。get_resultは拡張ライブラリのメソッドなので、get_resultでブロックされるとプログラム全体が止まる。*1

get_resultをRubyで実装するとか、Thered.forkではなくてProcess.forkを使えばうまくいくかもしれないが、その後は茨の道が待っている気がする。それに2.の問題もある。

2.については、以下のようなコードで

require 'mysqlplus'

my = Mysql.connect('localhost', 'user', 'pass', 'db')

my.query_with_result = false
my.send_query("insert into emps (name, age) values ('yamada', sleep(3))")
my.async_in_progress = false
# async_in_progressをfalseにしないと、get_resultなしにsent_queryを連続して実行することが出来ない
my.send_query("insert into emps (name, age) values ('yamada', sleep(3))")

my.query_with_result = true
my.query("select count(*) from emps") {|rs| rs.each {|i| p i } }
my.query("select count(*) from emps") {|rs| rs.each {|i| p i } }
my.query("select count(*) from emps") {|rs| rs.each {|i| p i } }

my.close

"select count(*) from emps"の結果が3回出力されることが期待されるが、実際には1回しか出力されない。たぶん"insert into emps (name, age) values ('yamada', sleep(3))"の結果がバッファだかキューだかに格納されていて、get_resultでそれらを取得しないと、後続のquery()の結果として返されるのだろう。*2

ということで「通常とは別のコネクションでDBに接続していて、そこにはINSERTを投げるだけ」とかならMySQLPlusが使えるかもしれないと考えている。*3

追記

get_resultしないのはアリなんだろうか?
Threadがキャッシュされてたりするとおかしなことになる気がしてきた。

追記2

えー…

/*
  Send the query and return so we can do something else.
  Needs to be followed by mysql_read_query_result() when we want to
  finish processing it.
*/


int STDCALL
mysql_send_query(MYSQL* mysql, const char* query, ulong length)
{

*1:Ruby 1.9では異なる動作かもしれない

*2:MySQLがどのように結果を保持しているかは追っていない。コードを読まないと…

*3:例えばロギングとか