redis-cli SET ... 을 반복 호출하면 건마다 연결과 왕복이 생겨 수만 건만 되어도 감당이 안 된다. 세 가지 방법이 있고 규모에 따라 고른다.
| 방법 | 적합한 규모 | 특징 |
|---|---|---|
| 파이프라인 (클라이언트 라이브러리) | 수만 ~ 수십만 | 코드에서 제어. 오류 처리가 쉽다 |
redis-cli --pipe |
수십만 ~ 수천만 | 가장 빠르다. 프로토콜 형식을 만들어야 한다 |
MSET / HSET 다중 필드 |
소규모 | 명령 하나에 여러 값. 크기 제한에 주의 |
Redis 의 mass insertion 방식이다. 입력을 RESP 프로토콜 형식으로 만들어 표준 입력으로 흘려보낸다.
*3\r\n$3\r\nSET\r\n$10\r\nuser:00001\r\n$5\r\nvalue\r\n
*3 은 인자가 3개라는 뜻이고 $10 은 다음 인자가 10바이트라는 뜻이다. 길이는 문자 수가 아니라 바이트 수이므로 한글이 들어가면 UTF-8 바이트 수로 세야 한다.
CSV 를 변환하는 예는 다음과 같다.
import sys
def resp(*args):
out = [f"*{len(args)}\r\n"]
for a in args:
b = str(a).encode("utf-8")
out.append(f"${len(b)}\r\n")
out.append(b.decode("utf-8"))
out.append("\r\n")
return "".join(out)
with open("users.csv", encoding="utf-8") as f:
next(f) # 헤더 건너뛰기
for line in f:
uid, name, email = line.rstrip("\n").split(",")
sys.stdout.write(resp("HSET", f"user:{uid}", "name", name, "email", email))
python3 to_resp.py > payload.txt
redis-cli --pipe < payload.txt
끝나면 다음과 같은 요약이 나온다.
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1000000
errors 가 0 이 아니면 프로토콜이 틀렸거나 명령이 거부된 것이다.
redis-cli --pipe 나 대화형에서 invalid argument 가 나는 경우는 원인이 몇 가지로 정해져 있다.
값에 공백이나 따옴표가 들어 있는데 감싸지 않았다. 셸에서는 셸 인용, 프로토콜에서는 바이트 길이로 해결한다.
redis-cli SET greeting "hello world"
길이 표시와 실제 바이트 수가 다르다. 한글·이모지를 문자 수로 세면 반드시 어긋난다.
줄 끝이 \n 만이고 \r\n 이 아니다. RESP 는 \r\n 을 요구한다. Windows 에서 만든 파일을 리눅스로 옮기면 반대 방향으로도 깨진다.
명령 이름이나 인자 개수가 틀렸다. HSET 은 필드와 값이 짝을 이뤄야 하고 홀수 개면 거부된다.
오류를 건별로 다뤄야 하면 클라이언트 라이브러리의 파이프라인을 쓴다.
import redis
r = redis.Redis(host="redis01", port=6379, decode_responses=True)
with open("users.csv", encoding="utf-8") as f:
next(f)
pipe = r.pipeline(transaction=False)
for i, line in enumerate(f, 1):
uid, name, email = line.rstrip("\n").split(",")
pipe.hset(f"user:{uid}", mapping={"name": name, "email": email})
if i % 5000 == 0:
pipe.execute()
pipe.execute()
transaction=False 로 두면 MULTI/EXEC 로 감싸지 않아 서버 부담이 적다. 원자성이 필요 없으면 이쪽이 낫다.
메모리 한도를 넘기면 maxmemory-policy 에 따라 기존 키가 쫓겨나거나 쓰기가 거부된다. 적재 전에 예상 크기를 재 본다.
INFO memory
MEMORY USAGE user:1001
DBSIZE
AOF 를 켠 상태로 대량 적재하면 디스크가 병목이 된다. 적재 중에만 appendfsync everysec 로 두거나, 적재 후 BGREWRITEAOF 로 정리한다.
복제를 걸어 둔 상태라면 대량 적재가 복제 버퍼를 넘겨 복제본이 전체 동기화를 다시 하게 될 수 있다. client-output-buffer-limit replica 값을 확인한다.