레이블이 redis인 게시물을 표시합니다. 모든 게시물 표시
레이블이 redis인 게시물을 표시합니다. 모든 게시물 표시

수요일

Redis maxmemory 설정 — OOM 에러와 eviction policy 완벽 가이드

증상 — 에러 메시지

OOM command not allowed when used memory > 'maxmemory'.
ERR command not allowed when used memory > 'maxmemory'

캐시 서버로 Redis를 운영하다 보면 어느 날 갑자기 쓰기 명령이 전부 실패하는 상황을 만납니다. OOM(Out of Memory) 에러는 Redis가 설정된 최대 메모리 한도에 도달했을 때 발생하며, 기본 정책(noeviction)에서는 새 데이터를 추가할 수 없습니다.

원인

Redis는 기본적으로 maxmemory 제한이 없어서 서버 물리 메모리를 모두 소모할 수 있습니다. 운영 환경에서 명시적으로 제한을 걸면, 그 한도를 넘는 순간 maxmemory-policy 설정에 따라 동작이 결정됩니다. 기본값인 noeviction은 메모리가 가득 차면 쓰기 에러를 반환합니다.

해결방법

1. maxmemory 및 정책 설정

# redis.conf 파일 수정
maxmemory 2gb
maxmemory-policy allkeys-lru

# 또는 런타임에 즉시 적용 (재시작 불필요)
redis-cli CONFIG SET maxmemory 2gb
redis-cli CONFIG SET maxmemory-policy allkeys-lru

# 설정 확인
redis-cli CONFIG GET maxmemory
redis-cli CONFIG GET maxmemory-policy

2. Eviction Policy 종류와 용도

# noeviction (기본): 메모리 초과 시 쓰기 에러 반환
# allkeys-lru  : 모든 키 중 LRU 순서로 삭제 — 순수 캐시에 권장
# volatile-lru : TTL 있는 키 중 LRU 순서로 삭제 — 혼합 환경
# allkeys-lfu  : 사용 빈도 낮은 키 삭제 — Redis 4.0+
# volatile-ttl : TTL 짧은 키부터 삭제 — 세션 저장소
# allkeys-random: 무작위 삭제

3. 메모리 사용량 모니터링

redis-cli INFO memory
# 주요 항목: used_memory_human, maxmemory_human, mem_fragmentation_ratio

redis-cli DBSIZE                  # 전체 키 개수
redis-cli MEMORY USAGE mykey      # 특정 키 메모리 크기(bytes)

4. Spring Boot에서 Redis 메모리 효율화

@Bean
public RedisCacheConfiguration cacheConfiguration() {
    return RedisCacheConfiguration.defaultCacheConfig()
        .entryTtl(Duration.ofMinutes(30))   // TTL 필수 지정
        .disableCachingNullValues()          // null 캐싱 방지
        .serializeValuesWith(
            RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer())
        );
}

정책 선택 가이드

사용 목적권장 정책이유
순수 캐시 (세션 없음)allkeys-lru모든 키를 대상으로 LRU 교체, 가장 일반적
캐시 + 영구 데이터 혼합volatile-lruTTL 있는 캐시 키만 삭제, 영구 키 보존
세션 저장소volatile-ttl만료 임박 세션 먼저 삭제
쓰기 보장 필수noeviction메모리 부족 시 에러 반환, 데이터 손실 없음
접근 빈도 편중 심함allkeys-lfu자주 쓰는 키 보존, Redis 4.0 이상

핵심 요약

  • maxmemory와 maxmemory-policy는 항상 함께 설정한다.
  • 캐시 전용 서버라면 allkeys-lru가 기본 선택지다.
  • Spring Boot에서는 entryTtl로 TTL을 반드시 지정해 키가 무한정 쌓이지 않게 한다.
  • mem_fragmentation_ratio가 1.5 이상이면 MEMORY PURGE 또는 재시작을 고려한다.

금요일

Redis WRONGTYPE 에러 완벽 해결 가이드

 🔍 검색 키워드: Redis WRONGTYPE 에러, Redis 데이터 타입 오류, Redis 자료구조, Redis 디버깅


Redis WRONGTYPE 에러 완벽 해결 가이드


증상: WRONGTYPE Operation against a key holding the wrong kind of value

Redis 명령어를 실행했는데 갑자기 이런 에러가 뜨나요?


(error) WRONGTYPE Operation against a key holding the wrong kind of value


이것이 WRONGTYPE 에러입니다. 이미 다른 자료구조로 저장된 키에 맞지 않는 명령어를 사용할 때 발생합니다.


원인 분석


원인 1: 키에 저장된 자료구조와 명령어 불일치

예를 들어 String로 저장된 키에 List 명령어(LPUSH)를 사용하면 에러가 납니다.


SET user:1:name "John"

LPUSH user:1:name "extra" 

