본문 바로가기

소프트웨어-이야기/데이터 저장소 + 시각화

[카우치베이스]Insert와 Select의 시간차

금일 과제를 진행하면서 카우치베이스에서 Insert를 하고난 직후에 Select를 하면 Insert했던 값이 반영이 안되는 이슈가 있었다.


왜 그럴까? 이유를 확인한다면 이걸 수정하려면 어떻게 해야할까? 궁금했다.


왜 그럴까?


1. insert의 메모리 객체 반영론

카우치베이스의 Insert 메서드(add)는 데이터가 디스크에 완전히 반영되기 전에 메모리 객체에 반영되고 나면 성공 메시지를 되돌려 준다. 입력된 데이터는 디스크에 저장되고, 이 데이터가 다시 복제 설정된 개수만큼 다른 노드로 복사되게 된다.   << 카우치베이스 실전 가이드 중 >>

즉, 디스크에 값이 저장되기 전에 true를 리턴해줘서, select를 할 때 직전에 insert 한 값이 바로 반영이 안되었던 거다. (그렇다면 select는 디스크에 직접 접근해서 계산해온다는 거겠지?) 그런데 조작의 성공 결과를 받는 시점을 디스크에 데이터가 완전히 저장되거나 복사본에 복사가 되었을 때 받을 수 있도록 옵션을 설정할 수도 있다고 한다 !! 

오호...! 


2. VIEW에 데이터를 반영하는 시점론

 View are updated as the document data is updated in memory. There may be a delay between the document being created or updated and the document being updated within the view depending on the client-side query parameters. << 카우치 베이스 문서 중 >>

데이터가 메모리에서 업데이트 되듯이 뷰도 업데이트 된다. 문서가 새로 만들어지고 수정되는 것이 클라이언트 쿼리 파라미터에 의존적인 뷰에 업데이트 되는 사이에는 딜레이가 있을 수 있다. (.. 내 번역이 완전 구리지만.. 암튼 느낌은 뭔지 알겠다)



어떻게 해야할까?


1. insert의 메모리 객체 반영론에 대응하기

insert 메서드의 옵션은 두가지가 있다. persist_to와 replicate_to이다. 


persist_to[각주:1] : 앞서 말했듯이 insert는 데이터가 메모리 캐시 영역에 들어가면 바로 리턴해준다. 이 파라미터에 숫자를 지정해주면 노드 수만큼 디스크에 쓰여졌을 때 콜백함수로 리턴하게 된다. 카우치베이스는 본인 노드 이외에 최대 세 개의 복사본을 가지고 있으므로, 이 숫자는 0에서 최대 4까지 저장이 가능하다.


replicate_to[각주:2] : 오퍼레이션을 통해 저장된 데이터가 얼마나 많은 복사본에 저장되고 나서 리턴될지에 대해 지정한다. 복사본은 본인을 제외하고는 최대 세 개까지이므로 이 값은 0부터 3까지를 가질 수 있다.

중요한 데이터일수록 각 옵션별 숫자를 늘려주면 되는데, 그만큼 수행시간은 늦어지게 된다.

(persist_to와 replicate_to의 개념이 헷갈린다. 복사본에 저장된다는 개념과 노드수만큼 디스크에 쓰여진다는 개념이 명확히 어떻게 다른건지 찾아볼 필요가 있다. 복사본 저장 개념은 메모리 캐시영역에만 들어가면 바로 리턴해주나..?)

오늘 내가 경험한 이슈는 persist_to의 값을 조정하면 어느정도 해결되지 않을까 싶다. 그런데... select 할 때는 무조건 디스크에서 처리하나..? 


2. View 쿼리에 옵션주기 

View Query : View를 업데이트/조회 하기위한 요청. View의 업데이트 시점은, 문서의 업데이트 시점이 아닌 View Query가 요청되는 시점이라고 한다.  안전하게 하기 위하여 stale 옵션이 true가 default로 설정되어 있다. stale() 옵션을 false로 주면 View가 모두 업데이트 된 후에 결과를 가져오게 할 수 있다. 대신 속도가 느려질 수 있다는 단점이 있다.

