728x90

 

 

캐시란 일종의 임시 저장소라는 개념으로 기억하면 된다. 여러 종류가 많은데, 그중에서도 하나의 application처럼 별도로 설치해서 사용하는 application cache 개념에 대해 정리해 보려 한다. 클라이언트의 요청 중에서는 분명히 데이터베이스에 중복되는 요청을 하는 경우가 많을 것이다. 이런 요청들에 일일이 응답하며 서버와 데이터베이스가 통신하면 트래픽에 따라서 성능에 무리가 갈 것이다. 

이러한 이유로 사용되는 것이 캐시이다. 

 

cache의 종류

Client caching
 - Caches can be located on the client side (OS or browser), server side, or in a distinct cache layer.

CDN caching

Web server caching
 - Reverse proxies and caches such as Varnish can serve static and dynamic content directly. Web servers can also cache requests, returning responses without having to contact application servers.

Database caching
 - Your database usually includes some level of caching in a default configuration, optimized for a generic use case. 

Application caching
 - In-memory caches such as Memcached and Redis are key-value stores between your application and your data storage. Since the data is held in RAM, it is much faster than typical databases where data is stored on disk. RAM is more limited than disk, so cache invalidation algorithms such as least recently used (LRU) can help invalidate 'cold' entries and keep 'hot' data in RAM.

 

기본적인 원리는 서버에서 프로그래머가 캐시에 저장해 둘 요청을 설정하면, 그에 대해서 캐시에 저장해두고 해당 요청과 데이터에 대해 변경이 있을때 까지, 해당 요청에 대해서는 서버까지 통신하지 않고 캐시를 우선으로 조회하게 된다. 

 

이러한 캐시의 대표적인 종류에 무엇이 있는지 간단하게 정리해 보려 한다. 

 

1. Query Cache

이는 사실 최근에는 잘 사용하지 않는 개념인듯하다. 곧 정리할 memecached, redis가 보편적으로 사용된다. 따라서 query cache에 대해서는 간단하게 개념만 정리해보려한다. 

 

쿼리 캐시의 원리는 간단하다. 서버에서 데이터베이스로 요청하는 SQL 쿼리문에 대한 결과를 메모리에 캐시해두는 것이다. 쿼리문장 자체를 캐싱하는 것은 아니다. 

 

쿼리 결과가 캐시에 저장된 이후 데이터가 변경되면 쿼리 캐시의 데이터는 의미가 없기 때문에 데이터를 제거해야 한다. 여기서 쿼리 캐시의 단점을 알 수 있다. 쿼리 캐시 데이터 제거는 테이블 단위로 처리되기 때문에 테이블의 크기가 큰 경우 상당한 시간이 소모될 수 있다. 쿼리 캐시는 절대 여러 스레드에서 동시에 변경할 수 없기 때문에 다른 스레드는 쿼리 캐시 삭제 작업이 완료될 때까지 기다려야 한다. 따라서 너무 큰 쿼리 캐시 설정 자체가 부하를 유발할 수 있다.

 

일부 쿼리의 큰 결과는 쿼리 캐시를 모두 독점할 수도 있기 때문에 이러한 현상을 예방하고자 특정한 크기 미만의 쿼리 결과만 캐시하도록 설정할 수 있다. 설정 파라미터는 query_cache_limit 이며 설정에 크기 미만의 쿼리 결과만 캐시한다. 일반적으로 1~2M 미만으로 설정한다.

 

[쿼리 캐시를 사용하지 못하는 경우]

  • 임시 테이블에 대한 쿼리
  • 사용자 변수의 사용(프리페어 스테이트먼트와 동일하게 작용)
  • 칼럼 기반의 권한 설정
  • LOCK IN SHARE MODE 힌트
  • FOR UPDATE 힌트
  • UDF(User Define Function)사용
  • 독립적인 SELECT문장이 아닌 일부분의 서브 쿼리
  • 스토어드 루틴(Procedure, Function, Trigger)에서 사용된 쿼리
  • SQL_NO_CACHE 힌트

 

2. Memecached

분산 메모리 캐싱시스템이다. 데이터 베이스의 부하를 줄여서 동적 웹 어플리케이션의 속도개선을 위해 사용된다. 클라이언트가 요청한 데이터들(DB, API 호출 또는 렌더링 등)을 key-value  형태로 메모리에 저장하는 방식이다.

 

