MySQLPlusのユースケースについて、つらつらと考えてみた。
MySQLPlusはMySQLに非同期でクエリを投げられるドライバで、ちょっと見ただけだと「パフォーマンスが上がるかも」と思いそうだが、使いどころが難しい。
使いどころが難しい理由は
- ResultSetの取得でブロックされる
- 同じコネクションでは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) {