결과: WRONGTYPE 에러 (String 키에 List 명령어 사용)


원인 2: 키 이름 재사용으로 인한 타입 충돌

여러 개발자가 같은 키 네이밍 규칙 없이 사용하다 보면 흔히 발생합니다.


원인 3: 캐시 데이터 구조 변경 후 기존 키 미정리

애플리케이션 로직이 바뀌어 자료구조를 변경했는데 기존 키가 남아있는 경우


해결 방법


방법 1: TYPE 명령어로 현재 키의 타입 확인

TYPE user:1:name

결과: string 또는 list, hash, set, zset 중 하나 반환


먼저 문제가 되는 키의 실제 타입을 확인하는 것이 첫 걸음입니다.


방법 2: 올바른 명령어로 재시도

확인된 타입에 맞는 명령어를 사용합니다.

- String이면: GET, SET, INCR

- List면: LPUSH, RPUSH, LRANGE

- Hash면: HSET, HGET, HGETALL

- Set이면: SADD, SMEMBERS

- Sorted Set이면: ZADD, ZRANGE


방법 3: 키를 삭제하고 재생성

기존 키의 데이터가 더 이상 필요 없다면 DEL로 삭제 후 새로 생성합니다.


DEL user:1:name

LPUSH user:1:name "value1" "value2"


주의: 프로덕션 환경에서는 DEL 실행 전 반드시 데이터 백업 여부를 확인해야 합니다.


방법 4: 키 네이밍 컨벤션 도입으로 예방

프로젝트 차원에서 키 이름에 자료구조를 명시하는 규칙을 도입하면 재발을 막을 수 있습니다.


예시:

- str:user:1:name (String)

- list:user:1:orders (List)

- hash:user:1:profile (Hash)

- set:user:1:tags (Set)


방법 5: 애플리케이션 코드에서 사전 타입 체크

Redis 클라이언트 라이브러리에서 명령어 실행 전 TYPE 체크 로직을 추가하면 에러를 사전에 방지할 수 있습니다. 특히 배치 작업이나 마이그레이션 스크립트에서 유용합니다.


정리

- WRONGTYPE은 키의 실제 자료구조와 명령어가 맞지 않을 때 발생

- TYPE 명령어로 먼저 확인하는 것이 가장 빠른 진단법

- 키 네이밍 컨벤션으로 예방하는 것이 근본적인 해결책

- 프로덕션에서 DEL 사용 시 반드시 백업 확인


Redis는 스키마리스 데이터베이스이기 때문에, 팀 차원의 키 관리 규칙이 없으면 이런 문제가 반복될 수 있습니다.

Redis WRONGTYPE Operation 해결 — 키 타입 충돌 완벽 정리

월요일

Redis 연결 에러 완전 정복: ECONNREFUSED 127.0.0.1:6379 트러블슈팅

🔍 검색 키워드: redis connection refused, redis ECONNREFUSED 6379, redis 연결 안됨, ioredis 연결 에러, spring boot redis 연결 실패, node redis ECONNREFUSED, docker redis 연결 에러

Redis 붙이다가 처음 보는 에러 아니다. 누구나 한 번쯤은 밟는다.

Error: connect ECONNREFUSED 127.0.0.1:6379

이거 뜨면 일단 당황하지 말고 순서대로 확인하면 금방 해결된다. 원인은 대부분 세 가지 중 하나다.


에러가 뜨는 주요 상황

상황에러 메시지
Node.js (ioredis)[ioredis] Unhandled error event: Error: connect ECONNREFUSED 127.0.0.1:6379
Node.js (node-redis)Error: Redis connection to 127.0.0.1:6379 failed - connect ECONNREFUSED
Spring BootUnable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException
Python (redis-py)redis.exceptions.ConnectionError: Error 111 connecting to localhost:6379.
Docker 컨테이너 간connect ECONNREFUSED 127.0.0.1:6379 (컨테이너 내부에서 호스트 Redis 접근 시도)

레벨 1: 입문자 — Redis가 켜져 있나요?

가장 흔한 원인. Redis 서버가 꺼져 있으면 당연히 붙을 수 없다.

Redis 실행 상태 확인

# 프로세스 확인
ps aux | grep redis-server

# 포트 리스닝 확인
ss -tlnp | grep 6379

# systemd 기반 (Ubuntu/CentOS)
sudo systemctl status redis

Redis 직접 연결 테스트

redis-cli ping
# 정상이면: PONG
# 실패하면: Could not connect to Redis at 127.0.0.1:6379: Connection refused

Redis 실행 방법

# systemd로 시작
sudo systemctl start redis
sudo systemctl enable redis   # 부팅 시 자동 시작

# 백그라운드 실행
redis-server --daemonize yes

레벨 2: 실무자 — 포트/바인딩/방화벽 확인

Redis는 켜져 있는데 연결이 안 되면 이쪽을 본다.

