CS 공부/데이터베이스

DB 정리 4 - Redis, Statement

Redis

레디스 (Remote Dictionary Server)는 메모리 기반의 key-value 구조(딕셔너리)의 데이터 관리 시스템이다.

모든 데이터를 메모리에 저장하고 조회하기때문에 빠른 Read, Write 속도를 보장하는 NoSql이다.

속도가 빠른 이유 ?

메모리 접근이 디스크 접근 방식보다 빠르기 때문.

 

Cache

: 나중의 요청에 대한 결과를 미리 저장했다가 빠르게 사용하는 것. 

→ 어디에 저장할까?

  • CPU cache - 캐시를 저장하기에는 너무 비쌈.
  • Main memory (DRAM) - 적당히 비싸고 괜찮음. 휘발성의 특징을 가지고 있음.
  • 하드디스크, SSD - 저렴하고 크며, 껐다 켜도 날아가지 않음. 

보통 데이터베이스는 하드 디스크나 SSD에 저장한다. (비교적 느리지만, 저렴하고 크며, 껐다 켜도 날아가지 않음)

그런데 점점 조금 더 빠른 연산이나 접근을 하면 어떨까? 라는 접근에서 Redis와 같은 접근이 이루어지게 된다.

 

이때 나타나는 개념이 In-memory Database, 즉 Redis이다.

Database보다 더 빠른 memory에 더 자주 접근하고 덜 자주바뀌는 데이터를 저장하자

 

Redis 특징

  1. Remote 에 위치한
  2. 프로세스로 존재하는
  3. In-Memory : 영속성을 지원하는 In-memory(RAM) 기반의 데이터 저장소.
  4. “키-값” 구조 데이터 관리 시스템 : 비 관계형이며, 키-값 구조이기 때문에 별도 쿼리 없이도 데이터를 간단히 가져올 수 있다. (따라서 Redis는 비정형 데이터를 저장하는 비관계형 데이터베이스 관리 시스템이다)
  5. 읽기 성능 증대를 위한 서버 복제 지원

Redis는 메모리(RAM)에 저장해서 디스크 스캐닝이 필요없어 매우 빠른 장점이 존재한다.

하지만, 기본적으로 RAM은 휘발성이기 때문에, 껐다키면 다 날아가는 특징이 있다.
이를 막기위한 백업 과정이 존재한다. 

  • snapshot : 특정 지점을 설정하고 디스크에 백업AOF(Append Only File) : 명령(쿼리)들을 저장해두고, 서버가 셧다운되면 재실행해서 다시 만들어 놓는 것

 

Redis는 어디에 사용되나?

  1. 인증 토큰 저장소
  2. 랭킹 보드
  3. 유저 API 리밋
  4. Job Queue
  5. 여러 서버의 데이터를 공유하고 싶을때

 

Redis의 기능

  • In-Memory 캐싱
  • Pub/Sub 메세지 큐
  • 세션 스토어

 

Redis가 지원하는 데이터 형식

캐시로 많이 사용하는 Memcached 와 Redis 의 가장 큰 차이는 다양한 자료구조(Collection) 을 제공하냐의 여부이다. Redis 에서는 Collection 을 제공한다. 

  • String (text, binary data) - 512MB까지 저장이 가능함
  • Set (String 집합)
  • Sorted Set (set을 정렬해둔 상태)
  • Hash (양방향 연결리스트도 가능)
  • List

하지만 동시에 이런 범위가 넘지 않도록 주의해야된다는 점은 유의하자.

 

왜 Redis를 사용하는가?

Redis를 안쓰고 만약 자바 객체를 사용할 경우...

  • 서버가 여러대인 경우, Consistency의 문제가 발생할 수 있다. 
  • 멀티 쓰레드 환경에서 레이스 컨디션이 발생할 수 있다.
    • Redis는 기본적으로 싱글 쓰레드로 구현됨.
      → 개발의 단순화를 위해서 사용됨.
      빨리빨리 처리해서 넘겨야하기 때문에 O(N)과 같은 연산은 넣지 않도록 주의해야 한다. 
    • Redis는 Atomic Critical Section(동시에 프로세스 여러대가 접근해서은 안되는 영역) 에 대한 동기화 제공 
    • 서로 다른 Transaction Read/write 동기화

Race Condition이란?

두 개 이상의 프로세스가 공통 자원을 병행적으로(concurrently) 읽거나 쓰는 동작을 할 때, 공용 데이터에 대한 접근이 어떤 순서에 따라 이루어졌는지에 따라 그 실행 결과가 같지 않고 달라지는 상황을 말한다. Race의 뜻 그대로, 간단히 말하면 경쟁하는 상태, 즉 여러개의 스레드가 하나의 자원을 놓고 서로 사용하려고 경쟁하는 상황을 말한다.

 

 

Redsi를 어디에 쓸까?

여러 서버에서 같은 데이터를 공유할 때 Single Server 라면? - Atomic 자료구조 & Cache

 

