▼Advertisement
right event
▼Advertisement
▼Advertisement
읽을거리 > 디벨로퍼 플러스

In-Memory 데이터베이스 시스템

유비쿼터스라는 단어가 더 이상 낯선 용어가 아닌 지금, 임베디드 소프트웨어에 대한 관심이 늘어남에 따라 관련 기술들은 아주 빠르게 발전하고 있다. 특히 임베디드 시스템이 처리해야 할 데이터의 양이 많아지고 처리해야 할 내용도 복잡해지고 있는데, 거기에 여러 디바이스가 네트워크로 연결되면 그 복잡도는 거의 지수승으로 늘어나게 된다. 그래서 기존의 In-House 방식으로는 데이터를 처리하기 쉽지 않게 되어 데이터를 좀 더 효율적으로 관리하게 될 방법이 요구되고 있다. 이런 경우에 사용될 수 있는 데이터베이스 시스템이 In-Memory 데이터베이스 시스템(In-Memory Database System, 이하 IMDS)이다.

IMDS는 실시간 임베디드 응용프로그램의 데이터를 관리하는 데 있어서 그 효용성이 입증되고 있는데, 이 글에서는 IMDS가 우리가 잘 알고 있는 기존의 DBMS(Database Management System)들과 어떤 차이가 있는지에 대해 초점을 맞춰 보겠다(이 글은 필자가 McObject사의 Ted A Kenney(ted@mcobject. com)와 많은 의견 교환을 거치며 작성한 것임을 미리 밝혀둔다).

IMDS의 현주소
IMDS는 근래에 나온 개념이나 제품은 아니다. 우리나라에도 이미 여러 상용 제품들과 오픈소스 제품들이 시장에 나와 있는데 아직까지 임베디드 개발자들에게 그 인지도는 그다지 크지 않은 것 같다. 우선 기존 RDBMS의 공룡 기업들이라고 할만한 오라클이나 IBM은 모두 관련 중소기업들을 합병해 TimesTen, SolidDB라는 제품들을 가지고 있고, IMDS 전문 업체인 McObject는 eXtremeDB라는 제품을 가지고 있다. 오픈소스 진영에서도 여러 제품들이 나와 있어서 자신들이 IMDS라고 하고 있다.

그런데 관련 내용을 가지고 실제 업무를 하는 엔지니어들과 이야기를 해보면 보통 이런 제품이 있다는 사실 자체를 모르는 경우가 많음을 알게 된다. 또한 이런 제품들을 주로 사용하게 될 임베디드 시스템 개발자들은 상대적으로 DB의 개념이 약한 터라 DBMS가 왜 필요한 것이고 어떻게 쓰여야 할지에 대해 모르는 경우가 많다(필자의 경험을 이야기하면, 이런 이유로 인해 IMDS에 대한 접근이 오히려 기존의 RDBMS를 사용하는 개발자들보다 임베디드 개발자들이 더 쉬운 것 같다. 일례로 이런 IMDS는 보통 SQL 인터페이스도 제공하지만 일반적으로는 자체 API를 제공하여 이 API를 사용하여 프로그래밍을 하게 되며 자체 API를 사용하는 것이 성능이 더 좋으므로 이를 권장하게 된다. 그래서 아무래도 SQL에 익숙한 프로그래머들에게는 큰 거부감이 일 것이라고 생각이 들었는데, 실제 임베디드쪽 개발자들은 SQL을 모르므로 전혀 문제가 되지 않아 기우로 그친 적이 많다).

