전통적인 application은 관계형 데이터베이스 (RDBMS)를 기반으로 구축할 수 있었다. 그러나 현대 application은 관계형 데이터베이스 구조에 딱 맞는 데이터만을 다루지 않는다. 더 이상 데이터는 하나의 엔터프라이즈로부터 생산되지 않는다. 즉 빅데이터가 나타났으며 이 빅데이터를 다루기 위한 여러 데이터베이스 시스템 종류들이 등장한다. 따라서 빅 데이터를 정의, 분류하고 그에 따른 적절한 데이터베이스 시스템들을 훑어본다. 전체적인 흐름은 데이터베이스 시스템 (Abraham Silberschatz , Henry F. Korth , S. Sudarshan 저)의 10장 Big Data을 따른다.
목차
- 빅 데이터 (Big Data) 이전의 데이터
- 빅 데이터 쿼리 (Query)
- 빅 데이터 저장소 유형
- Distributed File Systems (DFS)
- Sharding
- Key-Value Storage Systems (Key-Value Store, NoSQL)
- Parallel and Distributed Databases
- 복제 (Replication)와 일관성 (Consistency)
빅 데이터 (Big Data) 이전의 데이터
1990년 이전까지는 하나의 엔터프라이즈가 데이터를 생산했다. 이 때의 데이터를 최초의 데이터로 볼 수 있고 단순히 웹 서버에 저장되는 사용자 활동에 대한 text log file이었다. World Wide Web (www)이 발전함에 따라 데이터는 여러 엔터프라이즈들로부터 생산되기 시작한다. 웹 서버의 텍스트 파일들에 대한 데이터의 활용도가 점점 유용해지기 시작한다. 데이터들을 광고, 사용자 활동 추적, 마케팅 등으로 활용할 수 있었기 때문에 단순히 웹 서버의 log file에 그칠 수 없었다.
2000년대에는 social-media가 등장하며 생산되는 데이터는 폭증하기 시작한다. 이제 데이터는 전통적인 RDBMS로는 다루기 힘들어졌다. 데이터의 양이 많아지고, 형태가 다양해지고, 데이터의 생산 (소비)속도가 빨라졌다. 데이터 처리를 위해 높은 병렬성을 요구한다. Big Data가 등장한다. 매우 고용량의 데이터로 종류가 다양하다.
- 모바일 폰에서 생산하는 데이터 : 사용자와 앱이 상호작용한 데이터, web site 클릭 등
- retain 기업의 온/오프라인 데이터 : 월마트 같은 대형 소매 업체
- 센서 데이터 : IoT에서 생산되는 데이터, High-end 장비들의 failure 방지를 위한 health-check 모니터링 데이터
- 네트워크 메타 데이터 : 트래픽 모니터링, 음성 통화 정보 등
빅 데이터 vs 관계형 데이터 (Relational Data)
데이터 용량이 빅데이터가 더 크다. 병렬 작업을 지원하는 관계형 DB는 최대 수백개 수준의 머신을 가지나, 빅 데이터는 수천 개의 머신을 가지고 저장, 병렬 처리하기를 요구한다. 빅데이터를 처리하는 데이터베이스 시스템에는 N개의 machine (노드, node)들을 클러스터링 하여 수천 개의 머신을 클러스터링 해서 빅데이터를 저장하고 처리한다.
데이터 전송 속도는 이전보다 빨라졌다. 그렇기 때문에 데이터의 저장 속도도 더 빨라야한다. 많은 applicatoin들이 DB에 저장되는 즉시 처리되기를 원한다. Streaming Data가 그 예이다. (유명 셀럽이 SNS에 글을 게시하는 순간 해당 데이터의 READ 연산은 최소 팔로우 수만큼 요청될 것이다)
데이터 다양성 역시 관계형 데이터보다 다양하다. 관계형 데이터는 스키마 (Schema)로 정의되지만, 빅 데이터의 형태는 다양해진다. 또한 관계형 데이터는 SQL이라는 쿼리로 접근하지만 고정된 문법의 SQL로는 빅데이터에 접근하기에는 한계가 있다.
빅데이터 쿼리 (Query)
이전에는 stand-alone 애플리케이션으로 데이터베이스 시스템을 구축했다. 이 때 데이터베이스에 인덱스 데이터 구조를 활용해 SQL 질의를 하였다. stand-alone 데이터베이스로는 빅데이터를 처리하기에 역부족이다. 병렬 처리 (클러스터링)를 요구하게 된다.
즉, 빅데이터를 쿼리하기에 SQL은 한계가 있다. SQL보다 더 범위가 넓고 다양한 쿼리가 필요하다. 더 다양한 데이터 타입과 규모에 대응하기 위함이다. 데이터는 더 이상 단순 text 파일이 아닌, image, video 도 가능하다.
방법 1. 높은 확장성 (Scalability)이 필요한 트랜잭션 처리 시스템
매우 짧고 빈번한 쿼리와 Update를 지원한다. key-value 저장소가 대표적인 예시이다. 데이터를 저장하고, key로 데이터를 질의한다. 예를 들면, SNS에서 사용자가 로그인하면 빠르게 친구들의 포스팅을 읽어 들어야 한다. RDBMS라면 로그인 사용자 식별값을 사용해서 join 연산을 할 것이다. key-value 저장소를 사용하면 <key : 사용자 식별값, value : 친구 목록>을 저장하여 빠르게 친구 목록을 가져올 수 있다.
메시지 큐 (Message Queue)도 대표적인 예시이다. 사용자가 포스팅을 하면, 친구에게 message (포스팅)를 전송하는 방식이다.
방법 2. 높은 확장성과 non-relational data를 지원하는 쿼리 처리 시스템
대용량의 실시간 log를 저장한뒤 분석하는 시스템, document (JSON) 저장, 웹 사이트에서 키워드 (해시태그) 검색 등을 지원할 수 있는 데이터베이스이다. 주로 하나의 데이터베이스 시스템에 N개의 애플리케이션에서 접근하는 구조다. 대용량 파일을 저장할 수 있어야 하고, 병렬 쿼리 처리를 지원해야 한다. 다양한 데이터타입을 지원해야 한다.
빅 데이터 저장소 유형
빅 데이터를 다루는 애플리케이션은 높은 확장성을 요구한다. 주로 수백만 유저와 수백개의 애플리케이션이 동시에 데이터베이스에 접근한다. 따라서 데이터는 수천 개의 컴퓨팅, 스토리지 노드에 걸쳐 파티셔닝 (partitioning)되어 저장된다.
- Distributed File Systems : 여러 머신에 걸쳐 분산 저장
- 파일에 대한 접근은 전통적인 파일 시스템 인터페이스를 통해 이루어짐
- 큰 파일을 저장할 때 사용 (e.g. 로그)
- Sharding across multiple Database : record를 여러 시스템에 걸쳐 파티셔닝해서 저장
- 클라이언트 소프트웨어가 record를 저장하고 접근할 노드를 결정
- Key-value store system : key를 통해 데이터를 저장, 접근
- 일부 제한된 쿼리 기능
- 완전한 독립된 데이터베이스 시스템은 아님
- NoSQL 시스템이라고 불림 (SQL을 지원하지 않기 때문)
- Parallel and distributed database : 데이터를 1개 이상의 머신에 걸쳐 저장하고, 쿼리 병렬 처리 지원
- 전통적인 데이터베이스 인터페이스
Distributed File Systems (DFS)
파일을 여러 머신에 걸쳐 저장한다. 클라이언트는 하나의 파일 시스템 인터페이스를 제공한다. 따라서 클라이언트는 파일이 실제 저장된 물리 위치 (어떤 머신에 저장)를 알 필요가 없다.
비구조화된 데이터에 적합하다. 웹 페이지, 웹 서버 로그, 이미지 등에 해당한다. 용량은 수십 MB에서 수백 GB 이상이다. Google의 Google File Sstem (GFS), Hadoop Distriburted File System (HDFS)등이 그 예시다.
파일은 1개 이상의 block에 걸쳐 저장된다. 1개 파일에 대한 block은 여러 머신에 걸쳐 이루어질 수 있다. 즉 파일 하나가 여러 머신의 block에 걸쳐 저장될 수 있다. 다시 각 block들은 여러 머신에 걸쳐 복제 (replicated)될 수 있다. fault-tolerance (내결함성)을 위함이다.
파일 시스템 (File System)의 기본 지원
- 디렉터리 시스템 : 파일을 계층형 디렉터리로 저장 가능
- block 식별자와 파일을 매핑
- 식별자로 파일을 저장, 탐색 (Distribured File System에서는 식별자를 machine + disk를 매핑)
DFS 예시 : Hadoop Distriburted File System (HDFS)
NameNode에 파일의 메타 데이터를 저장한다. Hadoop의 코어에 해당한다. 파일 시스템의 모든 요청은 NameNode로 전달된다. 파일별 Block 식별자 정보, block 복제 정보를 유지하고 있다. DataNode에 block을 저장한다.
파일을 읽을 때에는 먼저 block이 위치한 machine (Rack) 식별자를 가져온 다음 머신 안에서 파일이 위치한 block 식별자를 가져와 읽는다. 파일을 쓸때에는 block 식별자를 생성해서 각 머신에 할당한다.
Java, Python 등의 언어로 구현된 API를 지원하고 있다.
Sharding
대용량 파일을 수백만 이상 사용자들이 사용할 때에는 single application으로는 역부족이다. (e.g. SNS) 따라서 데이터를 쪼개서 (파티셔닝, partitioning) 여러 데이터베이스 (혹은 머신)에 걸쳐 저장하는 것을 Sharding (샤딩)이라 한다.
방법 1. partitioning attributes (partitioning key, shard keys)
데이터를 파티셔닝하는데 사용하는 속성 (attribute, key)를 사용하여 파티셔닝 하는 방법이다. 주로 1개 이상의 속성을 사용한다. 예를 들어 사용자 데이터를 사용자 식별 키를 partitioning key로 하여 데이터를 쪼갤 수 있다. 머신 A에 사용자 계정 정보, 머신 B에 사용자 연락처 등으로 쪼개어 저장하는 것이다.
방법 2. range partitioning
paritioning key 값의 범위에 따라 파티셔닝 하는 방법이다. 예를 들면 머신 A에는 사용자 식별 키가 1 ~ 100,000을 저장하고, 머신 B에는 100,001 ~ 200,000을 저장하는 방법이다.
방법 3. hash paritioning
paritioning key를 해시 (hash)처리 결과 값에 따라 파티셔닝 하는 방법이다. hash(1) % 10 = DB1, hash(2) % 10 = DB2,...
애플리케이션은 데이터가 저장된 머신을 알아야 한다
데이터베이스에 접근하는 애플리케이션은 데이터가 어떤 머신에 저장되어 있는지 알아야 쿼리를 처리할 수 있다. 따라서 애플리케이션은 partitioning key에 대한 정보를 가지고 있어야 한다. 또한 어떤 쿼리는 여러 머신에 걸쳐 쿼리 해야 할 수도 있다. range partitioning이 사용자 식별키 1만 개 단위로 되어있을 때 쿼리가 3만 개 이상을 읽기를 요구하는 쿼리가 들어온다면 여러 머신에 걸쳐 쿼리 해야 한다.
따라서 이는 샤딩의 명확한 한계이다. 애플리케이션이 파티셔닝 정보를 기반으로 쿼리를 라우팅 해야 하기 때문이다. 만일 특정 머신에 부하가 많아진다면 일정 데이터를 다른 머신으로 다시 분산 (파티셔닝)해야 할 수 있다. 이런 한계를 일부 (Parallel) Key-value Storage System으로 해결할 수 있다.
Key-Value Storage Systems (Key-Value Store, NoSQL)
많은 웹페이지들은 작은 용량 (KB ~ MB)의 레코드를 수십억 개 이상 저장한다. (세션 정보 등) 이렇게 작은 레코드마다 파일을 생성해 저장하는 것은 매우 비효율적이다. key-value storage system은 key를 사용해 레코드를 저장하고, 접근한다. 수천 개의 머신에 걸쳐 저장되어 클러스터를 구성하기 때문에 대용량 데이터를 저장할 수 있다. 레코드는 클러스터 내부 여러 머신에 파티셔닝 되어 저장된다. SQL을 지원하지 않기 때문에 NoSQL system이라고도 불린다.
표준 데이터베이스 시스템이 제공하는 기능 (SQL, 트랜잭션)을 지원하지 않는다. 데이터베이스 구조가 key-value이기 때문에 non-key 속성에 대한 쿼리 지원이 제한적이다. 예를 들면 key를 사용자 아이디로 지정한 key-value store에 사용자 나이가 20살 이상인 데이터를 찾는 쿼리는 제한적일 수밖에 없다.
- put(key, value) : key-value 쌍을 저장
- get(key) : key에 해당하는 데이터를 읽음
공통 동작은 위와 같지만, 주로 API로 구현되어 있다. Google Bigtable은 rage query를 지원하고, Document store는 여러 추가적인 쿼리를 지원한다.
Parallel (병렬) Key-Value store
데이터베이스가 직접 머신별 파티션 키 정보를 유지하기 때문에, 애플리케이션은 정보를 알 필요가 없다. 이 때문에 오늘날 샤딩보다 더 선호되는 데이터베이스이다. 복제 (replication), 로드밸런싱을 지원한다. Google Bigtable, Apache HBase, Amazon Dynamo, Facebook Cassandra, MongoDB 등이 있다.
Document store (e.g. MongoDB)
value를 단순히 byte 시퀀스로 간주한다. value를 간단한 스키마 (schema)로 정의하기 때문에 쿼리를 지원한다. 대표적인 Document store인 MongoDB의 경우 JSON 스키마로 value를 정의한다.
In-memory cashing system (e.g. Redis, Memcached)
메모리 기반의 데이터베이스이다. 메모리에 데이터를 저장하기 때문에 휘발성을 감수해야 한다. 그러나 메모리 저장장치 특성상 매우 빠른 읽기 속도를 지원할 수 있다. Redis의 경우 엔터프라이즈는 하드디스크까지의 확장을 지원한다.
예시 : MongoDB
show dbs // Shows available databases
use sampledb // Use database sampledb, creating it if it does not exist
db.createCollection("student") // Create a collection
db.createCollection("instructor")
show collections // Shows all collections in the database
db.student.insert({ "id" : "00128", "name" : "Zhang",
"dept name" : "Comp. Sci.", "tot cred" : 102, "advisors" : ["45565"] })
db.student.insert({ "id" : "12345", "name" : "Shankar",
"dept name" : "Comp. Sci.", "tot cred" : 32, "advisors" : ["45565"] })
db.student.insert({ "id" : "19991", "name" : "Brandt",
"dept name" : "History", "tot cred" : 80, "advisors" : [] })
db.instructor.insert({ "id" : "45565", "name" : "Katz",
"dept name" : "Comp. Sci.", "salary" : 75000,
"advisees" : ["00128","12345"] })
db.student.find() // Fetch all students in JSON format
db.student.findOne({"ID": "00128"}) // Find one matching student
db.student.remove({"dept name": "Comp. Sci."}) // Delete matching students
db.student.drop() // Drops the entire collection
- use : DB 선택 (없으면 생성)
- db.createCollection : collection 생성
- student, instructor collection 생성, 각 collection에 JSON 형태의 document 저장
- 자동으로 key 지정 후 id 속성에 저장, 인덱스 생성
- value에 대한 쿼리 지원 db.student.find({"dept name": "Comp. Sci."})
데이터에 대한 인덱스 생성을 지원한다. 2개 이상의 머신으로 구성된 클러스터링을 지원한다. 데이터는 여러 머신에 걸쳐 샤딩되어 저장되는데, 이때 paritioning key를 사용한다. 쿼리가 들어오면 MongoDB Router가 머신으로 요청을 보낸다. 복제 (replication)을 지원한다.
Parallel and Distributed Databases
N개의 머신으로 운용되는 데이터베이스이다. (클러스터링) 현대 빅 데이터 시스템의 기반이 된다. 프로그래머 입장에서는 single-machine으로 보인다. 내결함성 (falue-tolerance)를 위해 복제 (replication)를 지원한다.
Map-Reduce는 parallel query processing (병렬 쿼리 처리)를 구현하기 위한 기법으로 fail이 발생하면 fail이 발생한 머신에서 다시 실행된다.
복제 (Replication)와 일관성 (Consistency)
복제 (Replication)는 데이터를 여러 머신에 복제하여 저장하는 것을 말한다. 즉 하나의 데이터가 N개의 머신에 중복으로 저장된다. 이때 복제되어 저장되는 머신을 레플리카 (Replica)라 한다. 복제는 내결함성 (fault-tolerance) 유지의 핵심이다. 그러나 데이터가 수정되면 모든 머신 (Replica)에 수정사항이 반영되어야 한다. 일관성 (Consistency) 유지를 위해 반드시 모든 레플리카에 데이터를 저장해야 하고, 모든 레플리카의 데이터는 반드시 최신화가 유지되고 있어야 한다. 클라이언트는 언제든 데이터베이스에 접근, 쿼리를 실행할 수 있어야한다. (availability, 가용성)
이제부터 문제가 발생한다. 모든 레플리카에 대해 트랜잭션 처리를 해야 한다. 트랜잭션 내용을 모든 레플리카에 반영하여 데이터를 최신화해야 한다. 레플리카의 반 이상은 항상 살아있어야 내결함성을 유지할 수 있다.
트레이드오프 : 일관성 (consistency) vs 가용성 (availability)
일관성을 유지하기 위해 모든 레플리카는 항상 최신화된 동일한 데이터를 저장해야 한다. 따라서 데이터에 수정이 있을 때마다 레플리카들에게 트랜잭션이 전파될 수 있는 동기화 시간이 필요하다.
그러나 가용성을 위해 클라이언트는 언제나 데이터베이스에 접근, 쿼리를 실행할 수 있어야 한다. 가용성을 확보하기 위해 동기화 시간을 단축하려 하면 일관성이 부족하다. 즉 둘은 trade-off 관계에 있다.
해결방법 1 : Key-Value store와 RDB 조합하기
자주 read 하는 데이터는 Key-Value store에 저장하여 가용성을 확보한다. 사용자의 프로필, 계정 데이터 등은 언제든지 접근할 수 있도록 록 할 수 있다. 더 복잡한 데이터는 MongoDB와 같은 Document store에 저장한다. 복잡한 쿼리로 관리되어야 하는 데이터는 RDB에 저장한다. 그 안에서도 복제를 구성해 RDB 안에서 일관성을 확보한다.
해결방법 2. In-memory cashing system (e.g. Redis, Memcached)
RDB에 저장되는 데이터 중 read-only 데이터는 인-메모리 시스템에 캐싱하여 저장해 둔다. 애플리케이션에서 빠르게 접근할 수 있게 할 수 있다. 데이터 수정은 반드시 RDB에서 수정하고 수정사항을 인-메모리 시스템에 반영한다.
참고
https://product.kyobobook.co.kr/detail/S000001693775
https://redis.io/redis-for-dummies/
'Programming > Database System' 카테고리의 다른 글
[Database] MySQL ORDER BY 방식 비교와 성능 (0) | 2024.07.16 |
---|---|
[Database] 데이터 타입 비교 : char vs varchar (0) | 2024.01.17 |
[Database] Oracle Database Index (19c 기준) (1) | 2024.01.08 |
[Message Brokers] Streaming data와 Pub / Sub system (1) | 2023.11.25 |
[Database] 인덱스 (Index) 6장 : 쓰기에 최적화된 인덱스 (0) | 2023.11.08 |
댓글