Redis의 장점

  • 리스트, 배열과 같은 데이터를 처리하는데 유용
    : value 값으로 다양한 데이터 형식을 지원하기 때문
  • 리스트형 데이터 입력과 삭제가 MySql에 비해 10배정도 빠르다.
    : 여러 프로세스에서 동시에 같은 key에 대한 갱신을 요청하는 경우, 데이터 부정합 방지 Atomic 처리 함수를 제공한다(원자성).
  • 메모리를 활용하면서 영속적인 데이터 보존(Persistence)
    : 명령어로 명시적 삭제, expires를 설정하지 않으면 데이터가 삭제되지 않으며, 스냅샷 기능을 제공해 메모리 내용을 *.rdb 파일로 저장하여 해당 시점으로 복구할 수 있다.
    • AOF : Redis의 모든 Wirte/Update 연산을 log 파일에 기록 후 서버 재시작 시 순차적으로 재실행, 데이터 복구
  • 1개의 싱글 쓰레드로 수행되기 때문에, 서버 하나에 여러개의 Redis Server를 띄울 수 있다.(Master-Slave 구조)
    master-slave 간의 복제는 non-blocking

 

Redis 주의점

In-memory 방식이기 때문에 메모리 파편화, 가상 메모리 등의 이해가 필요하며, 장애 발생시 데이터 유실이 발생한다. 따라서 영속적인 데이터 보존을 위해 스냅샷과 AOF 기능을 통한 복구 방식을 주의해서 작성해야 데이터 유실에 대비할 수 있다.

  • 메모리 파편화 : 메모리를 할당하고 해제하는 과정에서 위 그림과 같이 메모리 파편화가 발생할 수 있다. 따라서 메모리 공간을 할당하지 못해 프로세스가 죽는 문제가 발생할 수 있다. 
  • 프로세스를 메모리에 올릴때 일부만 올렸다가, 덜쓰는건 디스크에 올렸다가 필요할때 메인메모리에 올림. 그런데 이게 오래걸리면 싱글스레드라서 시간이 오래걸리면 안되기 때문에 이걸 고려해야함.
  • 기본적으로 메모리에 저장하기 때문에 없어질 가능성을 항상 고려해야 함. 이때 fork라고 메인메모리에 복사하는 과정이 진행되는데, 이때 넘치면 저장이 제대로 안되기 때문에, 데이터 관리가 중요하다.

Single Thread 서버이므로 시간복잡도를 고려해야 한다. (O(N)이면 안됨)

 



Statement vs PreparedStatement

PreparedStatement

:데이터베이스 관리 시스템(DBMS)에서 동일하거나 비슷한 데이터베이스 문을 높은 효율성으로 반복적으로 실행하기 위해 사용되는 기능

 

실행방법

  1. 준비(Prepare): 먼저 애플리케이션은 틀을 만들고 이를 DBMS로 보낸다. 특정값은 지정하지 않은 채로 남겨진다.
    INSERT INTO products (name, age) VALUES (?, ?);
  2. 그 다음, DBMS는 문의 틀을 컴파일하며(최적화 및 변환) 아직 실행하지 않고 결과만 저장한다.
  3. 실행(Execute): 나중에 애플리케이션이 문 틀의 변수에 값(바인드)을 지정하면 DBMS는 (결과를 반환할 수도 있는) 문을 실행한다. 애플리케이션은 여러 값으로 원하는 횟수만큼 문을 실행할 수 있다.
    위의 예에서 첫 번째 변수로 "bike"로, 두 번째 변수로 "20"을 지정한다.

Statement와 PreparedStattement의 차이이는 캐시(cache)사용여부에 있다.

 

그렇기에 우선 속도 면에서 PreparedStatement가 빠르다고 알려져 있다. 이유는 쿼리를 수행하기 전에 이미 쿼리가 컴파일 되어 있으며, 반복 수행의 경우 프리 컴파일된 쿼리를 통해 수행이 이루어지기 때문이다.

Statement에는 보통 변수를 설정하고 바인딩하는 static sql이 사용되고 Prepared Statement에서는 쿼리 자체에 조건이 들어가는 dynamic sql이 사용된다. PreparedStatement가 파싱 타임을 줄여주는 것은 분명하지만 dynamic sql을 사용하는데 따르는 퍼포먼스 저하를 고려하지 않을 수 없다.


하지만 성능을 고려할 때 시간 부분에서 가장 큰 비중을 차지하는 것은 테이블에서 레코드(row)를 가져오는 과정이고 SQL 문을 파싱하는 시간은 이 시간의 10 분의 1 에 불과하다. 그렇기 때문에 SQL Injection 등의 문제를 보완해주는 PreparedStatement를 사용하는 것이 옳다

 

 

참고자료

 

https://velog.io/@devsh/Redis-7-Redis-Server-%EC%9A%B4%EC%98%81%EC%8B%9C-%EC%A3%BC%EC%9D%98%EC%A0%90%EB%93%A4

https://youtu.be/Gimv7hroM8A

https://webstone.tistory.com/56