Memecached는 필요량보다 많은 메모리를 가졌을 때 시스템으로부터 메모리를 사용하고, 필요로 하는 메모리가 부족한 경우 이를 더 쉽게 가져다 사용할 수 있도록 만들어준다.

 

Memecached를 사용하지 않을 때는 분리되어 있는 메모리에 대해 각각의 서버에서는 할당된 메모리크기만큼만 사용할 수 있다. 하지만 사용할 경우에는 각 서버가 논리적으로 결합되어 있기 때문에 각 웹서버는 전체 메모리 캐시만큼의 용량을 사용할 수 있다. 즉 효율적으로 메모리 운영이 가능해진다는 것이다.

 

[Memecached를 사용하지 않을 경우]

각 노드는 완벽하게 독립적. 각 서버에 할당된 캐시크기만큼 사용할 수 있으므로 웹팜(여러 대를 사용해서 웹사이트를 구축한 형태)의 캐시 사이즈는 128MB이지만 각 서버에서 사용할 수 있는 사이즈는 65MB이다.

 

[Memecached를 사용할 경우]

Memecached로 묶인 모든 서버는 동일한 가상 메모리 풀을 공유한다. 분산 메모리 캐시를 적용하게 되는 것이므로 캐싱을 통해 DB나 API 호출에 대한 횟수를 줄일 수있고 이로인해 응용프로그램의 수요나 DB 데이터 접근에 대한 부하를 줄여 성능을 향상할 수 있다. 

 

 

Redis와 대표적으로 많이 사용되는 캐시서버인데, Memecached는 명료하고 단순함, 빠른 속도를 위하여 사용된다. 또한, 멀티스레드를 지원하기 때문에, 멀티프로세스코어를 사용할 수 있다. 따라서, 스케일업을 통하여 더욱 많은 작업처리를 할 수 있다.

 

3. Redis

데이터베이스(NOSQL)로 분류가 되기도 하고, Memecached와 같이 인메모리 솔루션으로 분류되기도 한다. Memecached와 비교하면 다양한 데이터 구조(Message Queue, Shared memory, Remote Dictionary)를 지원함으로써 다양한 용도에 효과적으로 사용할 수 있다는 특징을 지니고 있다. NOSQL 관점에서 봤을 때 Redis는 가장 단순한 key-value 타입을 사용하고 있다. 즉 단순한 구조를 통해 높은 성능을 보장하는 것이다. 

 

사용하는 이유

  • 데이터 저장소로 가장 입/출력이 빠른 메모리를 채택
  • 단순한 구조의 데이터 모델을 통해 빠른 속도를 보장
  • 캐시 및 데이터스토어에 유리
  • 다양한 API 지원
  • 급격히 사용자가 집중되는 상황이나 대규모 확장이 예정되어 있을 때 적합하다.

 

Redis의 특징

1. 다양한 데이터 구조

Memecached와 달리 Key-value 형식으로 데이터가 저장될 때, value는 단순한 object가 아닌 다양한 데이터 형을 갖는다.

문자열 뿐만 아니라 List, Set, 정렬된 Set, Hash, Bit 배열, hyperloglogs (매우 적은 메모리로 집합의 개수를 추정할 수 있는 방법)을 지원한다. 또한 실시간 위치기반데이터를 지원한다. 따라서, 두 위치의 거리를 찾거나, 사이에 있는 요소 찾기등의 작업을 수행할 수 있다. 이를 활용하여 맛집, 길찾기 그리고 지도기반의 고성능 서비스를 제공할 수 있다.

 

2. 복제

Master — Salves 구조로, 여러개의 복제본을 만들 수 있다. 따라서 데이터베이스 읽기를 확장할 수 있기 때문에 높은 가용성(오랜 시간동안 고장나지 않음) 클러스터를 제공한다. 복제는 non-blocking 상태로 이루어진다. 따라서 쿼리연산을 하고 있는 동안에 동시에 데이터를 복사할수도 있다는 이야기이다. 이 특징의 단점은 데이터 불일치성을 유발할 수 있다는 이야기이다. 

 

3. query off loading을 통한 성능향상

복제를 통해 성능을 향상시키고 동시 접속자수나 처리 속도를 늘릴 수 있다. 이를 위해 query off loading이라는 기법을 사용한다. 이 기법은 master node는 write only/ slave node(복제된 노드)는 read only로 사용하는 방법이다. 이는 트랜젝션을 분산 시킨 것이다. 

 

4. Pub / Sub messaging

