Lost Update(갱신손실) 문제
문제상황
read한 데이터를 누군가 update 한 뒤 다시 옛날데이터 update함.
- 1번 사용자가 데이터를 조회
- 2번사용자가 해당 데이터의 col2를 업데이트함.
- 1번 사용자가 col1의 데이터를 업데이트 하면서 2번사용자의 col2의 데이터를 다시 옛날 데이터로 업데이트함.
해결방안
버전관리 방식( 낙관적 동시성 제어중 하나) 내 어플리케이션의 경우 충돌이 잦지 않고 조회를 하는 기능에 영향을 주고 싶지 않다. 따라서 modified_at을 두어 변경시간을 특정하고 마지막으로 조회한 시점의 변경시간과 update 한 시점의 변경시간과 같은 경우에만 업데이트를 하도록한다.
1.버전관리(충돌이 빈번하지 않을때/ select와 update 간의 텀이 길 때)
데이터를 update 할 때 update 버전을 가지고 있거나 혹은 데이터 자체적으로 update 타임스탬프로 관리한다. DB자체적으로 여유가 있다면 loging 을 통해 전체 버전관리 전체 db관리를 하며, 여유가 없다면 modified_at을 두어 이게 최종버전을 보는건지 확인하는 것이 좋다.
첫번째 경우는 전체 버전이므로 어떤게 application 단에서 어떤 값을 조회했고 update했는지가 명확하기 때문에 구분해서 업데이트 해줄 수 있다. 두번째 경우는 변경시간만 알 뿐 특정을 하지 못한다. 따라서 1번사용자가 변경을 할 때 누군가 데이터를 변경했고 재조회를 해야한다는 알람을 다시 주어야한다. 이 경우 사용자경험이 떨어지게 된다.
-- 버전관리
UPDATE my_table
SET col1 = ?, version = version + 1
WHERE id = ? AND version = ?
-- modified_at 관리
UPDATE my_table
SET col1 = ?, modified_at= sysdate
WHERE id = ? AND modified_at = ?
2.즉각적잠금(select와 update간의 텀이 짧고 충돌이 빈번할 때)
SELECT 할 때 다른 CONNECTION에서 조회를 못하도로 LOCK을 건 뒤 UPDATE를 진행한다.
-- 트랜잭션 시작
BEGIN
-- 특정 행에 대해 즉시 잠금 획득
SELECT col1, col2
FROM my_table
WHERE id = ?
FOR UPDATE;
-- 이후 해당 행을 안전하게 업데이트
UPDATE my_table
SET col1 = ?
WHERE id = ?;
-- 작업 완료 후 커밋 (잠금 해제)
COMMIT;
END;
주의사항
대부분의 상황에서는 버전관리로 업데이트를 한다. 즉각적잠금의 경우 하나의 프로세스를 최대한 짧게 가져가야 시스템 성능을 최적화 할 수 있다.