pessimisticLock 이 발생하는 곳을 찾아 확인해보니 insert 쿼리에서 슬로우 쿼리가 발생했다.
이 부분의 insert 는 천 번부터 많게는 몇 만번까지도 발생할 수 있는 로직이다.
해당 문제는 insert 를 하다가 타임아웃이 발생했다.
로직에서는 row 단위로 데이터를 저장하고 있다.
row 단위로 저장하던 것을 json 데이터로 저장을 하면 문제가 발생하지 않을 것이다.
하지만, 이미 몇 십억건의 데이터가 쌓인 상황에서 그렇게 변경하기에는 불가능하다.
현재 상황에서 고려할 수 있는 상황은
1. JPA id 전략을 identity 에서 sequence 전략으로 변경하는 것
identity 는 키 생성을 DB에게 위임한다. (mysql 인 경우 auto_increment)
auto_increment 는 데이터가 insert 된 후 id 값을 알 수 있기 때문에 쓰기 지연을 사용할 수 없다.
7천개 데이터 삽입 시 시간
identity | sequence |
---|---|
60초 | 2.5초 |
order_inserts: true
order_updates: true
bulk insert 는 여러 개 Row 를 한번의 쿼리로 insert 하는 방식이다.
MySql 은 bulk insert 방식을 통해 batch insert가 가능하다.
AS-IS
INSERT INTO TABLE(~~) VALUES(~~)
INSERT INTO TABLE(~~) VALUES(~~)
TO-BE
INSERT INTO TABLE(~~) VALUES(~~), (~~), (~~)
우리 팀원 분들은 1번과 2번에서 의견들이 많았다.
1번을 주장하는 분은 간단하게 DB 설정을 변경해서 해결할 수 있는데, jdbcTemplate 으로 코드 레벨 수준까지 변경해 많은 사이드 이펙트가 생기진 않을지 염려를 했다. 그리고 jpa 를 쓰지 않고 native query 를 사용하는 것이 모니터링할 때도 더 힘들다는 의견이었다.
2번을 주장하는 분은 코드 레벨에서 해결이 가능한데 DB 설정까지 변경해야 하는지, 그렇게 변경을 해도 똑같이 사이드 이펙트가 생기는 건 마찬가지이고 해당 테이블을 sequence 로 변경했을 때 다른 곳에서 사용할 때도 이를 고려해야 한다는 의견이였다.
나도 2번이 더 좋은 것 같다.
결론은 jdbcTemplate 으로 bulk insert 를 사용해 문제를 해결했다.
pessimisticLock 은 더이상 발생하지 않았고 처리 속도도 100%로 향상되었다.