長いクエリを改善するために、:total_entriesなしでwill_paginateを使用する
-
07-07-2019 - |
質問
paginate_by_sql メソッドを使用してページネーションするコレクションを構築する will_paginate の現在の実装があります。 total_entries のカスタムクエリは非常に複雑であり、データベースに大きな負荷がかかります。したがって、total_entriesをページネーションから完全に削除したいと思います。
つまり、「previous 1 [2] 3 4 5 next」の一般的なページネーション表示の代わりに、単に「next-previous」ボタンのみが必要です。しかし、いくつかのことを知る必要があります。
- 前のリンクを表示しますか?これはもちろん、現在の選択で表示されるレコードより前にレコードが存在する場合にのみ発生します
- 次のリンクを表示しますか?コレクションの最後のレコードが表示されている場合、これは表示されません
行をカウントするためのクエリは 次の場合に自動的に生成されます :total_entriesを指定しないでください。もし、あんたが これで問題が発生します 生成されたSQL で手動でカウントを実行します アプリケーション。
したがって、最終的に理想的な状況は次のとおりです。
- total_entriesカウントを削除すると、データベースに過度の負荷がかかるため
- 次ページ/前ボタンのみを使用してナビゲートし、使用可能なすべてのページ番号を表示する必要がない、半ページ分割で一度に50レコードを表示します
- 次のボタンと前のボタンのみを表示します
同様の問題に取り組んだ人や解決策について考えたことはありますか?
解決
will_paginateは、特にカウントSQLジェネレーターを混乱させる結合が含まれる場合、エントリ数を計算するという非常にひどい仕事をする場合が多くあります。
必要なのが単純なprev / nextメソッドだけである場合、データベースからN + 1エントリを取得することを試み、最後のページにあるN以下しか取得しない場合に必要です。
例:
per_page = 10
page = 2
@entries = Thing.with_some_scope.find(:all, :limit => per_page + 1, :offset => (page - 1) * per_page)
@next_page = @entries.slice!(per_page, 1)
@prev_page = page > 1
これを必要とするさまざまなモデルに含めることができるいくつかのモジュールにこれを簡単にカプセル化するか、コントローラー拡張を作成できます。
これは、デフォルトのwill_paginateメソッドよりも大幅に優れていることがわかりました。
唯一のパフォーマンスの問題は、テーブルのサイズによっては問題になる可能性のあるMySQLの制限です。
何らかの理由で、MySQLで小さなLIMITを使用してクエリを実行するのにかかる時間は、OFFSETに比例します。実際には、データベースエンジンは、特定のオフセット値までのすべての行を読み取り、次のLIMIT個の行を返します。期待どおりに先にスキップすることはありません。
100,000以上の範囲のOFFSET値を持つ大きなデータセットの場合、パフォーマンスが大幅に低下することがあります。これがどのように現れるかは、ページ1のロードが非常に速く、ページ1000がやや遅いが、ページ2000は非常に遅いことです。