개발자로서 살아남기/JPA 이슈 및 최적화

Spring boot + JPA 환경에서 Insert 동시 요청 시 처리방법

코드 살인마 2023. 7. 4. 21:58
728x90

개요

이전에 회사에서 멀티스레딩 환경에서 동시성 이슈 관련해서 글을 작성한 적이 있다.

 

그 때는 동시성 이슈 즉 insert 요청이 동시에 온다면 락(LOCK)을 걸어 처리하는 방법을 제시하였다.

  • Spring boot + JPA 환경에서 비관적 vs 낙관적 Lock 선택하기 - https://code-killer.tistory.com/163
  • 사내에서도 해당 방법으로 처리하려던 와중 큰 프로젝트에 참여하게 되었고, 위 이슈는 크리티컬하지 않았기 때문에, 추후 개선 이슈로 놔두었다.

큰 프로젝트가 마무리 되고, 해당 이슈를 다시 살펴보는데, 그땐 생각하지 못했던 의문들이 떠올랐다.

 

"그때 당시에는 동시 요청이 오니까, 일단 락을 걸어 비동기로 처리를 해야겠다." 라고 단순히 생각했었는데 정확히 따지자면, 1번의 insert 쿼리가 수행되어야 하는데, 모종의 이유 때문에(사용자의 더블클릭 등등) 2번의 insert 쿼리가 수행되는 것이 문제였다.

 

근데 어찌됐건, 비관적 락의 쿼리를 보면 for update이고, 낙관적 락도 충돌이 발생한 후 처리 하는 로직이기 때문에 위 이슈와는 무관하다고 생각이 들었다.

 

그때, MYSQL 쿼리 중 INSERT IGNORE INTOON DUPLICATE KEY UPDATE 를 알게 되었고, 마침 해당 이슈에 적절한 해결책인 것 같아서 정리하게 되었다.

INSERT IGNORE INTO 쿼리

해당 쿼리는 데이터를 삽입 할 때 중복된 레코드가 있는 경우 무시하는 쿼리이다.


만약 중복된 레코드가 있다면 아무 작업도 수행하지 않고, 에러도 발생하지 않는다.

 

만약 아래와 같은 테이블이 있다고 가정하자

CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(50) UNIQUE
);

 

이 테이블에 INSERT IGNORE INTO 쿼리를 사용하여 새로운 사용자를 삽입한다면, 중복된 이름을 가진 사용자를 무시하고 나머지 사용자를 삽입한다.

INSERT IGNORE INTO users (id, name) VALUES (1, 'John');
INSERT IGNORE INTO users (id, name) VALUES (2, 'Jane');
INSERT IGNORE INTO users (id, name) VALUES (3, 'John'); -- 중복된 이름이므로 무시됨

 

ON DUPLICATE KEY UPDATE

해당 쿼리는 데이터를 삽입할 때 중복된 레코드가 있는 경우 해당 레코드를 사용자가 원하는 방식으로 업데이트 하는 작업을 수행한다.

INSERT INTO users (id, name) VALUES (1, 'John') ON DUPLICATE KEY UPDATE name = VALUES(name);

 

위 예제에서는 id가 1인 사용자가 이미 존재하므로 해당 사용자의 이름을 업데이트한다. 이를 통해 중복된 값을 가지는 레코드를 삽입하려고 할 때 업데이트 작업을 수행할 수 있다.

결론

INSERT IGNORE INTO 쿼리는 중복된 레코드를 무시하고 나머지 레코드를 삽입한다.

 

ON DUPLICATE KEY UPDATE 쿼리는 중복된 레코드를 업데이트하는 작업을 수행한다.

REFERENCE