운영중 mysql deadlock 트러블슈팅

Yeolsim's logs·2025년 2월 2일

문제현상

동일한 시간대에 여러개의 병렬 ecs 테스크를 실행하는데

해당 테스크 내부에서 데이터 벌크로 적재시 데드락이 발생하는 경우가 종종 있음

해당 문제로 일부 데이터가 적재 누락되는 현상 발생

데드락이란?

2개 이상의 트랜젝션이 서로의 락을 기다리는 상태 즉, 교착상태를 말함.

문제현상 발생원인

risk.to_sql('RISK_IDX', con=engine, if_exists='append', index=False)

기존 insert코드를 보면 row별 트랜잭션으로 insert되고있음
이와 같은 로직의 문제점은 아래 두가지 문제점을 야기할 것으로 예상됨

  • 과도한 트랜잭션으로 인한 오버헤드 증가
    • 트랜잭션이 많아지면 트랜잭션을 시작하고 커밋하는 비용이 증가함
    • 인덱스가 있는 테이블의 경우 각 insert마다 인덱스를 갱신해야하기 때문에 비효율적임
    • 각 트랜잭션마다 db서버와 클라이언트 간의 네트워크 통신비용이 발생하면서 네트워크 오버헤드 증가
  • 데드락 발생확률 증가
    • 각 insert마다 테이블을 lock하면서 각 트랜잭션 간 락 경합이 발생할 가능성이 상대적으로 높아짐

해결방법 아이디어

  • 기존 레거시 코드에서 row by row 로 insert되고있던 로직을
    벌크로 한번에 insert되도록 수정

  • to_sql 함수의 옵션 검토 후 알맞은 파라미터 추가하여 테스트 후 적용

to_sql 주요옵션

옵션기본값설명
name(필수)저장할 테이블 이름
con(필수)데이터베이스 연결 객체 (engine)
if_exists'fail'테이블이 이미 존재할 경우 처리 방식
indexTrueDataFrame의 인덱스 포함 여부
methodNone데이터 삽입 방식 (multi, execute_many, None)
chunksizeNone데이터 분할 크기 지정 (batch insert)
dtypeNone컬럼별 데이터 타입 지정

method=’multi’ 옵션 추가

  • 하나의 테스크당 최대 약 1000row이기 때문에 chunksize 설정 일단 보류 (추후 모니터링 결과 메모리 이슈 있으면 설정 예정)
  • 한번에 insert되도록 기존 insert코드에 옵션 추가
risk.to_sql('RISK_IDX', con=engine, if_exists='append', index=False, method='multi')

기대효과

  • 쿼리 실행 감소

  • 트랜잭션 수 감소로 인한 효율 증가

  • 그로 인한 데드락 방지 효과 기대

    데이터의 규모가 커지면서 이런 숨어있던 문제들이
    들어나기 시작하고있다.

    항상 데이터의 규모가 확장될 수 있다는 가정하에 아키텍쳐와 코드로직을 설계해야겠다고 다시 한번 다짐하는 계기가 되었다.

0개의 댓글