bind 설정 문제

/etc/redis/redis.conf 기본 설정:

# 기본값: 127.0.0.1만 허용 (로컬호스트 전용)
bind 127.0.0.1

# 외부 접속 허용하려면 (주의: 보안 설정 필수)
bind 0.0.0.0

# 변경 후 재시작
sudo systemctl restart redis

방화벽 확인

# UFW (Ubuntu)
sudo ufw status
sudo ufw allow 6379

# 외부에서 포트 테스트
nc -zv <서버IP> 6379

requirepass 설정 시 인증 필요

redis-cli -a yourpassword ping

# 또는 연결 후 AUTH
redis-cli
> AUTH yourpassword
> PING

레벨 3: 고급 — Docker, Kubernetes 환경

이게 제일 헷갈린다. 컨테이너 안에서 127.0.0.1:6379는 컨테이너 자신을 가리킨다.

Docker Compose로 Redis 연결

# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    environment:
      - REDIS_HOST=redis      # 127.0.0.1이 아니라 서비스명!
      - REDIS_PORT=6379
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
// Node.js - 잘못된 예
const redis = new Redis({ host: '127.0.0.1', port: 6379 });

// 올바른 예
const redis = new Redis({
  host: process.env.REDIS_HOST || 'redis',
  port: parseInt(process.env.REDIS_PORT || '6379'),
});

호스트 머신의 Redis에 접근하는 경우

services:
  app:
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      - REDIS_HOST=host.docker.internal

언어별 연결 코드 및 에러 핸들링

Node.js — ioredis

const Redis = require('ioredis');

const redis = new Redis({
  host: process.env.REDIS_HOST || '127.0.0.1',
  port: parseInt(process.env.REDIS_PORT || '6379'),
  password: process.env.REDIS_PASSWORD || undefined,
  retryStrategy(times) {
    return Math.min(times * 50, 2000);
  },
  maxRetriesPerRequest: 3,
});

redis.on('error', (err) => console.error('Redis 연결 에러:', err.message));
redis.on('connect', () => console.log('Redis 연결 성공'));

Python — redis-py

import redis, os

r = redis.Redis(
    host=os.getenv('REDIS_HOST', '127.0.0.1'),
    port=int(os.getenv('REDIS_PORT', 6379)),
    password=os.getenv('REDIS_PASSWORD'),
    decode_responses=True,
    socket_connect_timeout=5,
    retry_on_timeout=True,
)

try:
    r.ping()
    print("Redis 연결 성공")
except redis.exceptions.ConnectionError as e:
    print(f"Redis 연결 실패: {e}")

Spring Boot — application.yml

spring:
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: ${REDIS_PORT:6379}
      password: ${REDIS_PASSWORD:}
      timeout: 5000ms
      lettuce:
        pool:
          max-active: 10
          max-idle: 10
          min-idle: 2

원인별 체크리스트

체크 항목확인 방법조치
Redis 서버 실행 중?ps aux | grep redissystemctl start redis
포트 리스닝 중?ss -tlnp | grep 6379포트 충돌 확인
bind 설정 맞음?redis.conf 확인bind 0.0.0.0
방화벽 열려 있음?ufw status포트 허용
Docker 환경?컨테이너 여부 확인서비스명으로 host 변경
인증 필요?requirepass 설정 확인password 파라미터 추가
TLS 사용 중?Redis 6.0+ 설정 확인tls:// 스킴 및 인증서 설정
원격 서버?네트워크 경로 확인VPN, 보안그룹 확인

자주 하는 실수 TOP 3

1. Docker에서 localhost 씀
컨테이너 안에서 localhost는 컨테이너 자신이다. 다른 컨테이너의 Redis에 붙으려면 서비스명을 써야 한다.

2. 환경변수 안 쓰고 하드코딩
로컬에서 127.0.0.1로 하드코딩해두고 스테이징/프로덕션에 그대로 올리면 터진다. 처음부터 환경변수로 빼두자.

3. Redis 안 뜨고 앱부터 뜸
depends_on은 컨테이너 시작 순서만 보장하지 Redis 준비를 보장하지 않는다. healthcheck를 써야 한다.

redis:
  image: redis:7-alpine
  healthcheck:
    test: ["CMD", "redis-cli", "ping"]
    interval: 10s
    timeout: 5s
    retries: 5

app:
  depends_on:
    redis:
      condition: service_healthy

마무리

Redis 연결 에러의 90%는 위 체크리스트로 해결된다. 나머지 10%는 TLS, 클러스터 모드, Sentinel 설정 같은 고급 주제인데 그건 따로 다루겠다.

에러 메시지를 봤을 때 "Redis가 켜져 있냐 → 주소/포트가 맞냐 → 네트워크가 열려 있냐" 이 순서로만 확인해도 대부분 잡힌다.