AYEONG.DEV
모든 글 보기

Test

Redis에서 Valkey로 전환하기: 명령어는 호환되는데 데이터는 왜 안 옮겨질까?

Redis 한테 친구비 내기 vs Valkey 로 떠나기

·
Redis 8.0.1에서 Valkey 9.1.2로 전환할 수 있는지 검증했다. 연결과 주요 기능은 정상적으로 동작했지만 데이터 마이그레이션 과정에서 예상하지 못한 호환성 문제를 만났다.

1. Redis 대신 Valkey를 사용할 수 있을까?

기존 Redis 환경을 Valkey로 대체할 수 있는지 검토할 일이 생겼다. (왜냐면 레디스가 유료 된다고 함..)

Valkey는 Redis에서 포크된 오픈소스 프로젝트다. 기존 Redis 명령어와 클라이언트 대부분을 그대로 사용할 수 있기 때문에 마이그레이션 부담이 비교적 낮은 편이다.

처음에는 연결 주소를 변경하고 몇 가지 기능만 확인하면 될 것이라고 생각했고 실제로 연결 자체는 어렵지 않았다.

문제는 기존 데이터를 옮기는 과정이었다. 이번 검증에서 확인하고자 했던 내용은 다음과 같다.

  • 기존 Redis 클라이언트를 변경하지 않고 사용할 수 있는가?
  • Redis에서 사용하던 명령어와 자료형이 정상적으로 동작하는가?
  • 캐시, 세션, 임시저장, 분산락 등의 기능이 동일하게 동작하는가?
  • 기존 Redis 데이터를 Valkey로 이전할 수 있는가?
  • Valkey 재시작이나 연결 장애가 발생했을 때 애플리케이션이 복구되는가?

단순히 PING에 PONG이 돌아오는 것만으로는 전환 가능 여부를 판단하기 어려웠다.

2. Redis와 Valkey의 관계

Valkey는 Redis의 라이선스 정책 변경 이후 Redis OSS 7.2.4를 기반으로 시작된 프로젝트다. 현재는 Linux Foundation 산하에서 개발되고 있으며, Redis와 유사한 명령어와 데이터 구조를 제공한다.

기본적인 GET, SET, DEL뿐만 아니라 SET NX EX, GETDEL, EVAL 등의 명령도 사용할 수 있다. 다만 여기서 구분해야 할 부분이 있다.

명령어 호환성과 데이터 직렬화 형식의 호환성은 별개의 문제다.

Redis와 Valkey가 동일한 명령어를 지원하더라도, 특정 Redis 버전에서 생성한 RDB 파일이나 DUMP 데이터까지 읽을 수 있다는 의미는 아니다. 이번 검증에서 가장 중요했던 부분도 바로 이 차이였다.

3. 검증 환경 구성

기존 Redis를 중단하지 않고 비교하기 위해 별도의 Valkey 인스턴스를 구성했다.

3.1 Valkey 실행

공식 Docker 이미지를 사용해 Valkey를 실행했다.

shell
docker run -d \
  --name valkey-test \
  -p 127.0.0.1:6380:6379 \
  -v valkey-data:/data \
  valkey/valkey:9.1.2

기존 Redis와 저장 경로를 분리했기 때문에 서로의 데이터에 영향을 주지 않는다.

shell
docker exec valkey-test valkey-cli PING
plain text
PONG

정상적으로 실행됐다.

3.2 기존 ioredis 클라이언트 사용

별도의 클라이언트를 설치할 필요는 없었다.

기존에 사용하던 ioredis에서 포트만 변경해 연결했다.

typescript
import Redis from 'ioredis';

const redis = new Redis({
  host: '127.0.0.1',
  port: 6380,
  db: 0,
});

async function main() {
  await redis.set('test:key', 'hello-valkey');

  const value = await redis.get('test:key');

  console.log(value);

  await redis.quit();
}

main().catch(console.error);

출력 결과:

plain text
hello-valkey

연결과 기본 명령어는 정상적으로 동작했다. 여기까지는 정말 연결 주소만 변경하면 되는 작업이었다.

4. Redis 명령어 호환성 검증

실제 애플리케이션에서 사용하는 명령어를 기준으로 호환성을 확인했다.

단순히 명령어가 지원되는지만 확인하는 것이 아니라 반환값과 TTL, 데이터 변경 결과까지 비교했다.

4.1 주요 명령어

기본적인 캐시와 데이터 조회 기능에서는 별다른 차이가 발견되지 않았다.

4.2 분산락 검증

분산락에 사용하는 SET NX EX도 확인했다.

shell
SET lock:test acquired NX EX 5

첫 번째 실행:

plain text
OK

잠금이 유지되는 동안 다시 실행:

plain text
(nil)

TTL이 만료된 이후에는 다시 잠금을 획득할 수 있었다.

Redis에서 사용하던 방식과 동일하게 동작했다.

단, 명령어가 정상 동작한다고 해서 분산락 구현 전체의 안전성이 보장되는 것은 아니다. 락 해제 시 소유자 검증이나 TTL 만료에 따른 경쟁 조건은 애플리케이션에서 별도로 처리해야 한다.

