[groonga-dev,04252] Re: Mroongaの同値でのfast_order_limitでoffsetの結果が壊れる

Zurück zum Archiv-Index

Kouhei Sutou kou****@clear*****
2017年 1月 23日 (月) 14:28:19 JST


須藤です。

In <A15CE****@gmail*****>
  "[groonga-dev,04251] Mroongaの同値でのfast_order_limitでoffsetの結果が壊れる" on Sat, 21 Jan 2017 19:52:22 +0900,
  murata satoshi <murat****@gmail*****> wrote:

> fast_order_limitが有効な場合にoffsetの結果が壊れるケースがありましたので報告します。
> 同値でのorder byの場合に発生するようです。

ありがとうございます!
再現用データもあってとても助かります。

うーん、これはどうするのがいいんでしょうねぇ。

原因はソートキーが同じレコードの順番がGroongaのソートとMySQL
のソートで違うことです。ソートキーが同じなのでどの順番になっ
てもソートに問題はありません。単にGroongaとMySQLで違うことが
原因だというだけです。

MroongaはORDER BY LIMITを最適化するとき、Groongaでソートして
必要最小限のレコードだけ返します。その後、MySQLは必要最小限
のレコードをさらにMySQLでソートして返します。

必要最小限のレコードというのは、ソート後のレコードのうち、
LIMITで指定したオフセット+行数分のレコードです。たとえば、
オフセットが2で行数が7なら先頭9レコードです。

今回の例ではMySQLはレコードが追加された順にソートしているよ
うに見えます。つまり、この順番です。

> +-----+
> | _id |
> +-----+
> |   1 |
> |   2 |
> |   3 |
> |   4 |
> |   5 |
> |   6 |
> |   7 |
> |   8 |
> |   9 |
> |  10 |
> +-----+

一方、Groongaは次の順にソートしていそうです。

> +-----+
> | _id |
> +-----+
> |   1 |
> |  10 |
> |   3 |
> |   5 |
> |   4 |
> |   6 |
> |   9 |
> |   8 |
> |   7 |
> |   2 |
> +-----+

このデータの場合、LIMIT 0, 5だとGroongaは次のレコードを返し
ます。

+-----+
| _id |
+-----+
|   1 |
|  10 |
|   3 |
|   5 |
|   4 |
+-----+

これをMySQLがソートしてこうなります。

+-----+
| _id |
+-----+
|   1 |
|   3 |
|   4 |
|   5 |
|  10 |
+-----+

オフセットが0で行数が5なのでこれをそのままユーザーに返します。

これがLIMIT 5, 5だとGroongaは次のレコードを返します。全部で
すね。

+-----+
| _id |
+-----+
|   1 |
|  10 |
|   3 |
|   5 |
|   4 |
|   6 |
|   9 |
|   8 |
|   7 |
|   2 |
+-----+

これをMySQLがソートしてこうなります。

+-----+
| _id |
+-----+
|   1 |
|   2 |
|   3 |
|   4 |
|   5 |
|   6 |
|   7 |
|   8 |
|   9 |
|  10 |
+-----+

オフセットが5で行数が5なので後ろの5レコードをユーザーに返します。

+-----+
| _id |
+-----+
|   6 |
|   7 |
|   8 |
|   9 |
|  10 |
+-----+

そうすると、ユーザーには2が消えて10が重複しているようにみえ
ます。


MroongaがMySQLにGroongaのソート結果を返すとき、オフセット以
下のレコードのソートキーの値をNULLとして返すといいんですか
ねぇ。こんな感じで。

+-----+-------+
| _id | price |
+-----+-------+
|   1 |  NULL |
|  10 |  NULL |
|   3 |  NULL |
|   5 |  NULL |
|   4 |  NULL |
|   6 |   500 |
|   9 |   500 |
|   8 |   500 |
|   7 |   500 |
|   2 |   500 |
+-----+-------+

そうすればNULLのレコードは必ず先頭にまとまってMySQLがソート
してもこうなるはず。

+-----+-------+
| _id | price |
+-----+-------+
|   1 |  NULL |
|   3 |  NULL |
|   4 |  NULL |
|   5 |  NULL |
|  10 |  NULL |
|   2 |   500 |
|   6 |   500 |
|   7 |   500 |
|   8 |   500 |
|   9 |   500 |
+-----+-------+

で、オフセットが5で行数が5なので後ろの5レコードをユーザーに
返すことになってこう。

+-----+
| _id |
+-----+
|   2 |
|   6 |
|   7 |
|   8 |
|   9 |
+-----+


MySQLに返すデータも少なくなってパフォーマンス的にもメリット
があるかも?だれか実装にチャレンジしてみませんか?

ha_mroonga::storage_store_fields()に値として必ずNULLを設定す
るモードみたいなのをつけて、ha_mroonga::storage_ft_read()で
オフセット以前の場合はそれを呼び出す、みたいにするといけると
思うんですよねぇ。


-- 
須藤 功平 <kou****@clear*****>
株式会社クリアコード <http://www.clear-code.com/>

Groongaベースの全文検索システムを総合サポート:
  http://groonga.org/ja/support/
パッチ採用 - プログラミングが楽しい人向けの採用プロセス:
  http://www.clear-code.com/recruitment/
OSS開発支援サービス:
  http://www.clear-code.com/blog/2016/6/27.html




groonga-dev メーリングリストの案内
Zurück zum Archiv-Index