カーソルベースのWebナビゲーションをしましたが、なぜ重複データをキャプチャしますか?


問題の背景


データをダウンロードする際、最新順でダウンロードすることにします.
オフセット:
  • 重複データ問題
  • データがクエリされるたびに、最初からデータベース
  • がスキャンされます.
    そこでカーソル方式を採用した.
    カーソル方式でページング方式で次のコードを実現し、エラーが発生しました.
    クエリリクエストの最後のidは7であり、7以降にインポートする必要があります.
    データは2,1であり,実際には6,5,4,3,2,1番idをクエリした.どこが問題ですか.
    まずはハーモニーを見てみましょう!
  • controller
  • @GetMapping
    public SeriesSubscribeList.Response getSeriesList(
    	@RequestParam(required = false) Long lastSeriesId,
      @RequestParam @Positive Integer size,
      @RequestParam(required = false, defaultValue = "ALL") Category[] categories
    ) {
    	return this.seriesService.getSeriesList(lastSeriesId, size, List.of(categories));
    }
  • service
  • public SeriesSubscribeList.Response getSeriesList(
    	Long lastSeriesId,
      Integer size
    ) {
    	PageRequest cursorPageable = PageRequest.of(
    		0,
        size,
        Sort.by(Direction.DESC, "createdAt", "id")
    	);
    
    	return new SeriesSubscribeList.Response((
    		(lastSeriesId == null) ? this.seriesRepository.findAll(cursorPageable)
        : this.seriesRepository.findByIdLessThan(lastSeriesId, cursorPageable)
      )
    }
  • repository
  • List<Series> findByIdLessThan(
    	Long id,
      Pageable pageable
    );
    コードを表示します.
    最初のページに入ると最後のidが分からないのでnullをリクエストします.
    createdAtは、DESCソート後idが空のためfindAllにデータをインポートします.(実際には、この部分はオフセットでページを分割するのと同じです.)
    次に、最後のid値に基づいてfindByIdLessthanにデータをインポートします.primary keyはインデックスであるため、データベースからデータを最初からスキャンする必要がなく、id値に直接アクセスできます.最後のidより小さいidをリクエストのsizeとしてインポートします.コードに異常はないと判断します.
    では、データベース・シリーズのテーブルを見てみましょう.
    表には以下のデータが含まれており,id 7以降は2,1回のみである.
    なぜ6 5 4 3がクリック率になるのでしょうか?

    ここに変なところがあります.
    idはシリーズ発行の時間順にインクリメントされるため、create atでソートする場合、id値もDESCソートと同じであるべきであると考えられる.
    期待されるシーンはidが6,5,4,3,2,1に並べ替えられ,idが6,5,4,3,7,2,1に並べ替えられるのが見られる.
    私はこの点をよく考えた.
    現在のデータから
  • Sort.(Direction.DESC、createdAt、id)でソートします.
  • ByIdLessthan(最後のシリーズID,cursorPageable)が見つかった場合、
  • もちろん7未満の6,5,4,3,2,1はすべてクエリーされます.論理は間違っていない.
    まず、createdAtデータが予想通りに入らない理由を理解しました.
    原因は.
  • 現在、ローカルデバイスと操作デバイスが分離されていない.
  • ローカル環境は韓国時間、ec 2サーバは米国時間.
  • 要するにidとcreate atの並べ替えが同じ前提自体が誤りである.
    どうやってこの問題を解決しますか?簡単に思いつく方法は.
    ローカル環境とサーバ環境では、この前提がいつでも破られる可能性があると考えています.
    さらにidはauto incrementポリシーではなくuuidポリシーである.
    カーソルはidではなく、他のデータかもしれないので対応できません.
    では、idの順番にかかわらず、自分の意思でidを導入したい場合は、どうすればいいのでしょうか.
    最後のidリクエストが11であれば、次のデータは10、9、8、6、5、4、37、3......クエリーされるべきです.
    10-1番のデータをどのように照会しますか?

    (atが作成され、id DESCがソートされました)
    クエリーを考えてみると、以下のようになります.
    SELECT * FROM table_name
    WHERE created_at < "2021-12-12 07:58:15"
    OR (
    	create_at = "2021-12-12 07:58:15"
    	and id < 11
       ) 
    したがって、2021-12-1207:58:15未満のデータを最初に取得すると、重複する可能性があります.
    2021-12-1207:58:15、idが11未満のデータを入力すると、所望の結果が得られる.
    図では、1番領域と4番領域がクエリーが必要なオブジェクトになります.