노드 한 대를 늘렸을 뿐인데 94%가 움직였다 — 안정 해시 설계 (가상 면접 사례로 배우는 대규모 시스템 설계 기초 4편)
·
시스템 설계/가상면접 사례로 배우는 대규모 시스템 설계
데이터를 여러 서버에 나눠 담을 때 가장 먼저 떠오르는 방법은 나머지 연산이다. 키를 해시하고 서버 수로 나눈 나머지를 쓴다. 서버가 네 대면 hash(key) % 4, 여덟 대면 % 8이다. 코드로는 한 줄이고 계산도 빠르다. 문제는 서버 수가 바뀔 때다. 나누는 수가 바뀌면 나머지도 바뀌니까 키가 다른 서버로 이동한다. 여기까지는 예상할 수 있는데, 실제로 얼마나 움직이는지를 재 보고 나서 생각이 달라졌다. 서버를 여덟 대에서 아홉 대로 늘리는 것만으로 89%가, 열여섯 대에서 열일곱 대로 늘릴 때는 94%가 자리를 옮겼다. 5장은 이 문제를 다룬다. 안정 해시(consistent hashing)는 노드 집합이 바뀔 때 재배치되는 키를 최소로 만드는 해시 방식이고, 1997년 Karger 등의 논문에..
요청을 어떻게 거절할 것인가 — 처리율 제한 장치의 설계 (가상 면접 사례로 배우는 대규모 시스템 설계 기초 3편)
·
시스템 설계/가상면접 사례로 배우는 대규모 시스템 설계
앞 편에서 초당 몇 건이 들어오는지를 계산했다. 그 계산의 목적은 "그만큼 받을 수 있게 만들자"였다. 그런데 그 계산이 답하지 않은 질문이 하나 남는다. 계산한 상한을 넘는 요청이 실제로 도착하면 어떻게 되는가. 이 장의 답은 용량을 늘리는 쪽이 아니다. 넘는 요청을 거절하는 쪽이다. 처리율 제한 장치(rate limiter)는 정해진 시간 동안 허용할 요청 수를 정해 두고, 넘는 요청을 잘라 내는 구성 요소다. 거절이라고 하면 소극적인 조치처럼 들리는데, 실제로 읽어 보면 그렇지 않았다. 무엇을 언제 거절할지를 정하는 일에는 다섯 가지 알고리즘과, 그 알고리즘들이 각각 무엇을 포기했는지에 대한 판단이 들어간다. 그리고 그 판단이 그대로 시스템의 성격이 된다. 제한이 없으면 무슨 일이 벌어지는가책은 처..
숫자 없이는 설계도 없다 — 개략적 규모 추정 (가상 면접 사례로 배우는 대규모 시스템 설계 2장)
·
시스템 설계/가상면접 사례로 배우는 대규모 시스템 설계
숫자 없이는 설계도 없다 — 개략적 규모 추정 1편에서는 서버 한 대에서 시작해 로드밸런서, 캐시, CDN, 복제, 샤딩을 차례로 붙였다. 그 글을 쓰면서 끝까지 답하지 못한 질문이 하나 있었다. 그래서 지금 내가 만드는 서비스는 저 중 어디까지 필요한가. 캐시를 넣을 때인가, 아직 이른가. 샤딩을 고민해야 하나, 앞으로 3년쯤은 안 해도 되나. 2장은 그 질문에 답하는 도구를 다룬다. 개략적 규모 추정(back-of-the-envelope estimation)이다. 이름 그대로 봉투 뒷면에 적을 수 있는 수준의 계산이고, 정확한 값을 구하는 게 목적이 아니다. 자릿수가 맞는지, 그리고 그 자릿수가 어떤 구조를 요구하는지를 아는 게 목적이다. 솔직히 처음에는 이 장이 시험 대비용 계산 요령처럼 보였다...
사용자 한 명에서 백만 명까지 — 사용자 규모에 따른 확장성 (대규모 시스템 설계 1편)
·
시스템 설계/가상면접 사례로 배우는 대규모 시스템 설계
대규모 시스템 설계 1편 - 사용자 규모에 따른 확장성『가상 면접 사례로 배우는 대규모 시스템 설계 기초』를 읽기 시작했다. 1장은 사용자가 한 명뿐인 서버 한 대에서 출발해, 사용자가 늘어날 때마다 구성 요소를 하나씩 더해 가며 백만 명 규모까지 끌고 간다. 로드밸런서, 캐시, CDN, 복제, 샤딩 같은 말들이 순서대로 등장하는데, 이 순서가 이 장의 전부라고 해도 된다. 읽으면서 계속 걸린 게 하나 있었다. 각 단계가 "이런 것도 있다"는 소개처럼 읽힌다는 점이다. 로드밸런서를 놓으면 트래픽이 분산된다, 캐시를 두면 빨라진다, 샤딩을 하면 데이터를 나눌 수 있다. 다 맞는 말이지만 이렇게만 기억하면 순서가 남지 않는다. 실제로 중요한 건 그 반대 방향이다. 어떤 병목이 먼저 터졌기에 이 장치가 필요해..
[Redis] Redis - Redis 캐시의 함정 / 주의점 (3)
·
Redis
캐시는 잘 동작할 때가 아니라 무너질 때가 문제다1편에서 캐시가 무엇이고 왜 Redis로 가는지를, 2편에서 같은 Redis로 쓰기 경합을 다스리는 법을 정리했다. 두 편을 거치고 나면 Look-aside 캐시 한 벌을 붙이는 것 자체는 어렵지 않다. 캐시를 먼저 보고, 없으면 DB에서 읽어 채우고, 데이터가 바뀌면 지운다. 히트율 그래프가 95%를 찍으면 일이 끝난 것처럼 보인다. 문제는 그 95%가 아니라 나머지 순간에 있다. 인기 상품의 캐시가 만료되는 바로 그 1초, 누군가 존재하지도 않는 상품 번호를 초당 수천 번 조회하는 순간, 배포 직후 모든 키가 한꺼번에 만료되는 시점, 혹은 Redis 자체가 잠깐 죽는 동안. 이런 순간에 캐시는 부하를 덜어 주기는커녕, 평소 막아 주던 트래픽을 한꺼번에 ..
[Redis] Redis - 동시성 제어 (2)
·
Redis
지난 1편에서는 캐시가 무엇이고 왜 Redis로 가는지, 캐시를 읽고 쓰는 패턴들을 정리했다. 그때 Redis는 주로 "원본 앞에 둔 읽기 가속 계층"이었다. 이번 2편에서는 Redis를 한 걸음 더 밀어붙여, 읽기가 아니라 쓰기 경합을 다스리는 도구로 쓴다. 여러 요청이 같은 데이터를 동시에 바꾸려 할 때 생기는 동시성 문제를, DB의 락에서 시작해 Redis 분산락과 원자 연산으로 옮겨 가며 풀어 본다. 문제 상황은 선착순이다. 선착순 쿠폰이나 한정 수량 상품을 만들어 본 적이 있다면, 코드를 처음 짤 때는 대개 이렇게 쓴다. 재고를 읽고, 0보다 크면 1 빼서 다시 저장하고, 발급 기록을 남긴다. 혼자 테스트할 때는 멀쩡히 돌아간다. 그런데 실제로 100개 한정 상품에 200명이 같은 순간 몰리면,..
[Redis] Redis 캐시 - 기본편 (1)
·
Redis
데이터베이스 앞에 Redis를 한 겹 두면 응답이 빨라진다는 이야기는 많이 듣는다. 그런데 막상 "캐시가 정확히 뭘 하는 건데?"라고 물으면 답이 흐려지는 경우가 많다. 빠른 저장소 정도로만 이해하고 @Cacheable을 붙이면, 어느 순간 캐시와 DB가 다른 값을 들고 있거나, 캐시를 붙였는데도 별로 빨라지지 않는 상황을 만난다. 이 글은 Redis 시리즈의 1편으로, 캐시라는 개념 자체에서 출발한다. 캐시가 본질적으로 무엇인지, 로컬 캐시로는 어디까지 되고 어디서 막히는지, 그래서 왜 Redis 같은 분산 캐시로 넘어가는지를 정리한다. 그다음 캐시를 읽고 쓰는 네 가지 패턴(look-aside, read-through, write-through, write-behind)을 비교하고, Spring에서 ..
[Java & Spring] Pub/Sub은 어떻게 컴포넌트를 떼어놓을까
·
Java & Spring
백엔드 코드를 한참 짜다 보면 어느 순간 이런 코드를 만난다. orderService.create(order);inventoryService.decrease(order);pointService.accumulate(order);notificationService.sendOrderEmail(order);analyticsService.track(order); 처음에는 두 줄이었다. 그러다 어느 날 마케팅이 SMS도 보내달라고 한다. 데이터팀이 추적 이벤트를 더 보내달라고 한다. 결제 팀이 후처리를 끼워달라고 한다. 한 메서드가 다섯 가지 다른 도메인을 알게 되고, 한 명만 실패해도 주문이 통째로 롤백되거나, 비동기로 빼려고 했더니 호출 순서와 트랜잭션 경계가 꼬인다. 이쯤 되면 "주문이 생성됐다는 사실만 어딘..