NoSQL
말 그대로 No SQL, 즉 'SQL을 안 쓴다'는 뜻으로 해석할 수 있다. SQL을 활용할 수도 있다는 뜻으로 'Not Only SQL'을 뜻한다고 주장하기도 하지만, 사실 NoSQL 데이터베이스들이 SQL 기반의 관계형 데이터베이스와는 구조나 사용 방법이 상당히 동떨어져 있다 보니, 이런 데이터베이스를 쓰다 보면 SQL을 뭐하러 쓰지? 싶은 생각이 든다.
등장 배경
빅데이터 시대가 되면서 특히 NoSQL이 주목 받고 있다. 빅데이터는 데이터의 양도 엄청나지만 데이터의 형태가 정형화되어 있지 않고 워낙에 변화무쌍한 것들이 많아서 데이터 구조의 일관성을 중시하는 관계형 데이터베이스에서는 까다로운 부분이 많고 굉장히 많은 가공을 해야 한다. 게다가 데이터의 양 자체가 어마마하게 많다 보니, 서버의 저장용량이 빠르게 바닥이 나고 서버가 데이터를 처리하는 능력도 빠르게 힘에 부친다. 관계형 데이터베이스는 이럴 때마다 서버를 업그레이드하는 방식으로 대처한다. 더 빠른 프로세서, 혹은 더 많은 멀티 프로세서, 더 많은 메모리, 더 많은 저장장치를 거느린 서버로 교체하는 것이다. 이것을 수직 스케일(vertical scaling)이라고 한다. 그런데 빅데이터의 시대에 데이터가 불어나는 속도는 기하급수적으로 빨라지는데 하드웨어의 발전 속도는 오히려 둔화되어 데이터의 증가를 따라잡지 못하는 지경에 이르렀다. 이를 극복하기 위해서는 뭔가 다른 방법이 필요해진 것이다. 이 때문에 대두되는 것이 수평 스케일링(horizontal scaling)인데, 서버 하나의 성능을 업그레이드하는 게 아니라 서버의 수를 늘리는 것이다.
게다가 이제는 글로벌 서비스의 시대다. 예전에도 인터넷 망을 통해서 전 세계 어느 웹사이트에 접속할 수 있었지만 대체로 인터넷 서비스에는 국경이 있었고 대부분의 트래픽은 인터넷 서비스를 제공하는 국가 안에서 발생했다. 글로벌 인터넷 서비스라고 해도 나라별로 따로따로 서비스를 제공했다 야후를 생각해보라. 미국 야후와 한국 야후, 일본 야후는 각각 서버를 따로 구축하고 각각 그 나라에 전용 서비스를 제공했다. 물론 데이터베이스도 각 나라의 서비스별로 따로 따로 구축해서 사용했다.
하지만 지금은 페이스북은 어떤가? 물론 접속하는지 여기 다니면 표시되는 언어나 광고는 다를 수 있다. 그러나 이것은 똑같은데 페이스북 서비스가 사용자의 접속 지역을 파악해서 그에 맞는 인터페이스나 광고를 보여줄 뿐이지 서비스 자체는 글로벌 공통이다. 내가 한국에서 페북에 포스팅을 하면 한국은 물론 아시아 여러 나라에서, 미국에서, 유럽에서, 아프리카에서 댓글이 달린다. 이제는 하나의 인터넷 서비스에 엄청난 트래픽이 쏟아진다. 만약 서비스를 제공하는 서버와 데이터베이스 서버가 페북 본사가 있는 미국에만 있다면 어떤 일이 일어날까? 아마도 글 하나 올리려면 최소 몇 초에서 몇 십초 이상 걸릴 것이고 사진이나 동영상을 올리려면? 더 이상의 자세한 설명은 생략한다. 그나마 인터넷 천국인 우리나라나 이런 정도지 우리나라보다 인터넷 사정이 나쁜 수많은 다른 나라는 거의 서비스 이용이 불가능할 것이다.
따라서 페이스북이나 구글 같은 인터넷 기업들은 세계 각지에 서버와 데이터베이스를 분산시켜서 서비스를 제공하고 있다. 이는 과거에 국가별로 따로 서버와 데이터베이스를 구축하고 국가별로 서비스를 제공 했던 것과는 다른 개념이다. 예를 들어 미국에서 저 멀리 떨어진 아프리카의 어느 나라에서 누군가가 페이스북에 글을 올렸다면 일단 그곳에서 제일 가까운 페이스북 서버의 데이터베이스에 그 글이 저장되고, 그 다음 세계 각지에 있는 서버들이 서로 변경된 내용을 주고받음으로써 데이터베이스를 일치시킨다.
이러한 시대의 관계형 데이터베이스의 한계는 점점 명확하게 드러나고 있다. 관계형 데이터베이스는 중앙집중식 시스템을 기본 전제 조건으로 한다. 즉 하나의 고성능 서버가 하나의 데이터베이스를 관장하는 것이다. 한 가지 단위의 정보는 하나의 테이블 안에 있어야 하며, 서로 관계가 있는 테이블은 하나의 데이터베이스 안에 있어야 한다. 수직 스케일링으로는 감당이 안 되다 보니 관계형 데이터베이스도 클러스터링이나 샤딩 같은 분산처리 기법들을 통해 수평 스케일링을 지원하고는 있지만 구현하기도 어려울 뿐더러 제약 조건도 많다. 글로벌로 콸콸콸 쏟아져들어 오는 빅데이터를 감당하기에는 역부족이다. 더 나아가 지금처럼 데이터베이스가 글로벌 여러 지역으로 분산되어 있는 상황에서는 샤딩으로는 답이 안 나온다.
NoSQL의 특징
NoSQL의 전략은 관계형 데이터베이스에서는 철칙처럼 되어 있는 무결성이나 일관성, 정규화를 조금 양보하는 대신 분산 처리에 최적화된 데이터베이스, 즉 수평 스케일링을 하기 좋은 데이터베이스를 구현하자는 것이다. 기존의 관계형 데이터베이스는 무결성이 보장되어야 한다. 즉 데이터에는 어떤 결함도 없어야 한다. 예를 들어 사용자를 아이디로 식별해야 하는데 사용자 정보 테이블에 같은 아이디를 가진 레코드가 두 개 있으면 안 된다. 데이터베이스 저장소가 여러 곳에 복제되어 있더라도 데이터베이스의 변화가 생길 경우 모든 복제본에 최대한 실시간으로 그 내용이 반영되어야 한다. 그래야 무결성을 보장할 수 있기 때문이다. 또한 스키마에 맞지 않는 데이터는 받지 않는다. 기존 스키마 구조와 맞지 않는 데이터가 있으면 스키마를 수정 또는 확장하든지 데이터를 스키마에 맞게 수정하든지 둘 중 하나다.
반면 NoSQL은 일단 쏟아져 들어오는 데이터는 각자 알아서 받은 다음에 그 내용들을 다른 데이터베이스에 반영하자는 것이다. 일시적으로는 무결성에 문제가 좀 생기더라도 일단 분산 처리 능력을 최대화 하는 쪽에 초점을 맞추고 있는 것이다.
또한 NoSQL은 일관성을 크게 강제하지 않기 때문에 들어오는 대로 일단 막 집어넣고 보자는 전략으로 나갈 수 있다. 애초에 스키마라는 개념을 사용하지 않으며, 따라서 데이터의 구조는 들어오는 데이터에 따라 유연하게 변할 수도 있다. 관계형 데이터베이스는 같은 테이블에 있는 레코드는 항상 같은 필드를 가지고 있지만 (값이 없으면 빈 값이나 NULL 값이라도 들어가 있어야 한다) NoSQL은 같은 컬렉션에 있어도 레코드마다 거느리고 있는 속성은 제각각일 수 있다.
데이터의 일관성은 관계형 데이터베이스가 가진 데이터의 무결성과 같은 장점을 보장하지만, 어떤 때에는 데이터의 처리를 까다롭게 만들고 경직되게 만드는 원인이기도 하다. 주소를 예로 들어 보자. 우리나라의 과거 형태 주소, 즉 지번 기반 주소를 외국의 도로명 기준 주소와 비교하면 다음과 같다.
- 지번 주소 : 시/도, 구, 동, 번지, 세부주소
- 외국의 도로명 주소 : 주, 시, 도로명, 건물번호, 세부주소
이게 다가 아닐 것이다. 어떤 나라는 또 다른 요소로 구성된 주소를 쓸 수도 있다. 만약 국제적인 쇼핑몰이라면 어떤 식으로 된 주소든 다 받아줘야 하는데, 나라마다 주소 체계가 제각각일 때 이를 받아주려면 테이블에 이들 모든 요소가 다 속성으로 정의되어 있는 테이블을 짜고, 각 튜플마다 필요 없는 속성은 비우는 식으로 처리해야 한다. 위의 주소 예라면,
주 | 시/도 | 구 | 동 | 도로명 | 번지/건물번호 | 세부주소 | |
---|---|---|---|---|---|---|---|
지번 주소 | — | 서울시 | 영등포구 | 여의도동 | — | 1-1 | 내위키아파트 101호 |
도로명 주소 | Victoria | Melbourne | — | — | Bourke Street | 1 | 1F |
국제적으로 주소를 저장하려면 나라 이름도 저장해야 할 텐데? 넘어갑시다.
그런데 한국은 아파트를 무지 좋아하는데, 아파트 이름을 그냥 세부주소에 다 퉁쳐서 저장하는 것은 나중에 검색이나 분류를 할 때 좋지 않다. 그래서 아파트는 따로 분리하는 게 좋을 것 같다. 이렇게 각국의 특성에 맞춰서 주소 스키마를 짜려면 밑도 끝도 없다. 나름대로 최대한 고려해서 크고 아름다운 테이블을 만들었는데 나중에 가서 또 다른 나라의 가입자를 받아야 하고 주소 체계가 다르다는 사실을 알게 되면 대략 멘붕 상태에 빠지게 된다.
반면 NoSQL은 미리 스키마를 짜놓고 딱 그에 맞게 데이터를 넣는 게 아니라, 데이터를 넣다가 분명 이 범주에 들어가야 할 데이터인데 구성 요소가 다르다면 그냥 그에 맞는 새 속성(필드)을 정의해서 새로 밀어넣을 수 있다. 그렇다고 기존 데이터에 새로운 필드가 추가되지도 않는다. 즉 데이터 구조의 일관성을 강제하지 않는다.
관계형 데이터베이스에 익숙해 있던 사람들을 당혹스럽게 하는 문제는 여러 가지가 있는데 먼저 무결성 문제로, 이를테면 NoSQL은 외래 키 개념이 없다. 예를 들어 관계형 데이터베이스는 테이블 b를 정의할 때 특정 속성이 테이블 a의 키값이 되도록 정의할 수 있으며, 만약 이 속성에 a의 키값이 아닌 다른 값이 들어 있는 데이터를 테이블 b에 집어넣으려고 하면 거부한다. NoSQL은 이런 거 없다. 그냥 다 받아준다.[1] 또한 관계형 데이터베이스는 테이블 b에서 키값으로 쓰이고 있는 테이블 a의 어떤 튜플을 삭제하려고 하면 이 역시 거부한다. 먼저 테이블 b에서 해당 튜플을 지워야 한다. NoSQL은 이런 게 없다. 물론 외래 키 참조가 안 되는 건 아니지만 데이터베이스가 자체적으로 무결성 체크 같은 것을 하지 않는다. 그렇기 때문에 무결성이 중요한 데이터라면 NoSQL이 오히려 지옥이 될 수 있다.
또한 정규화도 권장하지 않는다. 관계형 데이터베이스는 한 가지 종류의 정보는 한 테이블에만 있어야 하며 그 테이블에 있는 정보를 저장해야 하는 다른 테이블은 외래 키(foreign key)를 통해 참조만 해야 하며 정보를 가져올 때에는 JOIN 연산을 사용한다. 그래야만 데이터가 변경될 때 그 데이터가 담긴 테이블 하나만 변경하면 되기 때문이다. 같은 정보가 여러 테이블에 중복되어 있다면 이를 일일이 찾아다니면서 다 수정해야 하는데 이게 귀찮기도 한 데다가 만약 수정을 못한 정보가 있다면 무결성이 깨진다. 그러나 NoSQL은 정규화를 중시하지 않는다. 즉 같은 정보가 여러 곳에 중복되어도 된다는 것이다. 관계형 데이터베이스에 익숙해져 있는 사람들에게는 황당무계한 이야기로 들린다. 게다가 NoSQL은 대부분 JOIN 연산도 지원하지 않는다. 따라서 NoSQL을 예전과 같은 외래 키 참조 방식으로 사용하려면 질의 횟수가 엄청나게 많아져서 효율이 떨어진다.
NoSQL이 JOIN 연산을 지원하지 않는 이유는 수평 스케일 때문이다. 데이터가 여러 서버에 분산되어 있으며, 심지어는 한 종류의 데이터 컬렉션조차도 한 서버에 모여 있다는 보장이 없다. 서버를 넘나들면서 여러 종류의 데이터를 JOIN 연산으로 가져오는 것은 비용이 너무 크다.[2] 또한 데이터는 대체로 축적되는 경향이 있으며 변경되는 일은 별로 없는 데다가 다른 데서 참조를 할 때에도 모든 데이터 필드가 다 필요한 것이 아니다. 따라서 자주 변경될 일이 없는 것은 그냥 중복시키고, 자주 변경되는 것만 따로 저장했다가 참조하든가 하는 전략이 더 효율이 좋다. 정규화가 나쁘다는 게 아니라 정규화와 수평 스케일의 두 마리 토끼를 모두 잡을 수가 없기 때문에 NoSQL은 수평 스케일을 선택하는 것이다. 정규화가 중요하다면 그냥 관계형 데이터베이스를 쓰면 그만이기 때문이다. 조건에 맞는 레코드의 수만 뽑아내는 것도 안 된다. 전부 다 쿼리해서 가져온 다음 숫자를 세거나 따로 숫자를 기록해 두거나 해야 한다.[3] 이렇듯 관계형 데이터베이스라면 손쉽게 쓸 수 있던 기능을 NoSQL은 사용할 수 없는 게 한두 가지가 아니므로 데이터베이스 모델을 설계할 때 아예 접근 방법부터 달리 가져가야만 한다.
NoSQL은 SQL처럼 뭔가 표준화된 인터페이스가 없다 보니, 데이터베이스 제품별로 데이터를 넣고 빼고 가져오는 방법이 제각각이라는 것. 물론 SQL 기반 데이터베이스들도 자체 기능을 사용하기 위해서 표준에서 벗어난 구문들이 종종 추가되지만 그래도 SQL이라는 뼈대 위에 추가되는 형태인데 반해, NoSQL은 애초에 데이터베이스를 구현하는 기본 개념부터가 제품마다 제각각이다. 게다가 일관된 스키마를 강제하지도 않다 보니, 어떤 제품을 쓰다가 다른 제품으로 전환해야 할 때에는 특히나 마이그레이션 과정이 관계형 데이터베이스와는 비교도 할 수 없이 돌아버릴 수 있다. 따라서 애초에 제품을 잘 선택하는 게 정말로 중요하다. 게다가 관계형 데이터베이스야 어느 정도는 선택이 정해져 있는데 반해[4], NoSQL은 아직까지는 군웅할거 체제라서 제품 선택이 쉬운 문제가 아니다.
NoSQL이 기존 관계형 데이터베이스의 한계점을 극복하기 위해서 등장한 것이긴 하지만 그렇다고 모든 문제를 해결해 주는 것도 아니고, 또한 반대로 NoSQL보다 관계형 데이터베이스가 더 나을 때도 많다. 관계형 데이터베이스는 데이터베이스 분산이 어렵고 구조가 경직되어 있지만 그게 큰 문제가 아니라면 속도와 효율성이라는 면에서 NoSQL보다 우위를 보이는 게 보통이다. 여전히 많은 서비스들은 관계형 데이터베이스를 기반으로 하고 있다. NoSQL이 가진 문제점을 모두 해결해서 제대로 쓰고 있는 기업은 구글 뿐이라는 말이 있을 정도다.[5][6] 기존 데이터베이스를 버리고 NoSQL로 갈아타는 게 능사가 아니라 과연 어느 쪽이 필요한 데이터를 표현하고 활용하는데 더욱 효율적인가를 판단해야 한다. NoSQL을 사용할 때에도 많은 기업들은 둘 다 사용한다. 즉 NoSQL이 꼭 필요한 부분만 이걸 쓰고 그렇지 않으면 기존의 관계형 데이터베이스를 쓰는 곳이 많다.
NoSQL은 대체로 텍스트 검색이 안 된다. 즉 SQL에서 LIKE 구문을 통해서 텍스트 안에 어디에 있든 내용을 검색하는 기능이 NoSQL에서는 처음 혹은 끝과 일치하는 텍스트만 찾을 수 있는 정도의 기능만 제공한다. 텍스트 검색 기능을 쓰고 싶다면 ElasticSearch 혹은 Algolia 같은 외부 검색 엔진을 쓰던가 해야 한다.
데이터베이스 모델을 설계할 때 기존 관계형 데이터베이스와 NoSQL은 접근 방식이 달라야 한다. RDBMS는 데이터를 얼마나 잘 정규화하고 관계를 설정하는가에 초점을 맞춘다. 질의를 할 때에 JOIN 연산을 여러 개 연결해서 쓸 수도 있으므로 정규화와 관계 설정만 잘 되어 있으면 별의 별 복잡한 질의를 여러 테이블에 걸쳐서 할 수 있다. NoSQL은 JOIN 지원을 하지 않으므로 이렇게 할 수 없으며, 데이터를 중복시키는 데 거 효율이 높다. 따라서 NoSQL은 '질의' 중심으로 접근해야 한다. 즉 이 데이터를 어떻게 가져다 써먹을 것인지에 초점을 맞춰야 한다. RDMBS는 어떻게 가져다 써먹을 것인지를 SQL이 책임진다면, NoSQL은 애초에 데이터 저장 구조가 이를 책임져야 한다. 따라서 먼저 어떤 질의를 해서 데이터를 가져올 것인가를 리스트 업 하고, 여기에 맞춰서 가장 효율적인 데이터 구조를 데이터 중복을 불사하고서라도 짜야 한다.
종류
구현체마다 차이들이 있지만 크게 네 가지 종류로 나눈다.
- 키-값 기반 : HBase
- 문서 기반 : 관계형 데이터베이스의 레코드에 해당하는 것을 여기서는 '문서'(document)라고 부르고, 이 문서의 모음을 컬렉션(collection)이라고 한다. NoSQL답게 같은 컬렉션 안에 있는 문서라고 해도 일관된 스키마 같은 건 없으며, 문서 안에 문서나 컬렉션이 들어갈 수도 있다. 다만 문서를 식별할 수 있도록 유일한 키값은 존재한다. 여기에 해당하는 데이터베이스로는 몽고DB, 카우치베이스, 구글 클라우드 파이어스토어가 있다. 문서 형식으로는 JSON이 대세다.
- 컬럼 기반 : 구글 BigTable
- 그래프 기반
각주
- ↑ 다만 참조 데이터 유형으로 다른 '문서'를 URL이나 경로 방식으로 참조하는 경우에는 실제 그 문서가 존재하는지 검사하는 정도는 있다.
- ↑ 게다가 구글의 클라우드 파이어스토어와 같은 클라우드 기반 NoSQL이라면 쿼리 횟수에 따라 돈을 내야 하므로 사용자의 비용 부담도 눈덩이가 된다.
- ↑ 만약 중복 저장된 데이터를 변경해야 한다면 일일이 다 찾아다니면서 쿼리하는 방법이 있고, 서버가 트리거 함수를 제공한다면 이를 통해 이벤트 핸들링 방식으로 변경하는 방법이 있다. 이런 함수를 제공한다면 아마도 자바 또는 자바스크립트로 프로그래밍할 가능성이 높을 것이다.
- ↑ 금융권처럼 데이터베이스가 안 죽는 게 중요하다면 거의 오라클이고, 윈도우 서버를 쓴다면 MS의 SQL서버가 있을 것이다. 무료 쪽은 MySQL 또는 MariaDB, 혹은 PostgreSQL 정도가 거의 대부분의 선택이다.
- ↑ http://eincs.com/2012/06/nosql-is-not-useful/
- ↑ 사실 NoSQL의 개념을 처음으로 정립한 곳이 구글이기도 하다. 상당수 NoSQL 솔루션들이 구글의 빅테이블 관련 논문을 기초로 하고 있다.