Redis 개요
대규모 트래픽 처리의 핵심 과제는 다음과 같습니다:
- 수백만 사용자의 동시 접속
- 초당 수십만 건 이상의 요청 처리
기존 관계형 데이터베이스는 조인 연산 등 강력한 기능을 제공하지만 성능 한계가 명확합니다. 특히 디스크 I/O 병목 현상과 확장성 부족으로 인해 대용량 분산 환경에서 어려움을 겪습니다.
이러한 문제를 해결하기 위해 등장한 것이 NoSQL입니다. 메모리 기반 저장소를 활용하여 디스크 접근 빈도를 줄이고, 데이터 간 관계를 단순화하여 확장성을 극대화하는 방식입니다.
NoSQL 데이터베이스란?
NoSQL은 "Not Only SQL"의 약자로, 전통적인 SQL 기반 시스템 외에도 다양한 데이터 저장 구조를 지원하는 데이터베이스 패러다임을 의미합니다. 웹 2.0 시대에 맞춰 대용량 실시간 데이터 처리 요구사항을 충족하기 위해 발전했습니다.
RDBMS vs NoSQL 비교
| 항목 | RDBMS | NoSQL |
|---|---|---|
| 비용 | 라이선스 비용 발생 | 대부분 오픈소스 |
| 성능 | 디스크 기반 느린 응답 | 메모리 기반 고속 처리 |
| 데이터 구조 | 정형화된 테이블 형태 | 키-값, 문서, 그래프 등 유연한 구조 |
| 확장성 | 수직 확장 중심 | 수평 확장 용이 |
주요 NoSQL 제품군
- 키-값 저장소: Redis, DynamoDB
- 문서형 저장소: MongoDB, Couchbase
- 컬럼 패밀리: Cassandra, HBase
- 그래프 데이터베이스: Neo4j, Amazon Neptune
Redis 특징
Redis(REmote DIctionary Server)는 ANSI C로 작성된 오픈소스 고성능 키-값 저장소입니다.
- 단일 스레드 아키텍처
- 읽기 110,000 ops, 쓰기 81,000 ops 성능
- 5가지 기본 데이터 타입 지원(string, hash, list, set, sorted set)
- 영속성(persistence) 기능 제공
Redis 서버 실행
# 서버 시작
redis-server.exe redis.windows.conf
# 클라이언트 연결
redis-cli.exe -h 127.0.0.1 -p 6379
기본 명령어
# 데이터 저장
SET username "홍길동"
# 데이터 조회
GET username
# 여러 키 저장
MSET key1 "value1" key2 "value2"
# 키 삭제
DEL username
# 도움말 확인
HELP @string
데이터 타입별 활용
| Java 타입 | Redis 타입 |
|---|---|
| String | String |
| HashMap | Hash |
| List | List |
| HashSet | Set |
| TreeSet | Sorted Set |
String 타입
# 기본 조작
SET counter 100
INCR counter # 101
DECRBY counter 5 # 96
GETRANGE message 0 4 # 부분 문자열 추출
# TTL 설정
SETEX session_token 3600 "abc123"
TTL session_token # 남은 시간 확인
Hash 타입
# 객체 저장
HSET user:1001 name "김철수" email "kim@example.com"
HMSET user:1002 name "박영희" age 28
# 필드 조회
HGET user:1001 name
HGETALL user:1001
# 숫자 필드 증감
HINCRBY user:1001 login_count 1
List 타입
# 큐/스택 연산
LPUSH task_queue "job1" "job2"
RPUSH task_queue "job3"
LPOP task_queue # job2
RPOP task_queue # job3
# 범위 조회
LRANGE task_queue 0 -1
Set 타입
# 집합 연산
SADD news_category "sports" "politics" "tech"
SADD user_interests "sports" "music" "travel"
# 교집합/합집합
SINTER news_category user_interests # sports
SUNION news_category user_interests
Sorted Set 타입
# 점수 기반 정렬
ZADD leaderboard 100 "player1" 85 "player2" 120 "player3"
ZRANGE leaderboard 0 -1 WITHSCORES
ZREVRANGE leaderboard 0 2 # 상위 3명
Jedis 클라이언트 사용
<!-- Maven 의존성 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
import redis.clients.jedis.Jedis;
public class RedisConnectionTest {
public static void main(String[] args) {
try (Jedis jedis = new Jedis("localhost", 6379)) {
System.out.println("서버 상태: " + jedis.ping());
// 문자열 작업
jedis.set("greeting", "안녕하세요");
System.out.println(jedis.get("greeting"));
// 리스트 작업
jedis.lpush("tasks", "할일1", "할일2");
System.out.println(jedis.lrange("tasks", 0, -1));
}
}
}
Spring Boot 통합
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> objectRedisTemplate(
RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
@Service
public class CacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void saveUser(User user) {
redisTemplate.opsForValue().set("user:" + user.getId(), user, 1, TimeUnit.HOURS);
}
public User getUser(Long id) {
return (User) redisTemplate.opsForValue().get("user:" + id);
}
public void addToCart(Long userId, Product product) {
String key = "cart:" + userId;
redisTemplate.opsForList().leftPush(key, product);
}
}