사실상 임베디드 개발자에게는 IMDS는 DB보다는 데이터를 저장하는 방식으로만 여겨질 수 있다. 이는 코드 사이즈도 작고 메모리에 저장하는 방식이나 전체적인 복잡성이 기존의 크고 복잡한 DB와 비교한다면 상대적으로 간단하기 때문이다. 오히려 DB보다 각자 필요에 의해 작성한 데이터 저장 코드와 더 비슷하게 여겨질 수 있다. 하지만 DB라는 이름이 붙은 만큼 IMDS는 ACID(Atomicity, Consistency, Isolation, Durability)를 준수해야 하며, 여러 유저가 사용하는 것에 대한 고려라든가 자료를 쉽게 찾아보기 위해 인덱스(index)를 여러 개 줄 수 있는 등의 유용한 기능들을 가지고 있다(인덱스를 여러 필드에 주는 코드를 작성한다고 생각해보라. 이런 상용 제품이 있다는 것이 무척 고마울 것이다).

하지만 이 기사는 IMDS가 무엇인가라는 이야기보다는 이것의 상대적인 개념인 디스크에 자료를 보존하는 방식과의 차이점을 주로 이야기하도록 하겠다(이 글에서는 In-Memory Database를 간단히 메모리 데이터베이스로, 기존의 방식으로 디스크에 자료를 보존하는 방식인 On-Disk Database를 간단히 디스크 데이터베이스로 칭하겠다. 흔하게 쓰이는 표현은 아니나 편의상 두 개를 비교하기 쉽게 하기 위함이다).

인메모리 방식, 뭐가 다른가?
메모리 데이터베이스 시스템은 메인 메모리에 레코드들을 저장한다는 것이 기존의 디스크 방식의 데이터베이스와 가장 큰 차이점이라고 할 수 있다. 그리고 이러한 차이가 디스크 데이터베이스와의 커다란 성능 차이를 가져오게 하는 주요한 이유가 된다.

그런데 메모리 액세스가 디스크 액세스보다 훨씬 빠르다는 것은 전혀 새로운 사실도 아니라서, 이미 기존의 데이터베이스 시스템들도 이러한 사실을 캐시(Cache)를 통해 잘 이용하고 있다. 즉, 자주 사용되는 레코드들은 캐싱을 통해 메모리에 넣어두고 사용해서 디스크 I/O를 없애면서 접근 속도를 빠르게 하고 있는 것이다. 이를 고려한다면 과연 메모리 데이터베이스는 정말 새로운 것인가하는 의문을 가질 수 있다.

그런가 하면 요새는 RAM-Disk 유틸리티를 사용해 메모리에 파일을 만드는 방법도 고려해볼 수 있다. 그렇다면 이미 잘 알려진 디스크 데이터베이스를 통째로 RAM-Disk에 올려놓으면 이것이 바로 메모리 데이터베이스가 되는 것은 아닐까? 디스크에 저장을 전혀 하지 않고 메모리에서만 모든 일이 벌어지므로 특별한 메모리 데이터베이스를 사용하지 않아도 같은 효과를 가지게 될 것 같다.

하지만 결론을 먼저 말하자면 디스크에 저장하는 것을 기반으로 만들어진 데이터베이스는 그 작동 방법이 메모리에 저장하는 데이터베이스와는 현저하게 다르므로 그 성능은 같지 않다. 즉, 메모리만 사용한다는 점은 같아도 방법이 다르므로 성능에 차이가 난다. 재미있는 점은 성능을 향상시키기 위해 사용하는 캐싱이 오히려 성능을 저하시키는 주요 원인이 된다는 것이다.

물리적인 디스크에 접근하여 읽고 쓰기를 하는 것은 기존의 전통적인 방식의 데이터베이스라면 데이터 프로세싱의 가장 마지막 단계에서 하게 되는 것이고 이는 모든 데이터가 영구적인 저장 장소에 저장되어야 한다는 생각에 기초하게 된다. 캐싱은 데이터를 읽는 경우에는 실제로 성능을 크게 향상시킨다. 하지만 데이터를 업데이트하는 경우는 캐시를 지나 결국 디스크에 저장해야 하므로 성능 향상에 크게 도움이 되지 않는다. 메모리 데이터베이스는 이러한 디스크로의 접근을 없애거나 그 기능 자체를 옮겨서 성능 향상을 하게 되는데 이를 살펴보도록 하자.

