ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Batch Performance 극한으로 끌어올리기: 1억건 데이터 처리를 위한 노력
    세미나, 영상 요약정리 2023. 5. 31. 00:01

    https://if.kakao.com/2022/session/65

     

    if(kakao)dev2022

    함께 나아가는 더 나은 세상

    if.kakao.com

     

    카카오페이 정산플랫폼팀의 베니님의 발표를 요약해보고자 합니다.

     

    발표에서 다루고자 하는 내용

    • 대량 데이터 read
    • 데이터 Aggregation 처리
    • 대량 데이터 write
    • batch 구동 환경
    • 대량 데이터 처리 방식 총정리

     

    개발자들은 언제 Batch를 사용할까?

    특정 시간에 많은 데이터를 일괄 처리하는 경우

    • 특정 시간에 상품 주문 배송 정보를 고객에게 문자로 일괄 전송하는 경우
      • 서버 개발자는 Batch로 개발해서 16시에 스케줄을 걸어놔야겠다고 생각합니다
    • 일괄 생성
      • READ -> CREATE -> WRITE
      • 기존의 정보를 조합하여 새로운 정보를 만들어낼 때
    • 일괄 수정
      • READ -> UPDATE -> WRITE
      • 이미 저장된 데이터를 일괄로 수정할 때
    • 통계
      • 데이터를 SUM, GroupBy 형태로 가져와 주문 금액을 합산해서 WRITE 할 때

     

    Batch Performance 얼마나 신경써 보았는가?

    • 개발자들은 Batch 개발을 쉽게 생각한다
    • 배포 후 관리 소홀
    • 배치를 지원하는 APM Tool의 부재로 문제를 인지하기 어렵습니다

    1년 뒤에는 주문량이 많아져 알림 발송이 엄청나게 느려질 수도 있습니다.

     

     

    카카오페이 정산플랫폼팀이 처리하는 데이터량

    • 하루 평균 데이터 Access 횟수는 1억 번이 넘는다.
    • 1시간 만에 처리한다

     

    개발 환경

    • Spring Batch
    • MySQL
    • Spring Cloud Data Flow
    • Redis
    • Kubernetes

     

    Batch 성능 개선의 첫걸음 : Reader 개선

    대부분의 Batch에서 Reader가 차지하는 비중이 Wrtier보다 높습니다.

    복잡한 조회조건을 통해 데이터를 가져오기 때문에 성능에 관여할 가능성이 높습니다.

    • 10억 개의 데이터 중 100만 개를 찾는 경우

     

    읽을 때는 항상 Chunk Proessing

    대용량 처리를 위해서는 Chunk 단위로 처리하는 게 필수적입니다.

    이를 위해 보통 Pagination Reader를 같이 사용하곤 합니다.

     

    대량 처리에 부적합 : Pagination Reader

    Limit offset이 커질수록 조회 속도가 매우 매우 느려지게 됩니다.

     

    이런 문제를 해결한 IteamReader : ZeroOffsetItemReader

    • PK 기준으로 오름차순
    • 이제 항상 limit은 0부터 시작하게 되고 index를 통하여 빠르게 접근할 수 있게 됩니다.
    • 이런 장점을 누리고자 QueryDSL로 쿼리를 짜고 ZeroOffsetItemReader를 사용한다.

     

    Cursor를 지원하는 ItemReader

    하지만 MySql의 Cursor를 사용하면 HQL 또는 Native Query를 사용해야 합니다.

    JdbcCursorItemReader의 문자열 방식의 구현에 큰 부담을 느낀다.

     

    안전하고 세련된 방식 : Exposed

    Exposed는 JetBrains Kotlin 기반 ORM 프레임워크입니다.

    • 데이터 Access 방식
      • SQL을 매핑한 DSL 방식, 경량화한 ORM DAO 방식
    • 지원하는 DB
      • H2, MySQL, MariaDB, Oracle, PostgreSQL, SQL Server, SQLite
    • Kotlin 호환성(자바는 사용 불가)

     

    개선된 ItemReader 결과

    JpaPagingItemReader는 사용하지 말아야겠다고 생각이 듭니다(limit offset의 단점)

     

    300만 건 Haep Space Monitoring

    • 성능뿐만 아니라 안정적인 GC가 발생한다.

     

    데이터 Aggregation 처리

    개발자들은 보통 이렇게 생각합니다.

    통계 -> Batch로 개발 -> GroupBy와 Sum 활용

     

    하지만 데이터가 많아지고 쿼리가 복잡해지는 순간 잘못된 실행 계획을 만들고 까다로운 쿼리 튜닝을 만들어냅니다.

    연산과정이 쿼리에 의존적이게 되며, 데이터가 누적됨에 따라 난이도가 증가하게 됩니다.

    쿼리 튜닝을 위해 과도한 인덱스가 추가되며 INSERT, UPDATE 성능이 저하되고 데이터 용량보다 인덱스 용량이 더 커지기도 합니다.

     

    개선된 ItemReader가 무용지물 되었습니다.

    쿼리 자체가 느렸기 때문에.. 

     

    GroupBy를 포기하자

    쿼리는 단순하게 가고 직접 Aggregation 하자

    하지만 1000만 개 데이터 -> 50만 개 데이터로 만들고자 했을 때 애플리케이션에서는 OOM이 발생할 수도 있습니다. 

     

    충분한 저장공간과 연산을 지원하는 Redis를 쓰자

    Aggregation Tool로 Redis를 사용하는 이유는 다음과 같습니다.

    • hincrby, hincrbyflooat를 지원하여 메모리 수준에서 합산
    • 50만 개는 쉽게 저장하는 넉넉한 메모리
    • 인메로리 DB로 빠른 저장을 하고 영구 저장을 하지 않는다.

     

    그런데 네트워크 레이턴시는 어떻게 하지?

    1000만 개의 sum 요청을 위해서 연산은 빠를게 끝나지만 네트워크비용이 커져 전체 성능은 오히려 낮아지게 됩니다.

     

    Redis PipeLine으로 처리하자

    다수의 command를 한 번에 묶어서 처리하는 Redis PipeLine을 활용합니다.

    이로 인해 네트워킹 비용을 만 번으로 줄이게 되었습니다.

     

    효과적인 Wirter개선

    Batch Insert - 일괄 쿼리 요청

    명시적 쿼리 - 필요한 칼럼 한 Update 하고 영속성 콘텍스트 사용하지 않음

     

    JPA는 안녕~

    • Dirty Checking과 영속성 관리가 불필요하다
      • Batch에서는 Reader Writer가 구분되어 있다.
    • Update 할 때 불필요한 칼럼도 Update
      • JPA가 지원하는 Dynamic Update 쿼리를 동적으로 생성하여 오히려 더 큰 성능저하를 일으킬 수 있습니다.
    • JPA Batch Insert 지원이 어렵다.
      • ID 생성 전략을 IDENTITY로 하게 되면 Batch Insert를 지원하지 않음

     

    Batch 구동환경

    - Batch의 자원관리

    - Batch 모니터링의 어려움

     

    Spring Cloud Data Flow 도입

    데이터 수집, 분석, 데이터 입/출력과 같은 데이터 파이프라인을 만들고 오케스트레이션

    Spring Batch와 완벽한 호환으로 유용한 정보 시각적 모니터링

    k8s와 완벽한 연동으로 Batch 실행 오케스트레이션

    그라파타 연동 가능

    댓글

Designed by Tistory.