배치로 테이블 반복 처리하기
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 And 쿼리는 WHERE "ci_build