캐싱 오버헤드
일반적으로 캐싱은 디스크 데이터베이스의 성능을 크게 향상시킨다. 하지만 반대로 메모리 데이터베이스는 캐싱을 아예 없앰으로써 상당한 속도 향상을 얻게 된다. 좀 의아한 부분일 수 있는데, 캐싱이라고 하는 기능은 당연히 공짜가 아니다. 캐싱이 잘 이뤄지기 위해서는 캐시 동기화(Cache Synchronization)가 필요하고(이는 물리적인 디스크의 데이터베이스 페이지와 캐시에 있는 데이터베이스 캐시의 이미지가 동일한지를 체크한다), 실제 데이터를 요청하는 명령이 들어오면 일단 캐시에 가서 캐시에 있는지 확인한 후에(Cache Lookup) 데이터가 없으면 실제 페이지에 가서 데이터를 꺼내오게 된다.

이러한 캐싱 함수들은 디스크에서 레코드를 읽을 필요가 있을 때마다 호출되는데, 호출이 될 때마다 당연히 CPU 사이클을 잡아먹고 메모리를 사용하게 된다. 메모리 데이터베이스는 이러한 오버헤드를 가지지 않는다. 그래서 메모리 데이터베이스는 디스크 데이터베이스라면 항상 호출되는 캐싱 관련 함수들의 오버헤드가 없어져서 전체적인 성능이 올라가게 된다.

데이터 전송 오버헤드
데이터 전송 역시 디스크 데이터베이스를 느리게 하는 부분이다. 기존의 데이터베이스를 사용할 때, 프로그래머는 일반적으로 자신의 프로그램에 새로운 변수를 만들고 여기에 데이터베이스에서 가져온 데이터를 저장해 사용하게 된다. 이는 이미 데이터베이스에 실제로 저장돼 있는 데이터와는 몇 단계가 떨어져 있는 상황이 되는데, 디스크 데이터베이스의 경우 응용프로그램이 데이터를 읽어 와서 수정하는 과정은 여러 개의 단계를 걸치게 된다. 그 이동 경로를 보면 <그림 1>과 같다.

- 응용프로그램이 데이터베이스의 API를 이용하여 데이터베이스 런타임으로부터 데이터를 요청한다.
- 데이터베이스 런타임은 파일 저장소(디스크 또는 RAM-disk라면 메모리)에 있는 데이터를 가지고 오도록 파일시스템에 요청한다.
- 파일시스템은 데이터를 파일시스템 캐시에 복사하고 다른 복사본은 데이터베이스로 전달한다.
- 데이터베이스는 복사본을 데이터베이스 캐시에 저장하고 다른 복사본을 응용프로그램에 전달한다.
- 응용프로그램은 자신이 받은 데이터를 수정하여 데이터베이스 API를 이용해 다시 데이터베이스로 수정된 데이터를 보낸다.
- 데이터베이스는 수정된 데이터를 다시 데이터베이스 캐시로 보내 저장한다.
- 데이터베이스에 저장된 복사본은 결국 파일시스템에 보내 지는데, 이는 다시 파일시스템 캐시에도 저장된다.
- 마지막으로 데이터는 물리적 미디어(디스크 또는 RAM-disk)로 복사된다.

위에서 설명한 바와 같이 데이터를 데이터베이스에서 가져와서 수정하는 일은 상당히 많은 단계를 거치는데, 많은 단계가 캐시와 연결되어 있으며(처음에 언급했던 바와 같이 성능을 높이려는 캐시가 오히려 어느 경우에는 성능을 저하시킬 수도 있다) 많은 복사가 일어남을 알 수 있다.