Publish(발행)과 Sub(구독)방식의 메시지를 패턴 검색이 가능하다. 1:N의 형태로, 하나의 클라이언트가 메시지를 publish하면, 이 topic에 연결되어 있는 다수의 클라이언트가 메시지를 받을 수 있는 구조이다. 일반적으로 pub/sub 시스테의 경우, sub하는 하나의 topic에서만 subscribe하는데 반해서 레디스에서는 다수의 topic에서 message를 subscribe할 수 있다.

따라서 높은 성능을 요구하는 채팅, 실시간 스트리밍, SNS 피드 그리고 서버상호통신에 사용할 수 있다.

5. key-value 저장방식

redis는 특정 키 값을 저장하는 방식으로 구성되어 있다. 데이터는 메모리에 존재해서 데이터 읽기/쓰기의 속도가 매우 빠르다. 따라서 전체적으로 저장이 가능한 데이터 용량은 물리적인 메모리 크기를 넘어설 수 있다. 

 

6. Persistence

Memecached는 메모리에만 데이터를 저장하기 때문에 서버가 shutdown 된 후에 데이터가 유실되지만 redis는 disk에 데이터를 저장해 놓고, 서버가 shutdown된 후 다시 restart되더라도, 저장해둔 데이터를 다시 읽어서 메모리에 로딩하기 때문에 데이터가 유실되지 않는다.  

데이터를 저장하는 방식에는 두가지가 있다. 

a. snapshot(RDB)

순간적으로 메모리에 있는 내용을 DISK에 옮겨 담는 방식이다. 단점은 snapshot을 추출하는데 시간이 오래 걸리며, 추출된 후 서버가 다운되면 추출 이후 데이터는 유실된다.

b. AOF

redis의 모든 write/update 연산 자체를 모두 log 파일에 기록하는 형태이다. operation이 발생할 때마다 매번 기록하기 때문에 RDB방식과는 달리 특정 시점이 아니라 항상 현재 시점까지의 로그를 기록할 수 있다. 어느 시점에 server가 다운되더라도 데이터 유실이 발생하지 않는다. 하지만 로그 데이터 양이 과대하게 크며 복구시 저장된 것을 replay 하기 때문에 restart 속도가 빠르다. 

 

7. Sharding을 통한 용량 확장 

redis 자체는 클러스터링을 통한 확장성을 제공하지 않는다. 따라서 데이터의 용량이 늘어났을 경우, 여러개의 redis 서버를 구성한 후에, 데이터를 일정 구역별로 나눠서 저장하게되는데 이것 이 sharding이라는 아키텍쳐를 이용하는 것이다.

8. Expriation

데이터에 생명주기를 정해서 일정시간이 지나면 자동으로 삭제되게 할 수 있다. 두 가지 방식이 존재하는데, active 방식은 client가 expired된 데이터에 접근하려고 했을 때 체크해서 지우는 방법이며, passive 방식은 주기적으로 key들을 random으로 100개만 스캔해서 지우는 방식이다. 

expired time이 지난 후 클라이언트에 의해서 접근되지 않은 데이터는 active 방식으로 인해서 지워지지 않고 passive 방식으로 지워저야 하는데 이 경우 passive 방식의 경우 전체 데이터를 스캔하는 것이 아니기 때문에 redis에는 항상 expired 되었으나 지워지지 않는 데이터가 존재하게 된다.

 

주의점

위의 특징만 보면, 모든 상황에서 Redis를 선택해야할 것 같다. 하지만, Redis는 싱글 쓰레드이기 때문에, 1번에 1개의 명령어만 실행할 수 있다. (Memecached는 멀티스레드를 지원하여 멀티프로세스코어를 사용할 수 있다) Keys(저장된 모든키를 보여주는 명령어)나 flushall(모든 데이터 삭제)등의 명령어를 사용할 때, Memecached의 경우 1ms정도 소요되지만, 레디스의 경우 100만건의 데이터 기준 1초로 엄청난 속도 차이가 있다.

또한, RDB 작업(특정 간격마다 모든 데이터를 디스크에 저장)이 매우 오래걸린다. AWS, 60기가 메모리 기준으로 10분이나 소요된다. Redis 장애에 원인의 대부분이 해당 기능 때문에 발생하기 때문에 사용할 때 주의해야한다.

 

Redis는 다양한 데이터를 처리할 때, Memecached는 읽기등의 단순한 작업을 스케일 업을 통해 많이 처리해야 할때 사용한다고 정리할 수 있다.

 

 

Reference

 

참고 자료1

참고 자료2

참고 자료3

728x90

+ Recent posts