관련 한국 링크 :

http://okky.kr/article/214115 / 

https://erlnote.wordpress.com/2016/01/15/couchbase-cberl-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-view-%EC%A1%B0%ED%9A%8C%EC%8B%9C-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD/

http://eranova.tistory.com/


3. 집계만 캐시에 저장하기

 insert 후, 바로 조회해야하는 데이터가 혹시 집계와 관련된 데이터라면, 임의의 캐시키에서 집계를 관리하는 방식도 있다. 그러면 쿼리 옵션을 주거나, persist 옵션을 주면서 느려지는 문제는 피할 수 있다. 

예를 들어 이벤트 참여이력을 저장할 때 아래와 같은 이슈가 있을 수 있다. 이벤트 참여 이력을 카우치베이스에 저장하는데, 매번 참여 이력을 저장할 때마다 현재까지 쌓인 이벤트 참여 갯수가 몇인지 확인해야하는 로직이 있다고 생각해보자. 이런 경우, 참여이력을 저장할 때, 별도의 캐시에 값을 increment 해서 집계를 별도로 관리하고, 가져올 때는  쿼리가 아니라 key에서 값을 직접 가져오면 insert와 쿼리 조회 시의 격차를 없앨 수 있다.


번외


a. 카우치베이스 동시성 이슈에 cas 활용하기

 카우치베이스는 동시성을 보장해주어야한다는 이슈가 있다. 예를 들면 사용자 A가 문서1을 읽고, 이어서 사용자B가 문서1을 읽고, 또 이어서 사용자 A가 문서1을 수정하고, 연이어 사용자B가 문서1을 수정하면 값이 이상해져있을 수 있다. (카우치베이스 실전 가이드 참고) 

예를 들자면 유저A와 유저B가 쿠폰을 100장 뿌리는 이벤트에 참여하는 시나리오를 생각해보자. 유저A가 이벤트에 참여하려고 카우치베이스를 조회했더니, 이벤트가 1장이 남았다고 값이 리턴되었다고 생각해보자. 그래서 유저 A는 이벤트에 참여하여 카우치베이스에서 남은 쿠폰수를 -1 해줘서, 쿠폰은 0개가 남게 되었다.

그런데 동시에 유저B도 이벤트에 참여하려고 하면, 카우치베이스에서 이벤트가 1장이 남았다는 값을 리턴받게 된다. 그래서 유저B 또한 이벤트에 참여하여 남은 쿠폰수를 -1 해주었다.

이런 케이스 때 문제가 발생할 수 있다. 사실 유저B는 유저A단에서 이미 이벤트 쿠폰이 0장으로 업데이트되면서, 이벤트에 참여할 수 없어야 한다. 그런데 동시성이 보장되지 않았기 때문에 참여를 할 수 있었다. 

이럴 때 CAS를 사용하면 된다. CAS ID / CAS Token은 서버가 값을 변경하거나 삭제할 때 참조하는 값이다. 동시성 문제를 해결하기 위해 있는 기능인데, 어떤 데이터를 수정하려고 할 때 문서를 읽은 후 업데이트하려고 할 때 CAS값이 다르면 이를 수정하지 못하게 하는 형태로 이를 방지할 수 있다. 대신 이는 일종의 락 개념이 되어서 성능에 손해를 볼 수 있다.

( 그런데 View에서는 CAS 값이 리턴이 되지 않는다 )



기타 개념


클러스터 : 여러 대의 컴퓨터들이 연결되어 하나의 시스템처럼 동작하는 컴퓨터들의 집합

노드 :  노드는 물리적인 서버에서 기동하는 하나의 카우치베이스 인스턴스로, 카우치 베이스는 여러 개의 노드로 이루어진 클러스터로 구성됨. 하나의 노드는 한 대의 서버로 보면 됨.

  1. << 카우치 실전 가이드 참고 >> [본문으로]
  2. << 카우치 실전 가이드 참고 >> [본문으로]