여기에 비해 메모리 데이터베이스는 DB에 있는 데이터를 카피해 와서 내 지역 변수에 저장할 수도 있지만(이 경우도 여러 단계가 아닌 한 단계의 복사이다) 데이터베이스에 있는 데이터의 포인터를 직접 가져와서 그 포인터를 가지고 작업할 수 있다. 이 경우 특히 업데이트의 경우에는 전혀 부가적인 동작이 없이 빠른 작동을 할 수 있다.

운영체제에 대한 종속성
운영체제에 대한 종속성은 또 다른 성능 차이를 일으킬 수 있는 부분이다. 디스크 데이터베이스의 경우는 데이터베이스에 있는 데이터에 접근하기 위해 운영체제가 제공하는 파일시스템을 사용하게 된다. OS가 제공하는 데이터를 찾는 함수의 성능은 전체 성능에 영향을 주게 된다(리눅스의 lseek 함수 같은 것을 생각하면 된다). 메모리 데이터베이스는 파일시스템을 사용하지 않으므로 이런 문제에서 자유롭고 데이터 접근에 대해 최적화를 할 수 있게 된다.

트랜잭션 프로세싱에 대한 오버헤드
데이터베이스는 어떤 변화가 일어날 때마다 그 모든 변화들을 저널(트랜잭션 로그)에 기록한다. 그래서 문제가 발생했을 때 데이터베이스는 로그 파일로부터 트랜잭션을 커미트(commit)하거나 롤-백(roll-back)을 함으로써 데이터베이스를 다시 복구할 수 있다. 디스크 데이터베이스는 이러한 로그들을 항상 기록하고 있다가 트랜잭션이 커미트되면 로그 파일들과 캐시를 디스크로 플러쉬(flush)하게 되어 있다.

메모리 데이터베이스는 트랜잭션 로깅에 있어서 이러한 저널링(journaling)을 사용하는 방법이 보통 옵션으로 되어 있다. 예를 들어 McObject의 eXtremeDB의 경우에는 트랜잭션 로깅 외에 메모리를 사용하는 다른 방법을 제공하고 있는데, 데이터베이스는 수정 또는 삭제될 데이터의 그전 이미지와 트랜잭션 중에 추가된 데이터베이스 페이지를 저장하고 있다. 트랜잭션이 커미트되면 그전 이미지와 페이지를 가지고 있던 메모리는 다시 메모리풀로 돌아가는데 이는 아주 빠르고 효율적인 방법이다. 만약 데이터베이스가 트랜잭션을 취소해야 한다면(예를 들어 데이터가 들어오는 스트림이 이상해지는 등의 일로) 그 이전 이미지가 다시 데이터베이스로 돌아가고 새로 추가된 페이지는 메모리로 돌아가게 된다. 즉, 트랜잭션 로깅을 하는 방법도 메모리 데이터베이스가 디스크 데이터베이스보다 효율적으로 할 수 있다.

메모리 데이터베이스와 데이터 저장성
이상으로 기존 디스크 데이터베이스와 메모리 데이터베이스의 성능 차이가 날 수 있는 부분들을 살펴봤다. 메모리 데이터베이스 업체에서의 벤치마크 테스트 결과도 쉽게 구할 수 있는데, 기존 디스크 데이터베이스 시스템을 RAM-Disk에 올려 사용해도 크게는 10배 정도의 성능 차이가 남을 쉽게 알 수 있다. 사실 필자가 보기에 메모리 데이터베이스의 구현과 성능은 일반 디스크 방식의 데이터베이스보다는 In-House 방식으로 나름대로 만들어 사용하고 있는 자료 저장방식과 더 유사하다.

하지만 상용 제품들이 일반적으로 훨씬 좋은 성능과 안정성을 제공하고 있으며 또한 데이터베이스가 지켜야 할 기본적인 ACID(Atomicity, Consistency, Isolation, Durability) 트랜잭션을 지원하므로 신뢰할 수 있는 구현을 원한다면 당연히 사용을 고려하는 것이 좋다. 현대의 프로그래밍은 가능하면 컴포넌트를 구입하여 사용하는 것이 좋으므로 임베디드 개발자들은 메모리 데이터베이스를 주목하도록 하자.

