2020년 4월 24일 금요일

About Cassandra Compaction


카산드라는 compaction이라는 것이 존재하는데 이는 sstable을 줄여주는 작업이다.

이것이 왜 필요한지 살펴보자.

카산드라는 write를 하면 곧바로 disk에 쓰지 않는다. memtable이라는 메모리 공간에 데이터가 상주하게 된다. (commit log는 disk에 있겠지만..)

1000개의 row를 write 해본다.


memtable size가 커지는 것을 확인할 수 있다. 아직 sstable이 0개이기 때문에 space used가 0이다.

이때 카산드라가 비정상 종료되더라도 commit log가 있기 때문에 복구가 된다고는 하지만 강제적으로 disk로 써보자. 이때 사용할 수 있는 명령이 flush나 drain이다. (memtable->sstable로 데이터 이동)

하지만 이 작업을 강제로 하지 않아도 commit log, metable size가 한계치에 다다르면 자동으로 일어나는 작업이며 cassandra.yaml에서 옵션으로 조절 가능하다.

아래 사진은 ./nodetool drain을 한 직후이다.


sstable이 1개가 되었고 memtable에 있던 데이터가 모두 disk로 쓰여진 모습이다.

카산드라는 데이터가 delete가 일어날 때 실제로 delete를 하지 않는다. memtable과 sstable을 가져가기 때문에 구조상 delete flag를 남길 수 밖에 없고 timestamp를 통해 어떤 데이터가 조회가 되어야하는지 알아낸다.

만약 데이터가 꾸준히 insert/delete가 발생한다면 카산드라는 계속해서 sstable을 만들어낼 것이다. 이는 성능 저하의 원인이 된다.
따라서 sstable을 줄여주는(모아주는) 작업인 compaction이 필요하다!

compaction을 하면 cpu를 많이 사용한다고 하는데 필자의 경우에는 우려할 만한 수준은 아니었고 오히려 대량 insert 배치가 일어날 때 cpu가 90%이상 튀어서 throughput_mb_per_sec을 통해 속도조절을 하며 배치를 돌리는 형편이다.

현재 쓰는 옵션은 아래와 같다. (더 연구해봐야한다.)
.set("spark.cassandra.output.concurrent.writes","1")
.set("spark.cassandra.output.batch.size.rows","1")
.set("spark.cassandra.output.batch.size.bytes","512")
.set("spark.cassandra.output.throughput_mb_per_sec","1")



./nodetool compact


다음은 하루에 한번 강제로 major compaction을 한 모습이다.


drain을 하지 않았기때문에 memtable에 데이터는 남아있지만 sstable count와 size가 줄어든 것을 확인할 수 있다.

아래는 compcation이 발생할 때 cpu 사용율이며 딱히 문제없어보인다. 개인적으로는 cpu 사용율을 높이더라도 compaction 속도를 올리고 싶다.


그래서 setcompactionthroughput을 default 64MB에서 2049MB로 주고 돌려봤으나 별 차이가 없다. 이는 추후에 방법을 찾아봐야겠다.

compaction strategy는 테이블마다 정할 수 있으며 다음과 같은 선택지가 있다.

1. SizeTiredCompationStrategy(STCS)는 쓰기가 많은 테이블
2. LeveledCompactionStrategy(LCS)는 읽기가 많은 테이블
3. DateTieredCompactionStrategy(DTCS)는 시계열 테이블

현재는 1번으로 둔 상태이며 추후에 읽기만 발생하는 테이블은 LCS로 교체해볼 예정이다.

또한 compactionstats 명령을 통해 현재 진행상태를 볼 수 있으며 compactionhistory 명령으로 history를 볼 수 있다.



작업을 하다보니 확실치는 않은데 발견한 것이 있다. 확실치는 않은데 하다보니 발견한 부분은 memtable은 rowcache, keycache가 안타고 sstable에 있는 데이터만 캐시를 타는 것 같다.

cache 정보는 ./nodetool info에서 확인할 수 있다.


key cache ALL로 주어도 용량이 크지 않기때문에 선호한다. (추천)
row cache는 ALL로 주면 테이블에 따라서 무지막지하게 용량을 차지할 수 있다. default는 4GB이고 테스트때문에 capacity를 1MB로 줄여놓은 상태이다. (잘 알고 써야한다.)

off heap memory를 사용하기 때문에 조심해야한다.

기록을 남기기 위해서 주저리 주저리 썼는데 더 연구해 볼 것이 많다.

2020년 4월 1일 수요일

Cassandra nodetool tablestats(cfstats) - Read Count is always 0

카산드라 모니터링 툴을 Prometheus와 Grafana를 연동해서 쓰고 있다. 그런데 특정 테이블들의 write는 잡히는데 read는 Grafana나 Prometheus에서 수치가 0, NaN 으로만 나왔다.

살펴본 결과 Grafana나 Prometheus, JMX Exporter 등의 문제는 아니었기에 카산드라 자체에서 Read Log를 내뿜지 않는 것으로 결론지었다.

nodetool tablestats -H keyspace.tablename을 날려본 결과이다.

Read Count: 0
Read Latency: NaN ms
Local read count: 0
Local read latency: NaN ms


원인은 select 쿼리에서 allow filltering을 사용한 쿼리들이 잡히지 않는 것이었고 이들은 tablestat에서 잡히지 않았다. 결국 테이블 스키마를 바꿔서(partition key 수정) allow filltering을 사용하면 안되었고 제거하였다.

그 결과 Read Count가 잘 잡힘을 확인할 수 있었다.

2022년 회고

 올해는 블로그 포스팅을 열심히 못했다. 개인적으로 지금까지 경험했던 내용들을 리마인드하자는 마인드로 한해를 보낸 것 같다.  대부분의 시간을 MLOps pipeline 구축하고 대부분을 최적화 하는데 시간을 많이 할애했다. 결국에는 MLops도 데이...