MySQL パフォーマンスとtransactd その2の2
前回はselect * from tablename where fieldname = xxx
のfieldnameをキーセグメントの先頭に持つインデックスがない場合を書きました。今回は、インデックスがある場合です。
MySQLでfieldnameフィールドのインデックスがある場合
今回の例は条件式が一つですので簡単です。MySQLはまず、レコードバッファ内のfieldnameフィールドの位置にキー値(xxx)をセットしてhandler::ha_index_read_map(HA_READ_KEY_EXACT)
を呼び出します。
レコードがなければ(エラーならば)検索終了です。レコードが存在する場合、uniqueキーでなければhandler::ha_index_next_same()
をエラーになるまで繰り返し呼び出し、順次レコードを取得します。
handlerでアクセスしたレコード数は、fieldname=xxx
の重複レコードの数だけです。インデックスがあれば、実際に存在するレコード数しかアクセスしないので、パフォーマンスは最大です。(正確には、innodbの場合このキーがprimaryかそうでないかで若干パフォーマンスは異なりますが、重複レコードが少なければ大差ありません。)
アクセスするレコード数が少ないので、仮にinnodbのキャッシュに乗っていなくても、それなりに高速です。また、勝手にキャッシュされるレコード数も最少で、キャッシュメモリを温存できます。インデックスがあると色々お得ですね。
transactdでfieldnameフィールドのインデックスがある場合
クライアントフィルターを使う場合
最初にtable::setKeyNum()
でインデックス番号を指定し、table::setFV(fieldname, xxxx)
でキー値をセットします。その後、uniqueキーならtable::seekEqual()
で、そうでなければtable::seekGreater(orEqual=true)
でレコードにアクセスします。
レコードが見つかればtable::stat()
にゼロが返ります、uniqueキーでない時は、table::seekNext()
で次のレコードに移動してtable::stat()
とフィールドの値を調べます。値が異なるかtable::stat()
がゼロ以外になるまでtable::seekNext()
を繰り返し呼び出します。
最初にインデックスを指定しseekEqual
やseekGreater
といった名前の関数を呼び出すので、コードからパフォーマンスを容易に想像できます。
サーバーフィルターを使う場合
通常はクライアントフィルターで十分ですが、重複値がたくさんあるようなら、サーバーフィルターを使って一気に取得し、通信のオーバヘッドを削減しましょう。
table::setKeyNum()
でインデックス番号を指定し、table::setFV(fieldname, xxxx)
でキー値をセットするまでは同じです。その後、table::setFiletr(fieldname=xxx, rejectCount=1, maxRecords=0)
でサーバーフィルターをセットしてからtable::find()
を呼び出します。
サーバー側では、handler::ha_index_read_map(HA_READ_KEY_OR_NEXT)
を使ってスキャンの先頭に移動します。そして、handler::ha_index_next()
で次のレコードに移動しながら、条件にマッチするレコードかを調べていきます。
今回は対象フィールドのインデックスを指定しているので、レコードはそのインデックスでソートされていることが保証されています。そのため、マッチしないレコードが見つかればすぐにループを抜けるように、rejectCount=1
と指定することがポイントです。ここでもやはり、transactdは、スキャンの開始位置と終了条件を明確にコントロールできます。
まとめ
検索対象のフィールドがインデックスの先頭セグメントにあれば、検索パフォーマンスは最大です。ただし、重複レコードを許可するキーの場合は、重複レコードがどれくらいあるか想定し、パフォーマンスを読みましょう。これはMySQLでもtransactdでも同じです。
transactdでは、重複レコードの量に応じて、クライントフィルターかサーバーフィルターを選択しましょう。
このテーマの最後に、transactdを使った検索のひな形コード(C++)を書いておきます。(他の言語、Ruby ActiveX C# PHP などでも同じ名前のメソッドがあるので、ほとんど同じです。)
次は select * from tablename where fieldname in(a,b,c...)
を取り上げたいと思います。
Transactd インデックスを使った検索(クライアントフィルター)
static const char keynum = 0; tb->clearBuffer(); tb->setKeyNum(keynum); tb->setFV("fieldname", "xxxx"); tb->seekGreater(true/*orEqual*/); while (tb->stat() == 0) { if (strcmp(tb->getFVstr("fieldname"),"xxxx")!=0) break; //ここに見つかった場合の処理を記述します。 tb->seekNext(); }
Transactd インデックスを使った検索(サーバーフィルター)
static const char keynum = 0; tb->clearBuffer(); tb->setKeyNum(keynum); tb->setFilter("fieldname = xxxx", 1/*rejectCount*/, 0/*maxRecords*/); tb->setFV("fieldname", "xxxx"); tb->find(tbale::findForword); while (tb->stat() == 0) { //ここに見つかった場合の処理を記述します。 tb->findNext(); }