이번 프로젝트를하며 mongoose와 redis를 사용하기로 초기빌드에서부터 마음을 먹었다.
mongoose는 나중에 다른 글에서 다시 알아보도록하고 일단 redis는 채팅기능을 메인기능 중 하나로 사용해야하는 프로젝트 이기때문에 채팅내용을 저장하거나 캐싱된 데이터를 수시로 꺼내줘야 할 상황이 생길것이라 판단하여 redis를 초기부터 적용시키고 진행해왔다.
처음에는 단순하게 'redis는 엄청 빠르디야! 몽고나 sql보다 10배이상이나 빠른속도!'
라고 주변에서도 이야기하고 실제로 그냥 단순히 빠르다고 하니까 적용시키고보는 경우도 굉장히 많았고 나 또한 단순히 그렇게 적용시키고본게 사실이었다.
redis를 단순히 캐싱하기위해서만 사용할것이라면 유실가능성이있어선 안되는 데이터를
넣으면 안된다는이야기 이다.
대표적으로 redis와 비교되는 memcached 와 다른 점은 redis는 인메모리방식이면서
disk에도 데이터를 저장하여 영속성을 가질수있다는 장점이있다.
반대로 동시에 disk에 저장하기위해 RDB나 AOF기능을 이용하다보니 평균적인 응답속도는 상대적으로
momcashed 보다는 떨어질수밖에없다.
그렇다고 하더라도 영속성을 어느정도 가지고있어야하면서 in-memory 방식의 db가 필요하다면,
빠른속도가 필요하고 db의 로드를 분산시킬 대체품이 필요하다면 redis가 탁월한 선택이 될듯하다.
redis는 기본적으로 RDB와 AOF방식으로 disk에 저장을 한다.
RDB 영속성은 명시된 간격마다 dateset의 해당 시점의 스냅샷을 수행한다.
스냅샷은 특정 시간에 데이터 저장 장치의 상태를별도의 파일이나 이미지로 저장하는 기술이다.
redis에서는 특정 시점의 메모리에 있는 데이터를 바이너리 파일로 저장한다.
RDB 방식은 메모리의 snapshot을 그대로 저장하기 때문에 서버를 재구동시할 때 snapshot을 다시 읽으면 되므로
속도가 빠른 장점이 있다.
그러나, snapshot을 추출하는데 시간이 오래걸리고 도중에 서버가 꺼지면 이후의 데이터를 모두
사라진다는 단점이 있다.
operation 이 발생할때마다 매번 기록한다.(조회는 제외)
RDB방식과는 달리 특정시점이 아니라 항상 현재 시점까지의 로그를 기록할수있으며 논블록킹이라는 특징이있따.
AOF는 log 파일에 대해서만 append하기 때문에 log write 속도가 빠르고 어떤 시점에 서버가 다운되더라도
데이터가 사라지지 않는 장점이 있다.
RDB는 바이너리 파일이라서 수정이 불가능했지만, AOF 로그 파일은 text 파일이므로 편집이 가능하다.
만일 실수로 flushall 명령으로 메모리에 있는 데이터를 모두 날렸을 때, 레디스 서버를 shutdown하고
appendonly.aof 파일에서 flushall 명령을 제거 한 후 레디스를 다시 시작하면 데이터 손실없이
데이터를 살릴 수 있다.
모든 write/update 연산을 log파일에 남기기 때문에 log 데이터 양이 굉장히 크고,
복구 시 저장된 모든 write/update연산을 다시 실행하기 때문에 재시작 속도가 느린 단점이 있다.
예를 들어 set 명령이 key는 같고 값을 다른 조건에서 여러번 수행되었다고 하면,
메모리에는 마지막 수행된 값만 남아있지만, AOF에는 수행된 모든 기록이 남아있기 때문이다.
추가적으로 주의사항은 redis는 싱글스레이드이기때문에 keys와 같은 명령어를 잘못 사용하게되면 O(n)이 걸린다. (선형복잡도 입력값이 증가함에따라 시간또한 같은비율로 증가함다.)
간단하게 이야하기자면 'disk보다 memory가 가깝다.' 라고 할 수 있을 것 같다.
in-memory DB는 disk-based DB와 달리 말 그대로 메모리에 데이터를 저장한다.
외부 저장 장치에 데이터를 저장하지 않고 메모리에서 데이터를 읽고 쓴다.
메모리 <-> 디스크 간 병목이 없기 때문에 disk-based DB보다 훨씬 속도가 빠르다.
외부 저장 장치에 데이터를 저장하여 사용할 경우에는 왜 느린가?
외부 저장 장치에 있는 데이터를 읽고자 할 경우, 해당 데이터를 곧바로 사용할 수 없다.
데이터를 읽어서 메모리에 올리고, 메모리에 올라간 데이터를 읽어서 사용할 수 있다.
즉 메모리에 있는 데이터는 바로바로 읽어올수있고 바로바로 쓸수있다. 그러나 disk
에있는 데이터는 disk에서 한번 읽어내고 메모리에 올려서 그것을 사용한다.
그렇기때문에 memory에서 바로 사용하는게 빠른것이다.
프로젝트를 시작한지 3주차가 지나가는 지금 완성되어진 나의 socket.io를 사용한 랜덤매칭 , 채팅 기능의 코드들을 조금더 이쁘고 효율적이고 적은 코드양으로 바꾸는 과정에서 redis를 모듈화 시키다 문득 'redis를 지금 굳이 사용해야하나?' 생각이 들었다.
초기에 빌드할 당시 계획했던것들과 변경된 내용도 많고 예상했던 채팅내용을 저장, 재전송 및 기타 지속적으로 읽어서 보여줘야하는 데이터들이 현 시점에서는 거의 없다는것이 현 시점의 상황이다.
단순히 유실되어도 괜찮고 적은용량의 데이터를 캐싱 하려는 상황에서도 redis를 쓰는게 최선의 선택은 아닐것이다.
현 시점에서 redis를 빼놓고
부하테스트를 진행해본결과 초당 50~100건의 db operation 을 하는 상황에서 서비스를 이요해본 결과 별다른 속도저하를 체감하지 못했고
100건이 넘어간상태로 30초가 경과하면 트래픽이 누적되면서 레이턴시가 점진적으로 증가하고 기능이상이 생기기 시작했다.
이정도 성능이면 지금 진행중인 프로젝트에서는 충분하기 때문에 보수적으로 초당요청건수가 70~80건정도가 될만한 상황이 된다고 했을때 스케일업을 하거나 스케일아웃을 하는 방안을 고려해보는것이 단순히 redis를 어거지로 끼워넣고 로드를 분산시키고자 이렇게 했다고 이야기하는것보다는 조금더 효율적이고 납득가능할것같다.
결론은 일단 redis 사용은 빼자.
나중에 추가기능구현에서 갑자기 redis가 필요할 만한 상황이 생긴다면 다시 꺼내보는것으로 해야겠다.