메모리 데이터베이스를 사용함에 있어서 가장 걱정이 되는 부분은 아무래도 데이터가 휘발성 메모리에 저장되므로 문제가 발생했을 경우 데이터를 모두 잃어버릴 수 있다는 점이다. 그런데 응용프로그램들 중에는 이것이 문제되지 않는 경우도 많다. 어떤 임베디드 시스템의 경우는 메모리 데이터베이스의 내용이 시스템의 시작시 새로 채워지게 되는데, 예를 들어 셋톱박스에 있는 프로그램 가이드 응용프로그램은 계속적으로 내용이 업데이트되므로 미리 내용을 저장하고 있을 필요가 없다. 또한 네트워크 스위치는 프로그램 시작시 네트워크 토폴리지를 찾으며, 무선기기는 서버 쪽에서 보내는 정보로 접근점을 찾게 된다. 이러한 응용프로그램들은 애초에 데이터의 저장에 대해서는 크게 걱정할 필요가 없다.

물론 데이터를 실제로 저장해야 할 필요가 생기는 경우도 많다. 이 경우 역시 여러 가지 옵션이 존재하게 되는데 우선 주기적으로(또는 사용자가 원하는 시점에) 소켓이나 파이프 등을 열어 메모리 데이터베이스가 가지고 있는 데이터베이스 이미지를 읽거나 쓰는 기능을 제공해줄 수 있다. 물론 이는 갑자기 닥치는 재앙에 대비하기에는 좀 어려우므로 아예 휘발성이 아닌 메모리를 사용하여(예를 들어 배터리로 백업되는 메모리) 저장하는 방법도 제공된다. 또한 여러 개의 데이터베이스를 연동하여 동기화시킴으로써 사고가 터지면 다른 데이터베이스로부터 백업을 받기도 한다(물론 이는 일반적인 디스크 데이터베이스들도 백업 용도로 많이 사용하는 방법이다).

근래에 들어서는 벤더들이 메모리 데이터베이스와 디스크 데이터베이스를 섞어 쓰는 하이브리드 방식을 제공하기 시작했다. 디스크를 쓰게 되면 또다시 결국 디스크 데이터베이스와 똑같아 지는 게 아닌가도 싶으나, 이 경우에도 디스크에 꼭 저장해야 하는 데이터와 메모리에만 있어도 되는 데이터로 구분해 저장하는 방법들이 제공되므로 사용자가 데이터 별로 자신에게 필요한 방법을 사용할 수 있게돼 최적화된 방법을 사용할 수 있다.

또한 저장성과 성능을 둘 다 만족시키기 위해 메모리 데이터베이스는 디스크 데이터베이스의 프론트 엔드 역할을 할 수도 있다. 디스크 데이터베이스의 성능을 올리기 위해서 캐시를 이용하는 방법에 대해서는 앞에서 이미 언급하였는데, 아예 2개의 데이터베이스를 연동하여 일반적인 작업은 메모리 데이터베이스가 앞에서 빠르게 처리하고, 필요로 하는 경우에만 후방에 있는 디스크 데이터베이스(예를 들어 오라클)로 보내어 디스크에 저장토록 하는 방법이다. 이 경우는 이미 실제로 사용하고 있는 예들이 있으며, 대규모의 데이터를 아주 빠르게 읽고 쓰기 위해서 사용할 수 있는 방법이다.

많은 응용프로그램들은 그 특성상 디스크에 레코드를 저장하는 DBMS를 사용하게 될 터인데, 아주 빠른 성능이 필요하거나 아주 작은 메모리 풋프린트가 필요하거나 하면 메모리 데이터베이스가 사용될 수 있다. 즉, 메모리 데이터베이스는 대규모의 데이터를 다루어야 하는 개발자와 임베디드 개발자 모두에게 필요한 기술이다.

 

aboutmenu