이 책의 목적은 성능 좋은 SQL을 쓰는 방법, 특히 대량의 데이터를 처리하는 SQL의 성능을 향상시키는 방법을 이해하는 것입니다. RDB와 SQL은 '사용자가 직관적으로 처리할 수 있는 인터페이스'와, '대용량 데이터의 효율적 처리'라는 상반된 명제 사이에 있는 미들웨어입니다.
데이터가 쌓이는 속도와 데이터에 접근하는 속도가 달라지면서 데이터베이스에 많은 부하가 걸리기 시작했습니다. 2010년 전후로 그러한 빅데이터를 처리하기 위한 여러 방법이 있습니다. 예로, Nosql. 왜 2010년에는 SQL로 많은 양의 데이터를 처리할 수 없다 생각했을까요? 가장 큰 문제는 많은 개발자가 SQL을 제대로 사용하지 못하기 때문입니다.
1강 DBMS 아키텍처 개요
RDB 제품이 많은데, 전부 관계 모델이라는 수학적인 이론을 바탕으로 하기에 기본적인 구조는 모두 같습니다. DBMS로 전달된 SQL 구문은 다양한 처리를 수행합니다. 이에 대해 알아보죠.
1) 쿼리 평가 엔진
-> 입력받은 SQL 구문을 분석하고, 어떤 순서로 기억장치의 데이터에 접근할지 결정합니다. 이때 결정되는 계획을 '실행 계획'이라 하고, 이러한 실행 계획에 기반을 둬서 데이터에 접근하는 방법을 '접근 메서드'라고 부릅니다. 즉, 계획을 세우고 실행하는 모듈입니다.
2) 버퍼 매니저
-> DBMS는 버퍼라는 특별한 용도로 사용하는 메모리 영역을 확보해둡니다. 이 메모리 영역을 관리하는 것이 버퍼 매니저입니다.
3) 디스크 용량 매니저
-> 어디에 어떻게 데이터를 저장할지를 관리하며, 데이터의 읽고 쓰기를 제어합니다.
4) 트랜잭션 매니저와 락 매니저
-> 동시에 데이터베이스에 접근시 각각의 처리는 DBMS 내부에서 트랜잭션이라는 단위로 관리됩니다. 이러한 트랜잭션의 정합성을 유지하면서 실행시키고, 필요한 경우 데이터에 락을 걸어 다른 사람의 요청을 대기시키는 것이 트랜잭션 매니저와 락 매니저의 역할입니다.
5) 리커버리 매니저
-> DBMS가 저장하고 있는 데이터 중 절대 잃어버리면 안 되는 데이터가 있습니다. 하지만 시스템은 언제나 장애가 발생할 수 있고, 이를 대비해 정기적으로 데이터를 백업하고, 문제가 일어났을 때 복구해줘야 하는데 이를 수행하는 것이 리커버리 매니저입니다.
2강 DBMS와 버퍼
메모리는 한정된 희소 자원이고, 데이터를 버퍼에 어떠한 식으로 확보할 것인가 하는 부분에서 트레이드오프가 발생합니다.
2.1 공짜 밥은 존재할까?
일반적으로 기억장치는 기억 비용에 따라 1차부터 3차까지의 계층으로 분류합니다. 기억 비용이란 간단하게 '데이터를 저장하는 데 소모하는 비용'입니다. 많은 데이터를 영속적으로 저장하려 하면 속도를 잃고, 속도를 얻고자 하면 많은 데이터를 영속적으로 저장하기 힘들다는 트레이드오프가 발생합니다. 이러한 것이 저장소와 관련되어 나타나는 첫 번째 트레이드오프라는 것을 기억해주세요.
DBMS는 데이터 저장을 목적으로 하는 미들웨어입니다. 대표적으로 2가지가 있죠.
1) 하드디스크 (HDD) : 하드디스크는 기억장치 계층에서 2차 기억장치로 분류됩니다. 특출나게 장점과 단점이 나타나지 않고, 데이터베이스는 대부분의 시스템에서 범용적으로 사용되는 미들웨어이므로, 어떤 상황에서도 평균적인 수치를 가지는 매체를 선택하는 것이 자연스럽습니다. 일반적인 DBMS는 항상 디스크 이외의 장소에도 데이터를 올리는데 1차 계층의 기억장치 메모리에 말이죠.
2) 메모리 : 디스크에 비해 기억 비용이 비쌉니다. 테라바이트의 용량을 가지는 HDD와 비교하면 엄청 작은 크기이고, 규모 있는 상용 시스템의 데이터베이스 내부 데이터를 모두 메모리에 올리는 것은 불가능합니다.
3) 버퍼를 활용한 속도 향상 : DBMS가 일부라도 데이터를 메모리에 올리는 것은 성능 향상 때문입니다. SQL 구문의 실행 속도를 빠르게 만들기 위함이라는 것이지요. 메모리는 1차 기억장치이며, 자주 접근하는 데이터를 메모리 위에 올려둔다면, 디스크에서 데이터를 가져올 필요 없이 빠르게 검색할 수 있습니다.
디스크 접근을 줄일 수 있다면 성능 향상이 가능한데, 일반적인 SQL 구문의 실행 시간 대부분을 저장소 I/O에 사용하기 때문입니다. 이렇게 성능 향상을 목적으로 데이터를 저장하는 메모리를 버퍼 또는 캐시라고 부릅니다. 이러한 고속 접근이 가능한 버퍼에 '데이터를 어떻게, 어느 정도의 기간 동안 올릴지'를 관리하는 것이 DBMS의 버퍼 매니저입니다.
2.2 메모리 위에 있는 두 개의 버퍼
DBMS가 데이터 유지를 위해 사용하는 메모리는 크게 2가지가 있습니다.
1) 데이터 캐시 : 디스크에 있는 데이터의 일부를 메모리에 유지하기 위해 사용하는 메모리 영역입니다. 만약 Select 구문에서 선택하고 싶은 데이터가 운 좋게 모두 이러한 데이터 캐시에 있다면, 디스크와 같은 자속 저장소에 접근하지 않고 처리가 됩니다. 반대로 버퍼에서 찾을 수 없다면, SQL 구문의 응답 속도가 느려집니다.
2) 로그 버퍼 : 갱신 처리(INSERT, DELETE, UPDATE, MERGE)와 관련 있습니다. DBMS는 갱신과 관련된 SQL 구문을 사용자로부터 받으면, 곧바로 저장소에 있는 데이터를 변경하지 않습니다. 일단 로그 버퍼 위에 변경 정보를 보내고 이후 디스크에 변경을 수행하죠. DB의 갱신 처리는 SQL 구문의 실행 시점과 저장소에 갱신하는 시점에 차이가 있는 비동기 처리입니다.
이렇게 시점 차이를 두는 이유는 성능을 높이기 위함이죠. 저장소 변경이 끝날 때까지 사용자는 장기간 대기해야 하는데, 한 번 메모리에 갱신 정보를 받은 시점에서 사용자에게는 해당 SQL 구문이 '끝났다'라고 통지하고, 내부적으로 관련된 처리를 계속 수행하는 것입니다.
결국 DBMS는 '저장소의 느림을 어떻게 보완할 것인가'라는 것을 계속해서 고민해 온 미들웨어입니다.
2.4 메모리의 성질이 초래하는 트레이드오프
앞에서 '메모리가 가진 단점은 가격이 비싸서 보유할 수 있는 데이터양이 적은 것이다'라고 얘기했습니다. 더 알아보죠.
- 휘발성 : 메모리에는 데이터의 영속성이 없습니다. 하드웨어의 전원을 끄면 메모리 위에 올라가 있는 모든 데이터가 사라져 버리는데요. 이러한 성질을 휘발성이라고 하죠. DBMS를 끄면 버퍼 위의 모든 데이터가 사라집니다. 영속성이 없는 이상 기능적으로는 디스크를 완전히 대체하는 것은 불가능합니다.
휘발성의 가장 큰 문제는 장애가 발생했을 때 메모리에 있던 데이터가 모두 사라져버려 데이터 부정합을 발생시키는 것입니다.
1) 데이터 캐시라면 장애로 인해 메모리 위 데이터가 사라져도, 원본 데이터는 디스크 위에 남아있으므로 아무 문제없습니다. 그냥 디스크에서 데이터를 한번 더 읽으면 되니까요.
2) 하지만 로그 버퍼 위에 존재하는 데이터가 디스크 위의 로그 파일에 반영되기 전에 장애가 발생해서 사라져버린다면 어떻게 될까요? 이는 사용자가 수행했던 갱신 정보가 사라진다는 의미이고, 이러한 문제는 비즈니스적인 관점에서 심각한 문제입니다. 이를 회피하고자 DBMS는 커밋 시점에 반드시 갱신 정보를 로그 파일(이는 영속적인 저장소 위에 존재)에 씀으로써, 장애가 발생해도 정합성을 유지할 수 있게 합니다.
커밋이란 갱신 처리를 '확정'한다는 뜻입니다. DBMS는 커밋된 데이터를 영속화합니다.
반대로 말하면, 커밋 때는 반드시 디스크에 동기 접근이 일어납니다. 여기서 지연이 발생할 가능성이 있는데, 또다시 트레이드오프가 발생합니다. 디스크에 동기를 처리를 한다면 정합성은 높아지지만 성능이 낮아지죠.
2.5 시스템에 따른 트레이드오프
DBMS에서 공통으로 데이터 캐시에 비해 로그 버퍼의 초깃값이 굉장히 작습니다. 성능 검증을 하기 전에는 모르지만, 2개의 버퍼에 대해 비대칭적인 크기를 할당한 이유는 데이터베이스가 기본적으로 검색을 메인으로 처리한다고 가정하기 때문입니다. 따라서 갱신 처리에 값비싼 메모리를 많이 사용하는 것보다는, 자주 검색하는 데이터를 캐시에 올려놓는 것이 좋다고 생각하는 것이죠.
-> 실제로 물리 메모리에 여유가 있다면, 데이터 캐시를 되도록 많이 할당할 것을 추천하죠.
검색과 갱신 중에서 중요한 것?
여기서 어떤 것이 더 우선되어야 하는가라는 트레이드오프에 직면합니다. 로그 버퍼가 크게 잡혀있다면, 갱신 처리와 관련해 큰 부하가 걸릴 것을 고려한 설계임을 알 수 있고, 데이터 캐시가 크게 잡혀있다면 검색 처리와 관련된 처리가 중심이라는 것을 알 수 있습니다.
2.6 추가적인 메모리 영역 '워킹 메모리'
DBMS는 데이터 캐시, 로그 버퍼 이외에 메모리 영역을 하나 더 가지고 있습니다. 정렬(ORDER BY, 집합 연산, 윈도우 함수) 또는 해시(테이블 등의 결합에서 해시 결합이 사용되는 때 실행됩니다.) 관련 처리에 사용되는 작업용 영역으로 워킹 메모리(working memory)라고 부릅니다.
ORACLE -> PGA(Program Global Area)
PostgreSQL -> 워크 버퍼
MySQL -> 정렬버퍼
이 작업용 메모리 영역은 SQL에서 정렬 또는 해시가 필요한 때 사용되고, 종료되면 해제되는 임시 영역으로, 일반적으로 데이터 캐시와 로그 버퍼와는 다른 영역으로 관리되는 경우가 많습니다. 이 영역이 성능적으로 중요한 이유는, 만약 이 영역이 다루려는 데이터양보다 메모리가 작아 부족해지는 경우가 생기면 대부분의 DBMS가 저장소를 사용하기 때문입니다. 이는 OS 동작에서 swap과 같죠.
많은 DBMS는 워킹 메모리가 부족할 때 사용하는 임시적인 영역을 가집니다. 아래와 같은 이름으로 부르죠. 이러한 일시 영역들은 저장소 위에 있으므로 당연히 접근 속도가 느립니다.
ORACLE -> 임시 테이블 스페이스(TEMP Tablespace)
Microsoft SQL Server -> TEMPDB
PostgreSQL : 일시 영역(pgsql_tmp)
저장소는 메모리에 비해 굉장히 느리죠. 물론 메모리가 부족하다고 처리가 멈추거나 에러가 발생하는 것처럼 문제가 발생하는 것이 아니라, 메모리에서 작동하고 있을 때는 빠르게 움직이다가, 메모리가 부족해지는 순간 갑자기 느려지는 순간적인 변화가 일어나는 것이 문제입니다.
또한 이 영역은 여러 개의 SQL 구문들이 공유해서 사용하므로, 하나의 SQL 구문을 실행하고 있을 때는 메모리에 잘 들어가지만 여러 개의 SQL구문을 동시에 실행하면 메모리가 넘치는 경우가 있습니다. 하지만 데이터베이스는 메모리가 부족하다는 이유로 SQL 구문에 오류를 절대 발생시키지 않습니다.
3강 DBMS와 버퍼
사용자나 개발자가 데이터베이스에 의식적으로 사용하는 것은 SQL 레벨까지입니다. 이후 모든 것은 SQL 구문을 읽어들인 DBMS가 알아서 처리하고 결과를 제공해줍니다. 모든 것이 DBMS에게 맡겨지죠.
3.1 권한 이양의 죄악
RDB가 대담하게 권한 이양을 감행한 데는 정당한 이유가 있습니다. 바로 '그렇게 하는 것이 비즈니스 전체의 생산성을 향상시키기 때문'입니다. 물론 현재 상황을 놓고 보면 반은 맞고 반은 틀립니다. 맞다는 것은 RDB가 시스템 세계의 곳곳에 침투해 있는 것을 보면 알 수 있습니다. 틀렸다는 것은 여전히 우리가 RDB를 다루기 어려워한다는 점을 보면 알 수 있습니다. 또한 How를 의식하지 않고 사용하는 것 때문에 성능 문제로 고생하는 경우도 있죠.
3.2 데이터에 접근하는 방법은 어떻게 결정할까?
RDB에서 데이터 접근 절차를 결정하는 모듈은 쿼리 평가 엔진이라고 부릅니다. 쿼리 평가 엔진은 사용자로부터 입력받은 SQL 구문을 처음 읽어들이는 모듈이기도 합니다. 추가로 파서 또는 옵티마이저와 같은 여러 개의 서브 모듈로 구성됩니다.
[쿼리] -> 파서 -> [parse된 쿼리] -> 옵티마이저(플랜생성 -> 비용평가) -> 1. 카탈로그 매니저 , 옵티마이저의 실행계획 -> 2. 플랜 평가
1) 파서
파서의 역할은 [1] 구문 분석입니다. SQL 구문을 정형적인 형식으로 변환해주죠. 그렇게 해야 DBMS 내부에서 일어나는 후속 처리가 효율화됩니다. 사실 구문 분석이라는 것은 SQL에 한정되는 것이 아니라 일반 프로그래밍 언어의 컴파일 시점에서도 실행되는 것이랍니다.
2) 옵티마이저
서류 심사를 통과한 쿼리는 옵티마이저로 전송됩니다. 옵티마이저의 한국어 번역은 '최적화'입니다. 최적화의 대상은 데이터 접근법(실행 계획)입니다. DBMS 두뇌의 핵심입니다. 옵티마이저는 인덱스 유무, 데이터 분산 또는 편향 정도, DBMS 내부 매개변수 등 조건을 고려해서, [2] 선택 가능한 많은 실행 계획을 작성하고, [3] 이들의 비용을 연산하고, 가장 낮은 비용을 가진 실행 계획을 선택합니다.
접근법의 수가 많이 나온다면, 그 비용을 계산하고 비교해야 하는데요. RDB가 데이터 접근의 절차 지향 결정을 자동화하는 이유는 이런 귀찮은 일을 기계적으로 처리해 주기 위해서입니다.
3) 카탈로그 매니저
[4] 옵티마이저가 실행 계획을 세울 때 옵티마이저에 중요한 정보를 제공하는 것이 카탈로그 매니저입니다. 카탈로그란 DBMS 내부 정보를 모아놓은 테이블들로, 테이블 또는 인덱스의 통계 정보가 저장되어 있습니다. 간단하게 '통계 정보'라고도 부릅니다.
4) 플랜 평가(plan evalutaion)
[5] 옵티마이저가 SQL 구문에서 여러 개의 실행 계획을 세운 뒤 그것을 받아 최적의 실행 결과를 선택하는 것이 플랜 평가입니다. 실행 계획이라는 것은 곧바로 DBMS가 곧바로 실행할 수 있는 형태의 코드가 아닙니다. 인간이 읽기 쉽게 만들어진 문자 그대로의 '계획서'입니다. 성능이 좋지 않은 SQL 구문이 있을 때 실행 계획을 읽고, 수정 방안 등 고려할 수 있습니다. 하나의 실행 계획을 선택한 후 DBMS는 실행 계획에 절차적인 코드로 변환하고 데이터 접근을 수행합니다.
3.3 옵티마이저와 통계 정보
위는 DBMS가 쿼리를 읽어들여 실제로 데이터 접근을 수행할 때까지의 흐름입니다. 결국 데이터베이스 사용자로서는 옵티마이저를 잘 활용하는 것이 더 중요합니다. 특히 카탈로그 매니저가 관리하는 통계 정보에 대해서는 데이터베이스 엔지니어가 항상 신경 써야 합니다. 플랜 선택을 옵티마이저에게 맡기는 경우, 실제로 최적의 플렌이 선택되지 않는 경우가 꽤 많습니다.
옵티마이저가 실패하는 패턴이 몇 가지 있는데, 통계 정보가 부족한 경우가 대표적인 원인이죠. 아래와 같습니다.
1) 각 테이블의 레코드 수
2) 각 테이블의 필드 수와 필드의 크기
3) 필드의 카디널리티(값의 개수)
4) 필드값의 히스토그램(어떤 값이 얼마나 분포되어 있는가)
5) 필드 내부에 있는 NULL 수
이러한 정보를 활용함으로써 옵티마이저는 실행 계획을 만듭니다. 문제가 생기는 경우는 이러한 카탈로그 정보가 테이블 또는 인덱스의 실제와 일치하지 않을 때입니다. 테이블에 데이터 삽입/갱신/제거가 수행될 때 카탈로그 정보가 갱신되지 않는다면, 옵티마이저는 오래된 정보를 바탕으로 실행계획을 세우게 됩니다.
3.4 최적의 실행 계획이 작성되게 하려면
올바른 통계 정보가 모이는 것은 SQL 성능에 있어서 중요한 문제입니다. 수동으로 갱신하는 것뿐만 아니라, 데이터를 크게 갱신하는 배치 처리가 있을 때는 Job Net(각각 작업의 실행 순서를 나타냅니다. 업무적인 전후 관계 또는 병렬 실행 가능 여부를 고려해서 조합)을 조합하는 경우도 많고, Oracle처럼 기본 설정에서 정기적으로 통계 정보 갱신 작업이 수행되는 경우도 있으며, Microsoft SQL Server처럼 갱신 처리가 수행되는 시점에 자동으로 통계 정보를 갱신하는 DBMS도 있습니다.
통계 정보 갱신은 대상 테이블 또는 인덱스의 크기와 수에 따라서 몇십 분에서 몇 시간이 소요되기도 하는, 실행 비용이 높은 작업입니다. 하지만 DBMS가 최적의 플랜을 선택하려면 필요한 조건이므로 갱신 시점을 확실하게 검토해야 합니다. 옵션 지정에 따라 테이블 단위가 아니라 스키마 전체를 기준으로 구하거나, 샘플링 레이트를 지정하거나, 테이블에 부여된 인덱스의 통계 정보도 함께 구할 수 있는 등의 다양한 제어가 가능합니다.
4강 실행 계획이 SQL 구문의 성능을 결정
실행 계획이 만들어지면 DBMS는 그것을 바탕으로 데이터 접근을 수행합니다. 데이터양이 많은 테이블에 접근하거나 복잡한 SQL 구문을 실행하면 지연을 발생시키는 경우가 있습니다. 위의 이유 말고, 이미 최적의 경로가 설정되어 있는데도 느린 경우도 있습니다. 또한 통계 정보가 최신이라도 SQL 구문이 너무 복잡하면 옵티마이저가 최적의 접근 경로를 선택하지 못할 수 있죠.
4.1 실행 계획 확인 방법
SQL 구문의 지연이 발생했을 때 제일 먼저 실행 계획을 봐야 합니다. 모든 DBMS는 실행 계획을 조사하는 수단을 제공합니다. 3개의 기본적인 SQL 구문 실행 계획을 보죠.
1) 테이블 풀 스캔의 실행 계획(FUll Scan)
2) 인덱스 스캔의 실행 계획
3) 간단한 테이블 결합의 실행 계획
4.2 테이블 풀 스캔의 실행 계획
Oracle과 PostgreSQL의 실행 계획을 예시로 들겠습니다. 실행 계획의 출력 포맷이 같지 않지만, DBMS에 공통적으로 나타나는 부분이 있습니다. 아래와 같은 3가지입니다. (SELECT * FROM Shops;)
1) 조작 대상 객체
-> 테이블, 인덱스, 파티션, 시퀀스 같은 SQL 구문으로 조작할 수 있는 객체라면 무엇이든 올 수 있습니다.
2) 객체에 대한 조작의 종류
-> PostgreSQL은 맨 앞에 "Seq Scan", Oracle은 Operation 필드로 "TABLE ACCESS FULL" 나타납니다. 엄밀히 말해 2가지가 같은 것은 아닙니다. 테이블에서 데이터를 모두 읽는 방법으로, 반드시 Sequential 스캔을 선택할 이유는 없기 때문입니다. 따라서 PostgreSQL의 출력이 좀 더 물리적 차원에 가까운 출력이라고 볼 수 있습니다. 하지만 실질적으로 Oracle도 테이블 풀 스캔을 할 때는 내부적으로 시퀀셜 스캔을 수행하므로, 같다고 해도 상관없죠.
3) 조작 대상이 되는 레코드 수
-> 모두 Rows라는 항목에 출력됩니다. 결합 또는 집약이 포함되면 1개의 SQL 구문을 실행해도 여러 개의 조작이 수행됩니다. 각 조작에서 얼마만큼의 레코드가 처리되는지가 SQL 구문 전체의 실행 비용을 파악하는 데 중요한 지표가 됩니다. 옵티마이저는 어디까지나 동계라는 메타 정보를 믿기 때문에 실제 테이블을 제대로 보지 않습니다.
4.3 인덱스 스캔의 실행 계획
(SELECT * FROM Shops WHERE shop_id = '00050';) 예제로 보았을 때 이전과 마찬가지로 3개의 부문으로 나누어 살펴보죠.
1) 조작 대상이 되는 레코드 수
-> 두 가지 DBMS 모두 Rows가 1로 바뀌었습니다. WHERE 구에서 기본키가 '00050'인 점포를 지정했으므로 접근 대상은 반드시 하나의 레코드로 당연한 결과입니다.
2) 접근 대상 객체의 조작
객체와 조작은 어떻게 되었을까요? PostgreSQL에서 'Index Scan', Oracle에서는 'INDEX UNIQUE SCAN'이라는 조작이 나타나죠. 인덱스를 사용해 스캔을 수행한다는 것입니다.
Oracle에서는 'TABLE ACCESS FULL'이 'TABLE ACCESS BY INDEX ROWID'로 바뀌었고, 추가적으로 내역을 보면 레코드에 'INDEX UNIQUE SCAN', Name에 'PK_SHOPS'가 출력됩니다. 이 PK_SHOPS는 기본키의 인덱스 이름이죠. 일반적으로 레코드 수에서 선택되는 레코드 수가 적다면 테이블 풀 스캔보다 빠르게 접근을 수행합니다. 이는 풀 스캔이 모집합의 데이터양에 비례해서 처리 비용이 늘어나는 것에 반해, 인덱스를 사용할 때 활용되는 B-tree가 모집합의 데이터양에 따라 대수 함수적으로 처리 비용이 늘어나기 때문입니다.
간단히 말해, 인덱스의 처리 비용이 완만하게 증가한다는 뜻으로, 특정 데이터양을 손익분기점으로 인덱스 스캔이 플 스캔보다 효율적인 접근을 하게 된다는 뜻이죠.
4.4 간단한 테이블 결합의 실행 계획
결합을 수행하는 쿼리의 실행 계획을 살펴봅시다. SQL에서 지연이 일어나는 경우 대부분 결합과 관련된 것입니다. 결합을 사용하면 실행 계획이 복잡해지고, 옵티마이저도 최적의 실행 계획을 세우기 어렵습니다.
(SELECT shop_name FROM Shops S INNER JOIN Reservation R ON S.shop_id = R.shop_id;)
일반적으로 결합을 할 때 3가지 알고리즘을 사용합니다.
1) 가장 간단한 결합 알고리즘은 Nested Loops입니다. 한쪽 테이블을 읽으면서 레코드 하나마나다 결합 조건에 맞는 레코드를 다른 쪽 테이블에서 찾는 방식입니다.
2) Sort Merge는 결합 키로 레코드를 정렬하고, 순차적으로 두 개의 테이블을 결합하는 방법입니다. 결합 전에 전처리로 정렬을 수행해야 하는데, 이때 작업용 메모리로 워킹 메모리를 사용합니다.
3) Hash는 이름 그대로 결합 키값을 해시값으로 맵핑하는 방법입니다. 해시 테이블을 만들어야 하므로, 마찬가지로 워킹 메모리 영역을 필요로 합니다.
마치며
- 데이터베이스는 다양한 트레이드오프의 균형을 잡으려는 미들웨어
- 특히 성능적인 관점에서 데이터를 저속의 저장소와 고속의 메모리 중에 어디에 위치시킬지의 트레이드오프가 중요
- 데이터베이스는 갱신보다 검색과 관련된 것에 비중을 두도록 기본 설정 되어있지만, 실세 시스템에서도 그럴지 판단 필요
- 데이터베이스는 SQL을 실행 가능한 절차로 변환하고자 실행 계획을 만듦
- 사실 사용자가 실행 계획을 읽는다는 것은 데이터베이스의 이상을 어기는 일이지만, 필요한 일임.
연습문제
DBMS의 데이터 캐시는 용량이 한정되어 있는 메모리에서 효율적으로 데이터를 유지하고자 다양한 알고리즘을 사용한다. 어떤 알고리즘을 사용할지 생각해보세요.
-> answer : 불특정 다수의 사용자에게 요구되는 데이터의 캐시 히트율을 올리고 싶을 때, 어떤 데이터를 캐시하는 것이 좋을지를 기계적으로 풀기 위한 기본적인 알고리즘은 LRU입니다. '참조 빈도가 가장 적은 것을 캐시에서 버리는' 알고리즘이죠.
컬럼 - 다양한 캐시
캐시라는 구조는 데이터베이스 계층뿐만 아니라 다양한 계층에서 사용됩니다. 미들웨어 층(데이터베이스), OS 층(파일 시스템), 하드웨어 층(저장소). 애플리케이션에서 데이터베이스의 결과 집합을 캐시에 저장해서, 데이터베이스에 다시 SQL 구문을 발행하지 않아도 사용자에게 결과를 리턴해주는 고조도 일종의 캐시라고 할 수 있습니다. 이러한 방법은 최신 데이터를 필요로 하는 경우가 아니라면, 간단하게 애플리케이션 전체의 성능을 개선할 수 있는 방법입니다.
이러한 다양한 계층의 캐시는 데이터베이스에서의 캐시 역할과 일부 중복되죠. 하지만 다른 계층의 캐시를 할당하기보다는, 서버의 실제 메모리를 압박하지 않는 범위에서 데이터베이스의 데이터 캐시를 할당하는 편이 DB의 성능 향상을 더 기대할 수 있죠.
'독서 > SQL 레벨업' 카테고리의 다른 글
5장 반복문 (0) | 2024.11.24 |
---|---|
4장 집약과 자르기 (0) | 2024.11.23 |
3장 SQL의 조건 분기 (0) | 2024.11.22 |
2장 SQL 기초 (0) | 2024.11.21 |