배치로 테이블 반복 처리하기
EachBatch 모듈과 키셋 페이지네이션을 사용하여 대용량 데이터베이스 테이블을 효율적으로 배치 처리하는 방법을 설명합니다.
Rails는 행을 배치 단위로 반복 처리하는 데 사용할 수 있는 in_batches 메서드를 제공합니다. 예시: User.in_batches(of: 10) do |relation| relation.update_all(updated_at: Time.now) end 안타깝게도 이 메서드는 쿼리와 메모리 사용 측면 모두에서 효율적이지 않은 방식으로 구현되어 있습니다. 이를 해결하기 위해 모델에 EachBatch 모듈을 포함한 후 each_batch 클래스 메서드를 사용할 수 있습니다. 예시: class User < ActiveRecord::Base include EachBatch end User.each_batch(of: 10) do |relation| relation.update_all(updated_at: Time.now) end 이는 다음과 같은 쿼리를 생성합니다: User Load (0.7ms) SELECT "users"."id" FROM "users" WHERE ("users"."id" >= 41654) ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1000 (0.7ms) SELECT COUNT(*) FROM "users" WHERE ("users"."id" >= 41654) AND ("users"."id" < 42687) 이 메서드의 API는 in_batches 와 유사하지만, in_batches 가 지원하는 모든 인수를 지원하지는 않습니다. in_batches 가 꼭 필요한 경우가 아니라면 항상 each_batch 를 사용해야 합니다. 고유하지 않은 칼럼에 대한 반복 처리 # 고유하지 않은 칼럼(관계 컨텍스트에서)에 each_batch 메서드를 사용하면 안 됩니다. 이는 무한 루프가 발생할 수 있습니다 . 또한 고유하지 않은 칼럼에 대해 반복 처리할 때 일관되지 않은 배치 크기가 성능 문제를 일으킵니다. 특정 속성에 대해 최대 배치 크기를 적용하더라도 결과 배치가 해당 크기를 초과하지 않는다고 보장할 수 없습니다. 다음 스니펫은 id 가 1 에서 10,000 사이인 사용자에 대한 Ci::Build 항목을 선택하려 할 때 데이터베이스가 1 215 178 개의 일치하는 행을 반환하는 상황을 보여줍니다. [ gstg ] production> Ci::Build.where(user_id: (1..10_000)).size => 1215178 이는 구축된 관계가 다음 쿼리로 변환되기 때문에 발생합니다: [ gstg ] production> puts Ci::Build.where(user_id: (1..10_000)).to_sql SELECT "ci_builds".* FROM "ci_builds" WHERE "ci_builds"."type" = 'Ci::Build' AND "ci_builds"."user_id" BETWEEN 1 AND 10000 => nil WHERE "ci_builds"."user_id" BETWEEN ? AND ? 와 같이 범위로 고유하지 않은 칼럼을 필터링하는 And 쿼리는 범위