사용자 1000명의 세션을 만료시켜야 합니다. 하나씩 DEL을 1000번 호출하면 네트워크 왕복이 1000번 발생합니다. 파이프라인을 쓰면 명령 1000개를 한 번에 보내고 응답도 한 번에 받습니다.
Redis 명령 하나의 처리 시간은 마이크로초지만, 네트워크 왕복(RTT)은 밀리초입니다.
일반 방식 (1000개 명령):
RTT = 1ms라면 → 1000 × 1ms = 1000ms = 1초
파이프라인 (1000개 명령):
RTT = 1ms라면 → 1 × 1ms = 1ms
명령 자체 처리 시간보다 네트워크 대기 시간이 훨씬 길 때 파이프라인의 효과가 큽니다.
RTT(Round Trip Time): 요청을 보내고 응답을 받기까지 걸리는 네트워크 왕복 시간. 같은 데이터센터 내에서도 0.5~2ms 정도가 걸립니다.
# 여러 명령을 파이프로 전송
(echo -e "SET key1 value1\r\nSET key2 value2\r\nGET key1"; sleep 0.1) | redis-cli --pipe
import Redis from "ioredis";
const redis = new Redis();
// 파이프라인 생성 및 실행
const pipeline = redis.pipeline();
pipeline.set("user:1:name", "철수");
pipeline.set("user:2:name", "영희");
pipeline.set("user:3:name", "민준");
pipeline.get("user:1:name");
pipeline.get("user:2:name");
const results = await pipeline.exec();
// [
// [null, "OK"],
// [null, "OK"],
// [null, "OK"],
// [null, "철수"],
// [null, "영희"],
// ]
// 각 항목: [에러, 결과]
const results = await redis
.pipeline()
.set("a", 1)
.set("b", 2)
.incr("a")
.incr("b")
.exec();
// 사용자 1000명의 세션 만료 처리
async function expireAllSessions(userIds: string[]) {
const pipeline = redis.pipeline();
for (const userId of userIds) {
pipeline.del(`session:${userId}`);
}
await pipeline.exec();
}
// 대량 캐시 웜업
async function warmupCache(products: Product[]) {
const pipeline = redis.pipeline();
for (const p of products) {
pipeline.set(
`cache:product:${p.id}`,
JSON.stringify(p),
"EX",
3600
);
}
await pipeline.exec();
}
한 번에 너무 많은 명령을 보내면 메모리 부하가 생깁니다. 청크로 나눠 처리합니다.
async function batchProcess(items: string[], chunkSize = 500) {
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
const pipeline = redis.pipeline();
for (const item of chunk) {
pipeline.del(item);
}
await pipeline.exec();
}
}
MSET user:1 "철수" user:2 "영희" ← 원자적, 단순
vs
pipeline.set("user:1", "철수") ← 비원자적, 다양한 명령 가능
pipeline.set("user:2", "영희")
파이프라인은 여러 명령이 Redis에 도착하는 순간에 다른 클라이언트 명령이 끼어들 수 있습니다. 순서 보장은 되지만 원자성은 보장되지 않습니다.
const pipeline = redis.pipeline();
pipeline.get("key1");
// pipeline.set("key2", ???) ← get 결과를 여기서 쓸 수 없음
// exec 전에는 결과를 알 수 없기 때문
이런 경우 파이프라인이 아닌 Lua 스크립트나 트랜잭션을 써야 합니다.
const results = await pipeline.exec();
for (const [err, result] of results) {
if (err) {
console.error("명령 실패:", err);
}
}
파이프라인 내 하나의 명령이 실패해도 나머지는 계속 실행됩니다.