2019년 Redis 개발 현황 업데이트
원문은 Salvatore Sanfilippo님이 에 게재했습니다. 이 블로그 구독하기
어제 한 Redis 사용자가 Hacker News에 다음과 같은 글을 남겼습니다.
— https://news.ycombinator.com/item?id=19204436 —
Redis를 정말 좋아하지만, 현재 개발 중인 몇몇 변경 사항에 대해서는 다소 회의적입니다. respv3 프로토콜에는 들어보면 멋지게 들리는 기능들이 있지만, 클라이언트 라이브러리 코드를 상당히 복잡하게 만들 수도 있습니다. 또한 세분화된 ACL 작업에도 많은 공이 들어가고 있는데, 왜 이런 것이 필요한지, 그리고 왜 멀티스레드 지원이나 더 나은 영속성 모델, 데이터 타입 같은 다른 변경 사항보다 우선순위가 높은지 이해할 수 없습니다.
— 사용자 의견 끝 —
그분(성별은 잘 모르겠습니다)이 ACL을 “엔터프라이즈 사용자” 때문이라든가 하는 이유로 Redis Labs의 목표에 의해 강요된 기능 정도로 보는 유일한 사람은 아닐 거라는 느낌이 듭니다. 댓글의 다른 지적들도 흥미롭고, 앞으로 Redis가 나아갈 방향에 대해 커뮤니티와 명확하게 소통하기 위해서는 이 모든 점을 짚고 넘어갈 가치가 충분히 있다고 생각합니다.
설명을 쉽게 하기 위해 이 블로그 글을 원본 댓글에서 언급된 각 기능을 하나씩 다루는 섹션으로 나누어 보겠습니다.
RESP3
이미 이 블로그에서 언급한 바 있지만, RESP3의 목표는 사실 클라이언트 생태계를 단순화하는 것입니다. 이상적으로는 모든 클라이언트가 redis.call(“get”,”foo”) 같은 고수준 인터페이스를 다시 만들려고 애쓰지 않는 하위 레이어를 갖게 될 것입니다. 이제 프로토콜 자체가 호출자에게 반환된 응답이 어떤 모습이어야 하는지를 알려줄 만큼 의미론적(semantical)이기 때문에 변환을 일일이 조율할 필요도 없고, 대다수 명령어에 대해 사전에 명령어의 특징을 알 필요도 없습니다. 사용자가 언급한 것은 아마도 RESP3의 대역 외(out-of-band) 통신 지원, 즉 응답의 “attributes”일 것입니다.
저는 앞으로 Redis에서 “클라이언트 측 캐싱”이 큰 화두가 될 것이라고 진심으로 믿습니다. 이는 모든 확장 가능한 시스템에서 논리적인 다음 단계입니다. 하지만 서버의 도움 없이는 클라이언트 측 캐시 무효화가 악몽이 됩니다. 이것이 바로 RESP3가 응답에 attributes를 지원하는 주된 이유입니다. 다만 아마도 Redis 6에서는 *그 어떤 것도 구현되지 않을* 가능성이 높습니다. Redis 6이 될 unstable 버전에는 이미 거의 완성된 RESP3 구현이 들어가 있지만 attributes는 없습니다. RESP3를 구현하는 클라이언트는 정말로 미래를 대비하고 싶다면 attributes를 그냥 버리기로 결정하면 되고, 어차피 사용자가 어떤 특수 기능을 활성화하지 않는 한 향후 Redis 버전에서도 attributes가 전송되지 않을 가능성이 높습니다. 예를 들어 클라이언트 측 캐싱을 위해서는 연결을 특정 모드로 전환해야 할 것입니다. 게다가 아시다시피 Redis 6은 RESP2와 완전히 하위 호환될 것입니다. 사실 저는 RESP2 지원이 절대 제거되지 않을 것 같다는 생각이 들기 시작했습니다. 유지 비용이 거의 들지 않고, RESP2와 RESP3 사이의 추상화 계층을 구현하는 노력을 이미 기울인 마당에 하위 호환성을 깰 이유가 없기 때문입니다.
보통 저는 타당한 이유 없이 무언가를 바꾸는 것을 좋아하지 않지만, RESP2의 한계는 클라이언트 생태계에 큰 영향을 미치고 있었습니다. 저는 사용자가 한 클라이언트에서 다른 클라이언트로 옮겨가도 낯설지 않게 느끼고, API가 클라이언트 작성자가 만들어낸 레이어가 아니라 Redis API 자체인 클라이언트 환경을 만들고 싶습니다. 덧붙이자면 낮은 수준의 API 외에 *더 높은 수준의* API가 존재하는 것 자체를 반대하는 것은 아니지만, 공통 기반이 있어야 하고 클라이언트는 해당 명령어에 대해 아무것도 모르는 상태에서도 명령어를 전송할 수 있어야 합니다.
ACL
ACL 명세는 제가 4년 전에 직접 작성했습니다. 정말로 지금이 구현할 때인지 스스로 확신하기 위해 오랜 시간을 기다렸습니다. 우리는 주로 명령어 이름 변경 같은 편법만으로 ACL 없이도 오랫동안 잘 버텨왔습니다. 하지만 ACL의 주된 동기가 보안을 필요로 하는 엔터프라이즈 고객이라고 생각하지는 마십시오. 부수적인 효과로 ACL은 보안 목적의 사용자 인증도 가능하게 하지만, 이 기능의 주된 목표는 *운영상의* 이유입니다.
예를 하나 들어보겠습니다. Redis 인스턴스가 하나 있고, 이를 이용해 새로운 작업인 지연 작업(delayed jobs) 처리를 하려고 합니다. 인터넷에서 라이브러리를 하나 가져왔는데 잘 동작하는 것 같습니다. 그런데 한 줄 한 줄 알지도 못하는 그런 라이브러리가 왜 “FLUSHALL”을 호출해 순식간에 데이터베이스를 날려버릴 수 있어야 할까요? 어쩌면 그 라이브러리의 테스트 코드 안에 그런 명령어가 들어 있어 너무 늦게서야 알게 될지도 모릅니다. 혹은 이제 막 채용한 주니어 개발자가 회사 Redis 정책이 “No KEYS command”인데도 계속 Redis 인스턴스에 “KEYS *”를 호출하고 있을 수도 있습니다.
또 다른 시나리오로 클라우드 제공업체를 들 수 있습니다. 이들은 관리자 명령어를 신중하게 이름을 바꾸고, 심지어 어떤 이유로든 그런 명령어가 노출되지 않도록 숨겨야 합니다. MONITOR 출력에 해당 명령어가 나타나지 않도록 하는 등 더 많은 편법이 동원됩니다. ACL을 이용하면 인증되지 않은 기본 사용자가 관리성 또는 위험한 명령어를 실행하지 못하도록 Redis를 설정할 수 있습니다. 이는 운영 측면에서 큰 개선이 될 것이라고 생각합니다.
게다가 제가 알기로 ACL은 제가 Redis를 위해 작성한 코드 중 가장 잘 만든 코드 중 하나입니다. 키 패턴을 사용하지 않는 한 CPU 비용이 거의 들지 않으며, 사용하더라도 비용은 적습니다. 구현은 acl.c 파일 안에 완전히 자체적으로 포함되어 있고, 코어의 나머지 부분은 ACL API를 몇 차례 호출할 뿐입니다. 완전히 모듈식이기 때문에 시스템에 복잡성이 추가되지 않습니다. 오히려 ACL 코드 덕분에 AUTH 명령어 주변을 잘 리팩터링할 수 있었습니다.
멀티스레딩
Redis가 지원할 수 있는 멀티스레딩에는 두 가지 형태가 있을 수 있습니다. 사용자가 언급한 것은 “memcached와 유사한” 멀티스레딩, 즉 단일 Redis 인스턴스를 여러 스레드로 확장해 GET이나 SET 같은 단순 명령어에서 초당 처리할 수 있는 연산 수를 늘리는 방식이라고 생각합니다. 이는 I/O, 명령어 파싱 등을 멀티스레드로 만드는 것을 포함합니다. 이를 “I/O 스레딩”이라고 부르겠습니다.
다른 멀티스레드 접근 방식은 느린 명령어를 별도의 스레드에서 실행되도록 하여 다른 클라이언트가 차단되지 않게 하는 것입니다. 이 스레딩 모델을 “느린 명령어 스레딩”이라고 부르겠습니다.
결론부터 말하면, 제가 아는 한 I/O 스레딩은 Redis에서 일어나지 않을 것입니다. 깊이 고민한 끝에 타당한 이유 없이 복잡성만 크게 증가한다고 생각하기 때문입니다. 실제로 많은 Redis 환경은 네트워크나 메모리에 의해 병목이 결정됩니다. 게다가 저는 share-nothing 구조를 강하게 믿고 있어서, Redis를 확장하는 방법으로 특히 Redis Cluster를 통해 동일 호스트에서 여러 Redis 인스턴스를 실행하는 지원을 개선하고 싶습니다. 이와 관련해 2019년에 일어날 일은 두 가지입니다.
A) Redis Cluster의 여러 인스턴스가 로컬 인스턴스의 디스크를 현명하게 사용하도록 조율할 수 있게 됩니다. 즉, 동시에 AOF 재작성이 일어나지 않도록 하는 것입니다.
B) Redis 프로젝트의 일부로 Redis Cluster 프록시를 함께 제공할 예정이므로, 사용자들은 클라이언트 측에 Cluster 프로토콜을 제대로 구현하지 않고도 클러스터를 추상화할 수 있게 됩니다.
또 하나 짚고 넘어갈 점은 Redis는 Memcached가 아니지만, memcached처럼 인메모리 시스템이라는 것입니다. 데이터 모델이 매우 단순한 memcached 같은 인메모리 시스템을 멀티스레드로 만드는 것은 매우 합리적입니다. 디스크 기반 스토어를 멀티스레드로 만드는 것은 필수적입니다. 하지만 복잡한 인메모리 시스템을 멀티스레드로 만드는 것은 그 중간 어딘가에서 일이 꼬이기 시작합니다. Redis 클라이언트는 서로 격리되어 있지 않고 자료구조도 복잡합니다. LPUSH를 수행하는 스레드는 LPOP을 수행하는 다른 스레드를 처리해야 합니다. 얻을 수 있는 이득은 적고 추가해야 할 복잡성은 큽니다.
대신 제가 *정말로* 원하는 것은 느린 연산에 대한 스레딩이며, Redis 모듈 시스템 덕분에 이미 올바른 방향으로 가고 있습니다. 다만 향후(Redis 6이 될지 7이 될지는 아직 모르겠습니다) 모듈 시스템에 키 수준 잠금을 도입해 스레드가 느린 연산을 처리하기 위해 키에 대한 제어권을 완전히 획득할 수 있도록 할 것입니다. 현재 모듈은 명령어를 구현하고 완전히 분리된 방식으로 클라이언트에 대한 응답을 생성할 수 있지만, 공유 데이터셋에 접근하려면 여전히 전역 잠금이 필요합니다. 이는 사라질 것입니다.
더 나은 영속성
최근 우리는 Redis의 이런 근본적인 기능들을 개선하기 위해 여러 노력을 기울였습니다. 최근에 구현된 것 중 가장 좋은 것 하나는 AOF 파일 내부의 RDB 프리앰블입니다. 또한 복제(replication)에 대해서도 Redis 4와 5에서 많은 작업이 이루어졌으며, 이제는 예전과는 완전히 다른 수준에 도달했습니다. 그리고 네, 이러한 부분을 개선하는 것은 여전히 제 주요 관심사 중 하나입니다.
자료구조
이제 Redis는 Redis 5부터 Streams를 갖추게 되었습니다. Redis 6과 7에서는 우선 특정 구현을 변경해 현재 가지고 있는 것들을 훨씬 더 메모리 효율적으로 만드는 것이 계획되어 있습니다. 하지만 새로운 자료구조를 추가하는 데는 고려해야 할 점이 많습니다. 시계열과 스트리밍이라는 맥락에서 리스트, pub/sub, 정렬된 집합(sorted sets) 사이의 간극을 streams로 어떻게 메울지 깨닫는 데만 수년이 걸렸습니다. 저는 Redis가 사용자가 조합해서 쓸 수 있는 직교적인 자료구조들의 집합이 되기를 바라며, 바로 쓸 수 있는 *도구*들의 모음이 되기를 원하지 않습니다. Streams는 추상적인 로그이므로 매우 가치 있는 추가라고 생각합니다. 하지만 다른 것들에 대해서는 오랜 고민 없이 코어에 넣을 가치가 있는지 완전히 확신하지 못합니다. 어쨌든 최근 몇 년간 새로운 자료구조 추가에 확실히 더 많은 비중이 실렸습니다. HyperLogLog, 한층 발전된 비트 연산, streams, 블로킹 정렬 집합 연산(ZPOP* 및 BZPOP*) 등이 좋은 예입니다.
결론
Redis 커뮤니티는 왜 어떤 일은 진행되고 어떤 일은 미뤄지는지 알아야 한다고 생각합니다. 저는 마치 모두가 트위터에 있는 것처럼 트위터를 통해 많이 소통하는 실수를 저지르곤 하지만, 많은 분들은 :-D 삶이 바빠서 신경 쓰지 않습니다. 블로그가 커뮤니티에 알리는 훨씬 더 좋은 방법이며, 더 자주 블로그 글을 쓸 시간을 내야 합니다. 마침 글 쓰는 것을 좋아하니 일석이조이기도 합니다. 깨달아야 할 중요한 점은 Redis에는 확고한 로드맵이 없다는 것입니다. 수년간 경험해보니 기회주의적 개발이 로드맵을 갖는 것보다 훨씬 큰 이득이 됩니다. 수요가 있는가? 필요성이 보이는가? 코딩할 마음이 있는가? 다른 큰 우선순위가 없어 지금이 적기인가? 설계 과정에 도움을 주고 힌트와 아이디어를 주며 테스트를 도와주는 사용자 그룹이 있는가? 그렇다면 지금이 바로 할 때입니다, 해보자고요. Redis에 확고한 로드맵을 갖는 것은 어리석은 일입니다. OSS 코어 팀의 규모가 작고, 때로는 원인 불명의 크래시 하나에 몇 주씩 붙잡혀 있기도 하기 때문입니다. 고정된 장기 계획은 통하지 않습니다. 게다가 Redis 커뮤니티가 피드백을 주면 제 아이디어도 많이 바뀌어서 로드맵을 매달 다시 써야 할 것입니다. 그럼에도 블로깅은 최소한 현재 우선순위/아이디어의 버전이 무엇인지, 그리고 왜 다른 아이디어들이 폐기되었는지를 보여주는 좋은 방법입니다.
마지막으로 한 가지 덧붙이자면, 오픈소스 프로젝트 쪽에 무엇을 넣을지에 대해 제가 Redis Labs로부터 받는 자유도는 거의 무한합니다. 이는 업계에서 일종의 기적이라고 생각하거나, 아니면 제가 Redis Labs에서 함께 일하는 분들이 우리가 하는 일이 오픈소스 운동에서 시작되었고 그 방식을 유지하는 것이 현명하다는 것을 이해하는 좋은 분들이기 때문이라고 생각합니다. 하지만 흔한 일은 아닙니다. 제가 Redis 로드맵에서 실수를 한다면 그것은 분명히 제 실수입니다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기