세션이 몇 개나 있는지 궁금해서 redis-cli 로 붙어 KEYS * 를 쳤다. 그 순간 게임 접속이 멈췄다.
Table of contents
Open Table of contents
세션 수를 세려던 명령
몇 초 뒤에 결과가 나왔는데 키가 이백팔십만 개가 넘었다. 그동안 서비스 전체가 응답을 못 하고 있었다.
조회만 하는 명령이라 아무 영향이 없을 것으로 생각했다. 실제로는 그 시간 동안 다른 모든 요청이 대기하고 있었다.
Redis 는 명령을 한 번에 하나씩 처리한다. 여러 요청이 동시에 와도 순서대로 하나씩 처리하고 그동안 나머지는 기다린다.
이 성질 덕분에 INCR 같은 것이 여러 요청에 섞여도 자료가 안 깨진다. 대신 오래 걸리는 명령이 하나 들어오면 그 시간 동안 전부가 멈춘다. KEYS 가 정확히 그런 종류였다.
자료구조 크기에 비례한다
KEYS 는 저장된 키 전체를 훑는다. 매뉴얼에도 복잡도가 데이터베이스의 키 개수에 대한 O(N) 이라고 적혀 있고 운영 환경에서는 극도로 조심해서 쓰라고 경고한다. 디버깅이나 키 구조를 바꾸는 특별한 작업용이지 평소 코드에 넣을 것이 아니라는 말도 함께 있다.
같은 성질을 가진 명령이 더 있었다. SMEMBERS 로 집합 원소를 전부 가져오거나 LRANGE 0 -1 로 목록 전체를 가져오는 것들이다. 전부 자료구조의 크기에 비례해서 시간이 든다. 개발할 때는 자료가 적어서 이 성질이 안 드러난다.
매뉴얼은 노트북에서 백만 개짜리를 40 밀리초에 훑는다고 적어 뒀다. 우리는 몇 초가 걸렸는데 훑는 시간보다 이백팔십만 개의 키 문자열을 한 번에 만들어 보내는 쪽이 컸던 것으로 보인다.
나눠서 훑는 방법
전체를 훑어야 할 때는 SCAN 을 쓰라고 매뉴얼이 안내한다. 커서를 받아서 조금씩 나눠 훑는 방식이다.
한 번에 조금만 보고 돌아오므로 그 사이에 다른 요청이 처리된다. 대신 훑는 도중에 자료가 바뀌면 같은 키가 두 번 나올 수 있다. 정확한 스냅숏 대신 서비스가 안 멈추는 것을 얻는 교환이었다.
크기를 모르는 채로 전부 달라고 하지 않는다
개수만 필요한 경우에는 훑지 않고 얻는 방법도 있었다. DBSIZE 는 키 개수를 바로 돌려주고 SCARD 나 LLEN 도 마찬가지다. 세션을 집합 하나에 모아 두면 SCARD 한 번이면 끝난다.
운영 서버에서는 rename-command 로 KEYS 자체를 못 쓰게 만들었다. 조심하는 것보다 못 하게 하는 것이 확실하다. 크기를 모르는 채로 전부 달라고 하지 않는 것이 이 일에서 남은 규칙이었다.
정리
- Redis 는 명령을 하나씩 처리한다
- 오래 걸리는 명령 하나가 나머지 전부를 세운다
KEYS는 키 개수에 대한 O(N) 이고 매뉴얼이 운영에서 쓰지 말라고 한다SMEMBERS와LRANGE 0 -1도 같은 성질이다- 자료구조 크기에 비례하므로 개발할 때는 안 드러난다
SCAN으로 나눠 훑으면 서비스가 안 멈춘다- 대신 같은 키가 두 번 나올 수 있다
- 개수만 필요하면
DBSIZE·SCARD·LLEN을 쓴다 - 운영에서는
rename-command로KEYS를 아예 막는다