1 minute read

데이터베이스는 두 가지 작업을 수행해야 한다.

  1. 사용자가 데이터를 제공하면 데이터를 저장하고,
  2. 사용자가 데이터를 다시 요청하면 데이터를 다시 반환한다.

사용 가능한 많은 스토리지 엔진 중에서 애플리케이션에 적합한 스토리지 엔진을 선택하라.
애플리케이션의 워크로드에 적합한 성능을 발휘하도록 스토리지 엔진을 조정하려면 스토리지 엔진이 내부에서 어떤 작업을 수행하는지 알아야 한다.

우리들에게 익숙한 데이터베이스인 전통적인 관계형 데이터 베이스와 NoSQL 데이터베이스에 사용되는 스토리지 엔진에 대해 살펴보자.
여기서는 로그 구조 스토리지 엔진과 B-tree와 같은 페이지 지향 스토리지 엔진이라는 두 가지 스토리지 엔진이 있다.

데이터베이스를 강화하는 데이터 구조

두 개의 Bash 함수로 가장 간단한 데이터베이스를 구현할 수 있다.

#!/bin/bash
db_set () {
  echo "$1,$2" >> database
}
db_get () {
  grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}

이 두 함수는 키-값 저장소를 구현한다.
db_set 키 값을 호출하면 데이터베이스에 키와 값을 저장할 수 있다.
그런 다음 특정 키와 연관된 가장 최근 값을 조회하여 반환하는 db_get key를 호출할 수 있습니다.

$ db_set 123456 '{"name":"London","attractions":["Big Ben","London Eye"]}'
$ db_set 42 '{"name":"San Francisco","attractions":["Golden Gate Bridge"]}'
db_get 42
{"name":"San Francisco","attractions":["Golden Gate Bridge"]}

이 데이터베이스의 기본 저장 형식은 매우 간단하다.
각 줄에 쉼표로 구분된 키-값 쌍이 포함된 텍스트 파일이다(대략 CSV 파일과 비슷하며 이스케이프 문제를 무시한다).
db_set을 호출할 때마다 파일 끝에 추가되므로 키를 여러 번 업데이트해도 이전 버전의 값을 덮어쓰지 않으므로 파일에서 키의 마지막 발생을 확인하여 최신 값을 찾아야 한다.

db_set 함수는 매우 간단한 작업에 비해 성능이 매우 우수하다.
db_set이 하는 동작 처럼, 많은 데이터베이스가 내부적으로 추가 전용 데이터 파일인 로그를 사용한다.
실제 데이터베이스에는 동시성 제어, 로그가 커지지 않도록 디스크 공간 회수, 오류 및 부분적으로 기록된 레코드 처리 등 처리해야 할 문제가 더 많지만 기본 원칙은 동일하다.

반면에 이 데이터베이스에 많은 수의 레코드가 있는 경우 db_get 함수의 성능은 좋지 않다.
키를 조회할 때마다 db_get은 전체 데이터베이스 파일을 처음부터 끝까지 스캔하여 해당 키가 있는지 찾아야 한다.
알고리즘 용어로 조회 비용은 O(n)이다.

데이터베이스에서 특정 키의 값을 효율적으로 찾으려면 다른 데이터 구조, 즉 인덱스가 필요하다.
인덱싱 구조는 데이터를 찾는 데 도움이 되는 이정표 역할을 한다.

인덱스는 기본 데이터에서 파생된 추가 구조다.
데이터베이스는 인덱스를 추가하고 제거할 수 있으며, 이는 데이터베이스의 콘텐츠에는 영향을 미치지 않고 쿼리 성능에만 영향을 미친다.
추가 구조를 유지 관리하면 특히 쓰기에서 오버헤드가 발생한다.
모든 종류의 인덱스는 데이터를 쓸 때마다 인덱스도 업데이트해야 하기 때문에 일반적으로 쓰기 속도가 느려진다.

이러한 이유로 데이터베이스는 모든 것을 인덱싱하지 않고 애플리케이션 개발자 또는 데이터베이스 관리자는 인덱스를 수동으로 선택해야 한다.
인덱스를 필요한 부분에서만 사용하면 오버헤드 없이 애플리케이션에 인덱스는 검색에 큰 이점이 된다.