카산드라는 Null 값을 가지는 칼럼이 없다. 따라서 select * from users where location is Null; 과 같은 구문을 사용할 수 없다. 반면, RDB는 칼럼에 약간의 공간을 할당해 Null값을 표현한다. 아래 그림을 보면 잘 알 수 있다. 카산드라는 Null이 없기 때문에 칼럼 자체가 없는 걸 볼 수 있다. 이는 카산드라가 row key를 키로 하는 key-value 저장소이기 때문에 스키마가 유연해서 그렇다.

대신 데이터를 사용할 때 Null을 활용할 수 있다. update users set location = Null where username='alice' 는 delete location from users where username='alice'와 같다. 위와 같은 Null을 활용한 삭제는 여러 업데이트를 동시에 할 때 유용하다. update users set email='kim@naver.com', location=Null where username='carol' 와 같이 여러 칼럼을 동시에 업데이트하면서 삭제할 수 있다.
(참고로, 카산드라의 delete는 rdb의 delete와 다르다. 엄밀히 말하면 delete는 tombstone이라는 삭제를 나타내는 flag로 칼럼을 업데이터하는 걸 의미한다.

카산드라는 쓰기 성능이 좋기 때문에 읽기 성능을 향상시키기 위해 데이터를 중복해서 저장하는 데 무리가 없다. 그래서 효율적인 읽기를 위해 여러 장소에 같은 데이터를 쓰게 되는데 이를 Denormalization이라고 한다. query-driven design하는 데 많이 사용되는 기법이다.

그러나 Denormalization을 하는 데 주의해야할 요소가 있다. Denormalization을 사용하면 하나의 데이터를 여러 번 insert해야 한다. 즉, 애플리케이션 단에서 insert 쿼리 요청을 카산드라에게 여러 번 보내는 로직을 짜야 한다. 만약 애플리케이션에 문제가 있어 한 번의 insert 동작이 카산드라 내부에 적용되지 않는다면 데이터의 일관성은 파괴된다. 즉, 데이터 일관성에 대한 책임이 application 단으로 넘겨진다.

물론 이를 막기 위한 장치로 logged batch statement가 있다. ( unlogged batch도 있지만 카산드라 2.x 이후로 deprecated ) logged batch는 원자성을 보장해준다. 즉, 배치로 묶인 여러 쿼리가 모두 성공하거나 실패하거나 둘 중 하나의 결과를 얻는다. 고립성은 보장해주지 않는다. 동시에 여러 클라이언트나 스레드의 배치 내부 쿼리 순서가 지켜지지 않을 수 있다.

카산드라는 logged batch 요청을 받으면 coordinator node의 batchlog 테이블에 blob형태로 요청 정보를 기록한다. 이 기록은 클러스터 내 다른 두 개의 노드에도 기록된다. logged batch 요청이 성공하면 batchlog에 쓰여진 데이터를 지운다. 이와 같은 방식으로 배치를 수행하기 때문에 원자성이 보장되지만 30% 정도의 성능 페널티가 생긴다. 그래서 가급적 사용하지 않는 게 좋다. 애초에 원자성을 기대하지 않고 nosql인 카산드라를 DB로 선택하지 않았을까?

( unlogged batch는 batchlog에 기록하는 작업이 없다. )

+ Recent posts