MySQL Deadlock 발생하는 문제(SQLAlchemy)

Apr 1, 2017

Python, Flask 프레임웍으로 개발했던 서비스에서 월초만 되면 API에서 에러가 발생했다. DB는 MySQL, ORM은 SQLAlchemy를 사용했다. API 에러는 다음과 같았다.

ERROR in app: Exception on /XXXX/XXXX [POST]

...

OperationalError: (_mysql_exceptions.OperationalError) (1213, 'Deadlock found when trying to get lock; try restarting transaction') [SQL: u'INSERT INTO ...'] [parameters: (...)]

OperationalError 에러이고, 락을 가져오려고 할 때, Deadlock이 발견됐으니 트랜잭션을 다시 시작하라는 내용이다.

문제가 되는 부분을 찾았다. insert, commit 하는 부분이었다.

session.commit()

그래서 에러 메시지 내용에 따라 예외 처리를 했다.

try:
    session.commit()
except Exception as e:
    session.rollback()
    raise e

이 상태에서 예외를 처리하지 않으면, 다시 말해서 rollback을 하지 않으면 이 API(세션)는 더 이상 DB Select조차 할 수 없다. 그때 발생하는 에러는 다음과 같다.

InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush.

Dead Lock 발생으로 서비스가 되지 않는 문제는 rollback으로 해결했다. 이제 Dead Lock이 걸리는 원인을 찾아야겠다.

일단 2개의 MySQL 이벤트가 있다.

  • 이벤트 A : 월마다 한 번 오전 9시에 A 테이블에 파티션 추가하는 프로시저 실행
  • 이벤트 B : 매일 한 번 오전 11시에 B 테이블에 파티션 추가하는 프로시저 실행

특이사항:

  • 스토리지 엔진은 MyISAM.
    • 엔진 자체가 트랜잭션을 지원하지 않으므로, SQLAlchemy가 애플리케이션 단에서 처리하는 것 같음.
  • 매일 11시에 INSERT가 많이 발생한다.
  • lock 걸렸는지 확인한 결과 문제가 없었다.
  • 에러가 발생하고, Wait 상태인 프로세스도 없었다.
  • API만 재시작하면 정상 동작한다. 이것은 그 세션만 문제라는 얘기다.

그래서 개발 서버에서 재현하려고 시도했다. jMeter로 API에 Insert를 대량 발생시켰다. 그러나 에러는 발생하지 않았다.

여러 가지 설정도 바꿔봤지만, Dead Lock은 전혀 발생하지 않았다.

  • concurrent_insert
  • table_lock_wait_timeout

개발 서버와 실 서버의 mysql 버전 차이도 영향이 있을까?

  • Development : 5.1.73-log
  • Production 5.5.27-log

실 서버에서는 발생하는데 어째서 개발 서버에서는 발생하지 않을까? 정확한 원인은 찾지 못했다. 이벤트에서 테이블 락을 걸어주고, 프로시저 작업이 끝나면 락을 풀고, 에러가 발생하는지 확인해봐야겠다.

See Also