4.3 Lua 스크립트 검증

Redis에서 사용하던 Lua 스크립트 역시 Valkey에서 실행할 수 있었다.

plain text
EVAL "return redis.call('GET', KEYS[1])" 1 test:key
plain text
"hello-valkey"

기존 Redis 명령을 호출하는 스크립트가 정상적으로 실행되는 것을 확인했다.

다만 EVAL 검증 결과가 EVALSHA나 모든 Lua 스크립트의 호환성까지 보장하는 것은 아니므로, 실제 사용 중인 스크립트를 기준으로 테스트할 필요가 있다.

5. 실제 기능도 정상적으로 동작할까?

명령어 테스트 이후에는 Redis에 의존하는 기능들을 확인했다.

대표적인 테스트 대상은 다음과 같았다.

캐시와 임시저장 기능은 정상적으로 동작했고, 데이터 저장 후 관련 키를 삭제하는 처리에서도 문제가 없었다. Valkey 재시작 이후에도 저장된 데이터를 다시 조회할 수 있었다.

다만 개발 환경에서 진행한 기능 테스트이므로 실제 사용자 인증과 토큰 갱신의 모든 경로까지 검증됐다고 볼 수는 없다. 이 시점까지의 결론은 간단했다.

애플리케이션 기능을 기준으로 보면 Redis를 Valkey로 대체할 가능성은 충분했다. 하지만 기존 데이터까지 옮겨야 한다면 이야기가 조금 달라진다.

6. 첫 번째 문제: Redis RDB 파일을 Valkey에서 읽지 못했다

기존 Redis에서 생성한 RDB 파일을 Valkey에 직접 복원해 보았다. RDB는 Redis의 데이터를 특정 시점의 스냅샷 파일로 저장하는 영속성 방식이다.

기존 Redis에서 사용 중이던 RDB 형식은 REDIS0012였다. 문제는 Valkey에서 이 파일을 읽으려고 할 때 발생했다.

plain text
Can't handle RDB format version 12

처음에는 파일이 잘못 복사됐거나 권한 문제일 가능성을 생각했다. 하지만 원인은 파일 손상이 아니라 RDB 포맷 버전의 비호환성이었다. Redis 8.0.1에서 생성한 RDB 버전 12를 테스트한 Valkey 9.1.2가 지원하지 않았던 것이다.

Redis와 Valkey는 공통된 기술적 기반을 가지고 있지만, 이후 각각 개발되면서 내부 데이터 형식에도 차이가 생길 수 있다. 따라서 Redis에서 백업한 파일을 Valkey에 넣기만 하면 복원될 것이라는 가정은 성립하지 않았다. 결론적으로 해당 버전 조합에서는 RDB 파일을 직접 복원하는 방식을 사용할 수 없었다.

7. 두 번째 문제: MIGRATE도 실패했다

RDB 직접 복원이 실패했으니 다른 방법을 시도했다. Redis에는 데이터를 다른 Redis 호환 서버로 이전할 때 사용할 수 있는 MIGRATE 명령어가 있다.

이번에는 원본 데이터를 유지하면서 복사하기 위해 COPY REPLACE 옵션을 사용했다. 하지만 결과는 다음과 같았다.

plain text
ERR DUMP payload version or checksum are wrong

RDB 파일 복원에 이어 MIGRATE까지 실패했다. 이유를 살펴보니 MIGRATE 역시 내부적으로 데이터 직렬화 형식에 의존하고 있었다. MIGRATE는 소스 서버의 키를 DUMP 형식으로 직렬화한 다음, 대상 서버에서 RESTORE하는 방식으로 동작한다.

즉, RDB 파일 전체를 직접 복원하는 방식과는 다르지만 직렬화 데이터의 호환성이 필요하다는 공통점이 있다. 이번 환경에서는 Redis가 생성한 DUMP 데이터를 Valkey가 받아들이지 못했다.

정리하면 다음과 같다.

두 가지를 직접 시도한 뒤에야 데이터 이전 방식을 바꿔야 한다는 사실이 분명해졌다.

8. 결국 자료형 기반 논리 복사로 해결했다

직렬화된 데이터를 그대로 전달하는 방식이 실패했으니, 데이터를 명령어로 읽어서 다시 저장하는 방법을 선택했다. 이른바 논리 복사(Logical Migration) 방식이다. 복잡하게 생각할 필요는 없다. Redis에서 값을 읽고 Valkey에 동일한 자료형으로 저장하는 것이다.

8.1 String 복사

String 자료형은 다음 순서로 복사했다.

plain text
Redis
  │
  ├─ TYPE key
  ├─ GET key
  └─ PTTL key
       │
       ▼
Valkey
  ├─ SET key value
  └─ PEXPIRE key ttl

TTL이 없는 키는 만료 시간을 별도로 설정하지 않는다. 반면 TTL이 존재하는 키는 남은 만료 시간을 기준으로 복사했다.

8.2 Set 복사

Set 자료형은 SMEMBERS로 전체 멤버를 조회한 다음 SADD로 다시 저장했다.

