값을 쓰려면 어차피 값을 들고 있�어야한다.
하나의 트랜잭션에 대해서 모든 데이터는 x초안에 쓰기 작업이 완료되어야 한다. x초를 초과한다면 장비 혹은 로직상의 결함이 있다고 생각하고 서버의 에러로 간주한다. 그 x초 동안, 들어온 값은 캐싱을 한다.
값이 캐싱되어 있다면 .. 복잡한 쿼리에 대해서는 어떻게 그 값을 반환하지? 문제가 더 복잡해질 거 같다. 캐싱을 해서 어떻게 할 수는 있어보이는데 조금 더 생각해볼거리다.
Reader와 Writer데이터베이스에서 비동기로 통신하는 방식은 분명히 좋아보인다. Writer에서 Reader로 값을 쓰는 요청을 비동기로 보냈을 때, Reader는 작업을 완료했는지, 성공했는지 등의 정보를 담아 응답을 비동기적으로 반환할 수 있다. 잘 모르겠는 점은 두 데이터베이스간의 통신의 양이 많아지고, 결국에는 나중에 서로의 데이터가 정확한지 검증하는 대사 작업을 해야한다. 많아지는 통신의 양과 대사 작업이 얼마나 복잡도가 높을지 가늠이 잘 안되긴한다.
동기적으로 두 데이터베이스간 작업을 진행할 수도 있다. 하지만 병목현상이 생기지 않을지, Reader가 작업을 완료할 때까지 Writer는 순순히 기다릴 수 밖에 없다. 비동기적, 동기적으로 작업을 했을 때 각 작업을 비교해보는 것도 재밌어 보인다.
지금 생각해봤을 때는 동기적인 방식이 가장 좋아보인다.
MyTransaction을 정의한다. 타임아웃을 적절히 설정한다.
- 쓰기 요청이 들어오면 Writer db와 Reader db 동시에 값을 쓴다. 이 때, 변경 되어야할 값을 지역 변수로 들고 있는다.
- 변경된 값을 Reader db에서 읽는 쿼리를 요청한다.
- 지역변수와 Reader db에서 반환된 값이 같으면, 커밋을 한다. 아니라면, 롤백을 한다.
안전성이 중요할 때는 MyTransaction을 켜놓고 있다가, 성능이 중요할 때는 MyTransaction을 꺼놓으면 된다.
쓰기 DB와 읽기 DB를 나눠야할 이유가 무엇일까?
데이터의 안전성을 위해서라면 그냥 db를 두 개로 나눈 후, 비동기적으로 두 데이터베이스에 쓰기 요청을 보내고, 검증하면 되지 않을까..? 로드밸런싱 느낌으로
근데 쓰기와 읽기를 나눠버리면 좋은 점은 내가 봤을 땐 디비 락으로 인한 병목현상이 줄어들 것이라고 생각한다. 데이터베이스에서 여러 트랜잭션이 진행될 때 격리수준에 따라 팬텀리드, 논리피터블리드와 같은 부작용이 발생하기도 하고, 여러 트랜잭션이 걸려있으면 트랜잭션의 상호작용 관계에서 선점하지 못하면 기다릴 수밖에 없기 때문에 병목현상이 나타나기도 한다. 쓰기 db와 읽기 db로 나누는건 장점이 있다. 다만 두 데이터베이스가 정말로 일치하는지 확인해야할 필요가 있다. 쓰기 db에 데이터를 쓰게 되면 읽기 db는 복제를 통해 데이터를 가져온다. 레플리카가 정확히 어떤 방식으로 동작하는지는 조금 더 확인할 필요가 있겠지만, 레플리카 자체를 의심하는건 내가 사용하는 API에 대한 테스트코드를 짜야한다는 말과 비슷하다고 생각하기 때문에 복제 성공되었을 때는 모든 복제가 완벽하게 이루어진다고 가정한다.
하지만 복제라는 작업은 시간이 걸릴 수도 있고, 중간에 예외가 발생할 수도 있다. 현재 미션에서 주어진 상황은 writer와 reader 디비를 두 개로 나누었다. writer 디비에 값을 성공적으로 쓰기 했으나, 복제가 지연되어 다음으로 들어오는 조회 쿼리에서 복제되지 못한 값을 조회했기 때문에 예외가 발생한다. 1초의 복제 지연이 걸려있어 양 데이터 베이스의 데이터의 정합성이 맞지 않는다.
이 문제를 어떻게 해결할 수 있을까?
해결의 전제조건으로 일단
- writer 디비와 reader 디비는 합친다
- writer에 읽기를 한다
- reader에 쓰기를 한다
위 경우는 고려하지 않기로 했다. 문제를 해결하기 위해 db를 나눈 근본적인 이유를 퇴색시킬 필요가 없다고 판단했기 때문이다.
가장 먼저 생각나는 방법은
- writer 디비에 쿼리가 들어왔을 때, 서비스 단에서 reader 디비에 조회 쿼리를 보내어 값이 서로 일치하는지 확인하는 방법
- 쓰기작업을 할 때마다 reader 디비의 레플리카 작업이 완료될 때 신호를 보내게한다. 그 신호가 와야 쓰기 작업 트랜잭션을 커밋한다.
였는데, 기본적으로 미션에서 일어나고 있는 상황은 mysql의 비동기적 레플리카 방식 때문이고, 위에 생각났던 방식은 동기적 레플리카 방식과 똑같고 이미 mysql에서 지원하고 있다.
반비동기적 레플리카 방식이라는 것도 있는데, 여러 �reader가 있을시 하나의 reader에서 응답이 오면 레플리카가 성공한 것으로 간주한다. 느슨한 연결이라고 생각되는데 결국은 대사작업이 필요하지 않을까?