用传统的LIMIT m, n
做分页查询需要这么几步:
用SELECT COUNT(*) FROM table WHERE condition ORDER BY ...
查到总数并算出有多少页;
用SELECT columns FROM table WHERE condition ORDER BY ... LIMIT 0, 100
显示第一页(假设每页有100行),如果用SQL_CALC_FOUND_ROWS
这个参数的话,可以跟前一条合并成一条SQL;
点“下一页”时,用SELECT columns FROM table WHERE condition ORDER BY ... LIMIT 100, 100
查;
...
这样做往往花费很大,因为WHERE condition
有可能是全表扫描。如果MySQL没开缓存的话,每翻一页可能非常慢。
因此我就用一种新的办法:
用SELECT id FROM table WHERE condition ORDER BY ...
得到所有相符的ID,如果数据量太大(比如表中有1,000,000行),我们就限制一下行数(比如限制最多查10,000,就用LIMIT 10000
),于是这些ID就通过动态页面或Ajax(以JS代码或JSON的形式)被传到了前端;
前端JS选取前100个ID作为第一页,发送一个带这100个ID的查询请求,后端其实处理SELECT columns FROM table WHERE id IN (id_0, id_1, ..., id_99)
这么一个查询;
点“下一页”时,查询是SELECT columns FROM table WHERE id IN (id_100, id_101, ..., id_199)
;
...
这种方法只需要做一次条件查询(慢),列表数据其实都是主键查询(快)。
我在一个业余项目中用了这个办法,详见:(http://) www.chess-wizard.com/base/ (第一页数据被写在JSP页面里,有利于SEO).
我要求团队成员都用这种方式来处理分页,他们却并不认同 :-(
难道LIMIT m, n
是分页查询的标准做法或唯一途径吗?
大家讲道理2017-04-17 16:40:37
一種のid>$id limit $limit
; 渡パラメータ$offset、$limit=100;
最初のページ:$offset = 0
select id ,name from table order by id limit $limit;
2 番目のページ: $offset は最初のページから返された ID です
select id ,name from table where id>$offset order by id limit $limit;
黄舟2017-04-17 16:40:37
ページングが遅い主な理由は、最初のステップ (条件を満たすレコードの取得、並べ替え、現在のページの行の選択) が遅いことです。このステップでは、あなたが言及した方法は改善されていません。
別のケースでは、最初のステップで修飾されたレコードを取得するために少数のテーブルを使用できますが、この時点で、データベースによって実行プランが選択されている場合、詳細な行データを取得するには、複数の他のテーブルに関連付ける必要があります。が間違っていると、非常に遅くなります。このとき、@abul のメソッドを使用して、最初に小さなテーブルから条件を満たすレコード ID を削除し、その後、他のテーブルに関連付けることができます。
これらのプロセスはデータベース内で完了し、データをフロントエンドに転送する必要がないことに注意してください。主な理由は次のとおりです。
1. 修飾された結果セットのデータ量が大きい場合。データベースはすべての ID とクロスネットワークを照会します。送信コストは非常に高く、前述の最大制限 10,000 はすべてのシナリオを満たさない可能性があります。
2. 多くの場合、ユーザーは最初の数ページのコンテンツしか読まないため、一度にすべての ID を取得するのは実際には無駄です。
3. 1 回目のクエリと 2 回目のクエリの間でデータが変更された場合、ユーザーが取得する結果セットは不正確になります。クエリ結果の精度要件に基づいて実現可能かどうかを判断する必要があります。
さらに、クエリされたデータの公開性が高い場合は、システム全体の負荷を軽減するために、それを Redis と同様のキャッシュに置くことを検討できます。フロントエンドにのみ配置すると、再利用率が高くなりすぎます。低い。
絶対に正しい原則と言えば、実際には正しいナンセンスです。ビジネス シナリオのニーズと各ソリューションの長所と短所に基づいて判断と選択を行うことです。
ringa_lee2017-04-17 16:40:37
1. mysql がキャッシュを有効にしないのはなぜですか?
2. フロントエンドはページコンテンツに対して同期アクセスを使用しますか?
同期の場合、メソッドはフロントエンドのニーズを満たすことができません。
非同期の場合、クエリ メソッドは初めてすべての修飾 ID を取得します (最初に 10,000 が取得されると仮定します)。 10,000 フロントエンドから ID を取得するにはどうすればよいですか?それらがすべてページ上に配置されている場合、フロントエンド js はこの配列を直接使用して非同期リクエストを開始できますが、ジャンプ ページ番号がこの範囲を超えた場合、フロントエンドはページ番号 ID をリクエストする必要があります。現時点では、where クエリを使用する必要があります
したがって、このソリューションの現在の効果はそれほど明らかではありません。
私の分析が正しいかどうかはわかりませんが、参考程度にしてください。
黄舟2017-04-17 16:40:37
フロントエンドで ID をキャッシュするという考えなので、フロントエンドで ID 以外のフィールドもキャッシュしてみてはいかがでしょうか
たとえば、ページごとに 50 項目のデータの最初の 10 ページを取得する場合、SQL ステートメントは LIMIT 500 を使用して最初の 500 項目を取得します。これらの範囲内でページをめくるリクエストは必要ありません。 10 ページ; 次のページがめくられるまで、LIMIT 500,500 の SQL ステートメントを使用して最後の 500 項目を取得します
天蓬老师2017-04-17 16:40:37
これは実際にはプロジェクトによって異なります:
1. データ量が多く、一意のインデックス ID が 1 つしかない場合は、後で提案する方法が間違いなく最速になります (もちろん、エントリの数が多すぎることはできません)
2. 他のフィールドがインデックス付けされており、そのフィールドの 100% を条件として使用する必要がある場合は、通常の ORDER BY ... LIMIT m, n
ページング クエリを使用すると、非常に高速になります
3. キャッシュを使用するかどうかは、アプリケーションのシナリオによって異なります。クエリにあまり多くの where 条件が含まれておらず、データがリアルタイムで更新されない場合は、これを使用できます。