plain text
Redis
  │
  ├─ SMEMBERS key
  └─ PTTL key
       │
       ▼
Valkey
  ├─ SADD key member1 member2 ...
  └─ PEXPIRE key ttl

여기서 중요한 건 값뿐 아니라 자료형과 TTL도 함께 유지해야 한다는 것이다. 같은 문자열 값을 가지고 있더라도 자료형이 달라지면 기존 명령어가 정상적으로 동작하지 않을 수 있다.

8.3 TTL 복사 시 주의할 점

PTTL 명령은 키의 남은 만료 시간을 밀리초 단위로 반환한다.

반환값에는 특별한 의미가 있다.

따라서 TTL 값을 그대로 PEXPIRE에 넣으면 안 된다.

  • 1은 TTL 없이 저장하고, 2는 이미 사라진 키로 처리해야 한다.

또한 원본의 TTL을 읽는 시점과 대상에 적용하는 시점에는 시간 차이가 발생한다.

이번 검증에서는 복사 완료 후 TTL을 비교했지만, 운영 환경에서 엄밀한 만료 시각 보존이 필요하다면 PEXPIREAT과 절대 만료 시각을 활용하는 방법도 고려할 수 있다.

9. 논리 복사 결과

실제 Redis 데이터를 대상으로 복사를 진행한 뒤 키 수, 자료형, 값, TTL을 비교했다.

검증 당시 데이터 기준으로 값과 자료형, TTL이 일치했다. 복사된 데이터를 애플리케이션에서 직접 조회하는 테스트도 진행했고, 정상적으로 응답하는 것을 확인했다.

기존 데이터의 직렬화 형식을 그대로 옮기는 데에는 실패했지만, 자료형별로 데이터를 읽어서 저장하는 방식은 정상적으로 동작했다. 물론 논리 복사는 만능이 아니다.

데이터가 지속적으로 변경되는 운영 환경에서는 복사 중 발생하는 변경사항을 어떻게 처리할지 별도 전략이 필요하다. 또한 사용 중인 자료형과 명령어가 늘어난다면 복사 로직도 그에 맞춰 확장해야 한다.

10. 장애가 발생하면 어떻게 될까?

데이터를 정상적으로 저장하는 것만큼 중요한 것이 장애 이후의 복구다.

Valkey를 재시작한 뒤 다음 항목을 확인했다.

  • 서버 재시작 후 PING 응답
  • 저장된 데이터 복구
  • ioredis 클라이언트 재연결
  • 애플리케이션에서 데이터 재조회
  • 기존 Redis 연결 주소로의 복귀

기본적인 재연결 및 데이터 복구는 정상적으로 동작했다. 하지만 재연결이 된다는 사실만으로 장애 대응 검증이 완료되는 것은 아니다.

Valkey가 응답하지 않는 동안 요청은 어떻게 처리되는지, 재시도 횟수와 간격은 적절한지, 여러 애플리케이션 인스턴스가 동시에 복구되는지도 확인해야 한다. 또한 Redis로 다시 돌아가는 경우에는 주의할 점이 있다. Valkey로 전환한 이후 생성된 데이터는 기존 Redis에 자동으로 반영되지 않는다.

연결 주소를 되돌리는 것과 데이터를 원래 상태로 되돌리는 것은 다르다.

특히 세션이나 임시저장처럼 사용자 상태가 들어 있는 경우에는 더욱 중요하다.

11. 그래서 Redis를 Valkey로 바꿀 수 있을까?

이번 검증 결과를 정리하면 다음과 같다.

핵심 기능의 호환성은 확인했지만, 이것만으로 운영 전환까지 검증이 끝났다고 볼 수는 없다. 실제 인증 세션의 전체 수명주기, 장애 상황에서의 재시도 동작, 성능 비교, 자동 회귀 테스트 등은 별도로 확인할 필요가 있다. 특히 데이터가 존재하는 환경에서는 애플리케이션의 명령어 호환성뿐 아니라 데이터 마이그레이션과 롤백 전략이 전환 가능 여부를 결정하는 중요한 요소다.

12. 마무리

처음에는 Redis와 Valkey의 명령어 호환성만 확인하면 될 것이라고 생각했다. 실제로 클라이언트 연결과 주요 기능 테스트는 예상보다 순조로웠다. 하지만 데이터 마이그레이션은 달랐다.

RDB 파일을 직접 복원하는 방식도, MIGRATE 명령어를 사용하는 방식도 실패했다. 결국 자료형 기반 논리 복사를 통해 데이터를 이전할 수 있었다.

Redis에서 Valkey로 전환하는 작업은 단순한 엔드포인트 변경처럼 보일 수 있다. 실제로 애플리케이션 코드 변경은 거의 필요하지 않을 수도 있다. 하지만 실제로 서비스 중인 프로젝트에서 데이터까지 포함한 전환은 별도의 문제다.

휴.. 햄들었다.


참고 자료

조회 0

댓글

익명 캐릭터로 가볍게 이야기를 남겨 주세요.

0/1000