RDB의 동시성과 일관성 이슈를 어떻게 처리할 것인가?

박상훈·2020년 11월 12일
4

들어가며

웹서비스 중에 RDBMS를 채택하지 않는 경우는 드물다고 생각한다. 요즘은 ORM이 특화되어 Query를 잘 모르더라도 개발자들이 RDB를 능숙하게 다룰 수 있는 시대가 왔지만, 결제/재고 등 민감한 비즈니스를 다루는 환경에서는 RDBMS에 대하여 좀 더 깊은 지식이 필요할 때도 있다. 필자는 이번에 민감한 비즈니스에서 RDBMS의 동시성과 일관성을 유지하기 위해 채택한 방법을 정리해볼 것이다.

동시성과 일관성


(이미지 출처 : https://www.codeproject.com/Articles/1267757/Concurrency-vs-Parallelism)

위 그림과 같이 동시성(Concurrency)은 병렬성(Parallelism)과 다르게 transaction들 내에 각각의 작업들이 동시에 실행되는 것처럼 보이기 위해 번갈아 가면서 작업을 수행한다. 쉽게 말해, 쿼리문들이 transaction의 순서에 상관없이 동시에 실행된다.

일관성(Consistency)은 기존의 데이터베이스가 Correct State라면 트랜잭션을 수행하고 난 후에도 Correct State여야 한다는 논리다.

Correct State ?

도메인의 유효범위, 무결성 제약조건 등의 제약조건을 위배하지 않는 정상적인 상태

비관적인 Lock(Pessimistic Lock) 채택

낙관적인 Lock(Optimistic Lock)을 통해 어플리케이션에서 해결하는 것도 물론 좋은 방법이 될 수 있다. 하지만.. 동시 처리량은 높을 수 있으나 일관성(Concurrency) 면에서 불리한 점을 갖는다고 판단했다. 그리고 처리량이 많은 서버에서는 많은 Exception이 발생할 수 있으며, OptimisticLockException이 발생하였을 경우 어떻게 처리할건지에 대한 정책을 수립하기에도 쉽지 않다. (Retry? Slack notice? 흐음..)

그래서 필자는 다수의 쓰레드에서 발생하는 트랜잭션 사이에서 어느정도 트래픽까지는 대처할 수 있는 비관적인 Lock(Pessimistic Lock)을 채택하였다. MySQL 기준으로 고려할 수 있는 Lock은 2가지이다.

S(Shared) Lock vs X(Exclusive) Lock

  • S Lock : 하나의 트랜잭션에서 해당 row에 유입되는 다른 트랜잭션들의 shared locking select, non locking select를 제외한 insert, update, select for update 쿼리에 lock을 걸게된다.
  • X Lock : 하나의 트랜잭션에서 해당 row에 유입되는 다른 트랜잭션들의 insert, update, select 쿼리에 lock을 걸게 된다.

X(Exclusive) Lock 채택


"select까지 locking되는 X Lock은 많은 Lock이 발생되는 이슈가 있지 않을까?!"

MySQL의 isolation level이 REPEATABLE READ 수준일 경우 consistent read에 의해 다른 트랜잭션에서 발생하는 non-locking select는 쿼링이 가능하기 때문에 non-locking select와 locking select를 적절히 분배하면 이슈가 없을 것이다.

참고 자료 :
https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_consistent_read
https://www.letmecompile.com/mysql-innodb-transaction-model/

좀 더 상세하게 정리하자면,

S Lock을 채택했을 때 update 로직이 필요할 경우, 일관성/원자성을 보장하기 위해 Update Query에서 직접 숫자 +- 연산을 하고 보상 트랜잭션의 구현이 필요하다.

X Lock을 채택했을 때 update 로직이 필요할 경우, 주요 프로세스에 해당하는 API에서만!! locking select를 차용하면 동시성과 일관성 이슈를 해결할 수 있다.

그러므로, X Lock을 채택하는게 개발 생산성 측에서 유리할 듯 하다.

Redis vs RDB

Redis가 인메모리 캐시라 성능이 훨씬 좋고 동시성이 기본적으로 보장되지만, 개발 생산성 측면에서는 RDB로 처리하는게 효율적일 것이라고 판단하였다. (세팅, 관리포인트 증가) Redis를 채택한다면 보상 트랜잭션 패턴 구현이 불가피해보이며, Redis Transation을 고려해볼 필요도 있다. 무엇보다 RDB로도 충분히 좋은 성능을 뽑아낼 수 있을 것이다.

주의해야할 점

  • MySQL의 isolation level을 REPEATABLE READ로 유지해야한다.
  • 주요 프로세스(결제 등)가 아닌 이상 locking select을 하지 않는다.
  • DB Lock으로 인해 어플리케이션 성능 이슈가 발생된다면 Redis로 migration한다.

만약 제가 잘못 알고있는 부분이 존재한다면, 댓글로 피드백 부탁드리겠습니다 :)

profile
AWS & BackEnd Engineering에 관심이 많은 개발자입니다.

2개의 댓글

comment-user-thumbnail
2020년 12월 30일

JPA에서 s-lock과 x-lock구현하는것이 궁금합니다.!

1개의 답글