이제 우리의 서비스는 V4를 목전에 두고 있다. 나는 그 중에 V3에서 미처 구현하지 못 했던 엘라스틱 서치 적용도 해보고, 레디스를 이용한 분산 락 동시성 제어와 레디스를 이용한 검색 기능 개선도 해 볼 생각이다.
그래서 일단 내가 Redis를 모르기 때문에 먼저 문서화를 하고 내 머릿속에서 정리하는 시간을 가져보려고 한다.
Redis는 인메모리 기반 데이터 구조 저장소로 Key,Value 구조로 이루어진 비관계형 데이터베이스 관리 시스템이다.
데이터 베이스가 이미 있는데, 왜 굳이 인메모리 기반의 Redis를 쓰느냐?
DB와 인메모리 기반의 자세한 특징을 보면 왜 사용하게 되는 지 알 수 있다.
물리적으로 회전하는 플래터와 기계식 헤드로 데이터를 읽고 쓰는 하드디스크 드라이버(HDD)
반도체를 이용해 데이터를 저장하며 HDD보다 빠른 성능의 솔리드 스테이트 드라이브(SSD)
디스크를 쓰는 이유
디스크의 단점
Redis는 데이터를 메모리(RAM)에 저장하는 인메모리 기반 데이터 저장소
키-값(Key-Value) 형태로 데이터를 저장하며, 이로 인해 조회 성능이 빠름
이를 활용해 메시지 큐 시스템(RabbitMQ, Kafka, Redis Pub/Sub 등)은 비동기 처리, 분산 환경에서의 데이터 전달, 트래픽 부하 분산 등의 기능을 수행할 수 있다."
분산 시스템 지원: 여러 개의 Redis 노드를 묶어 사용이 가능하다. 이를 통해 묶인 노드들은 하나의 논리적 저장소처럼 동작하게 되는데 이러한 방식을 클러스터링이라 한다. 이는 데이터 분산 저장, 장애 대응, 확장성의 확보를 가능케한다.
Redis의 단점
관계형 데이터베이스에서는 DB를 사용할 때 쿼리문을 사용하면, 쿼리문이 동작하면서 데이터 저장소에서 필요한 정보를 꺼내오게 된다. 이 때 데이터 저장소에서는 자주 불러오는 메모리만 저장을 하여 사용하고, 전체 메모리는 Disk에 주기적으로 백업이 되어있는 형태이다. 그래서 같은 조건에서 조회를 하더라도 데이터 베이스를 처음 검색할 때랑 두 번, 세번 검색했을 때랑 속도 차이가 나게 된다.
레디스 등 인메모리 기반은 RAM을 사용해서 빠르게 검색하는 특징이 있는데 아래와 같이 생각해 볼 수 있다.
여행을 가는데 캐리어에 대부분의 짐들을 넣어가고, 미니 배낭에 물, 손수건, 휴대폰 등 가볍고 자주 쓰는 물품을 넣는 것과 비슷한 맥락이라고 볼 수 있다. 미니 배낭에서는 물건을 빠르고 쉽게 꺼낼 수 있지만 어쩌다가 한 번 자주 안 쓰던 물건을 캐리어에서 급히 꺼내야한다면 시간이 오래 걸리게 된다. 여기서 캐리어는 디스크에 해당하고, 미니 배낭은 RAM인 것이다.
일단, 낙관적 락과 비관적 락은 그동안 해 왔던 일련의 과정(
분산 락이란, 분산 환경(다중 스레드 환경)에서 여러 프로세스나 서버가 동시에 같은 리소스(컴퓨터 내 자원)를 변경하는 문제를 방지하는 기술이라고 한다.
어, 혹시 이거... 깃허브 컨플릭트와 비슷한 거 아닌가??? 내가 개발한 부분과 남이 개발한 부분이 겹칠 때, 남의 코드가 먼저 올라오고 내 코드를 그냥 올리면 풀리퀘 전에 컨플릭트 표시(머지가 안 된다는 표시)가 뜨는데, 분산 락이 컨플릭트 표시의 역할을 해주는 모양이다.
예를 들어 물품 판매를 위한 사이트에서 재고 관리를 위한 두 개의 서버가 존재할 때 두 서버가 동시에 한 상품의 서버를 변경시킨다면 조회수 동시 카운트 시 값이 누락되는 것 마냥 재고의 이상이 생길 수 있다는 것이다.
이러한 것을 방지하기 위해 서버에 락을 분산해서 걸어서 한 프로제스만 특정 리소스를 변경할 수 있게 제한을 두는 게 분산 락의 개념이라고 한다.
Redis를 분산 락으로 사용하는 이유
Redis는 빠르고, 네트워크를 통해 접근 가능하며, 원자적 연산(Atomic Operation)을 제공하기 때문에 분산 락 구현에 적합하다고 한다. 여기서 원자적 연산은 원자성을 지키면서 동작하는 연산을 말한다.
Redis를 이용한 분산 락의 장점
유효시간(Time-To-Live, TTL) 설정 가능: EXPIRE 개념에서 나온 제한 시간에 대한 설정을 TTL설정이라고 한다.
분산 환경에서 사용 가능: 여러 서버에서 동일한 Redis를 바라보면 하나의 중앙화된 락 시스템을 만들 수 있다.
내 개인적인 생각이지만 락을 분산 환경에서 사용하게 되면 중앙집중화 되서 편리하지만 한편으로는 단점도 존재할 거 같다. 만약 중앙에 해당하는 컴퓨터의 Redis가 다운 되어버리면 해당 Redis를 가리키고 있는 모든 서버에 락이 다운되는 것 아닌가? 하는 의문이다.
그리고 단점을 조사해보니 아니나 다를까 이미 Redis의 단점 중에 내가 생각한 단점이 존재했다. ㅋㅋ
RedLock을 사용해야 하는 이유를 쉽게 설명해보자면, 아래와 같은 비유로 가능할 거 같다.
<쉬운 이해를 위한 비합리적인 1인 스터디룸 게임>
질서관리를 위한 게임관리자 1명이 있고, 사용자 여러 명인 환경이다.
스터디룸은 이중문인데, 서로 멀리 떨어진 바깥 문이 여러개 존재하지만 내부에서는 단 하나의 스터디룸만 존재한다.
관리자는 사용자 한 명이 들어갔으니 잠가달라고 하면 전광판에 사용자 있음 표시를 하게 한다. 왜냐하면 동시에 단 하나의 스터디룸에만 사람을 들여보내고 문을 잠가야 하기 때문이다.
그리고 이 게임에서 두 상황을 방지하기 위한 두 가지 방지책을 제시할 수 있을 것이다.
하나는 아예 관리자를 여러 명으로 늘리는 것이다.
그리고 또 하나는 문이 잠긴 후 어느정도 시간이 지나면 관리자가 없더라도 문이 저절로 열리는 시스템을 적용하는 것이다.
여기서 전자는 레디스를 여러 노드로 분산시키는 것이고, 후자는 TTL 설정에 해당된다.
근데 그러면 바깥 문이 잠겼는데 또 다른 문에 사용자가 들어가서 잠갔을 때의 문제는? 관리자를 늘렸을 때, 관리자 과반수 이상이 동일한 바깥 문을 잠가야지만 최종적으로 문이 잠기게 되는 시스템을 도입하면 된다. 이게 바로 RedLock의 개념인 것이다.
이 때 여러 명의 관리자는 참 비합리적인 스터디룸 답게 각자 무전기를 사용하는데 무전기의 전파가 완벽하지 않고 가끔 늦게 정보가 전달되기도 한다. 이로 인해 과반수의 동의를 얻은 내용을 듣지 못한 관리자는 문이 아직 안 잠긴 줄 알고 들어간 사람의 문이 잠겼는지 지속적으로 모니터링하게 된다. 그러한 뻘짓을 방지하기 위해서 한 사람이 들어가고 너무 오랜 시간 잠기지 않았다는 판단이 될 시에는 계속 기다리지 않게 하는 로직이 필요하다.
이러한 개념은 네트워크 지연으로 인한 락의 무한로딩 문제를 방지해주고, 락이 안 걸리고 무한로딩이 될 거 같으면 포기하고 재시도 하는 것이다.
중요
그럼에도 Redis는 완벽하지 않다.
1. AOF로 개선이 가능하다 해도 기본적으로 휘발성 구조를 가져서 데이터 손실 위험이 있다.
2.Redlock 자체도 완벽하지 않은 개선책이다.
일단 나는 이정도 공부했으면 나름 이해한 거라 생각이 되기 때문에, 이제는 본격적인 분산 락을 위한 구현방법을 찾아보기로 했다.
<출처>
https://wildeveloperetrain.tistory.com/21
https://aws.amazon.com/ko/compare/the-difference-between-relational-and-non-relational-databases/
https://velog.io/@choidongkuen/서버-메세지-큐Message-Queue-을-알아보자
https://velog.io/@orcasuit/Atomic-Operation
https://sjh836.tistory.com/178
https://candypoplatte.github.io/TIL/2018/12/21/higher_order_function.1/
https://velog.io/@haron/Redis-Expire-를-어떻게-관리할까
https://codingdog.tistory.com/entry/redis-키-값을-일정-시간이-지나면-expire-시켜-봅시다
https://medium.com/sjk5766/redis가-제공하는-redlock을-알아보자-2feb7278411e
https://mangkyu.tistory.com/311
https://velog.io/@sweet_sumin/단일-장애-지점이란-무엇인가요-SPOF