컴퓨터의 CPU나 메모리와 같은 전기적 특성을 띈 장치의 성능은 짧은 시간동안 매우 빠르게 발전했지만, 디스크와 같은 기계식 장치는 그에 비해 발전 속도가 더디다. 데이터베이스 성능 튜닝을 위해서 디스크 I/O를 줄이는 것이 관건이기 때문에, 이와 관련된 기초지식에 대해 알아보자.
5.1.1 저장 매체
일반적으로 서버에 사용되는 저장 매체는 3가지로 나뉜다 :
- 내장 디스크(Internal Disk) : 개인용 PC의 본체 내 장착된 디스크와 같은 것, 컴퓨터의 내부 공간은 제한적이기에 장착할 수 있는 디스크의 개수가 적고 용량도 부족할 때가 많다.
- DAS (Direct Attached Storage) : 컴퓨터의 본체와 달리 디스크만 부착되어 있다. 컴퓨터 본체에 연결해서 사용할 수 있다. 여러 컴퓨터간 동시 공유는 불가능하다.
- NAS (Network Attached Storage) : TCP/IP를 통해 컴퓨터에 연결하고, 동시에 여러 컴퓨터에서 공유해서 사용할 수 있는 저장매체이다. 직접적인 연결방식보다는 속도가 매우 느리다.
- SAN (Storage Area Network) : DAS로는 구축할 수 없는 아주 대용량의 스토리지 공간을 제공하는 장치이다. 컴퓨터 본체와 광케이블로 연결되기 때문에 빠르고 안정적이지만 비싸다.
NAS는 TCP/IP로 데이터가 전송되기 때문에 데이터 읽고 쓰기가 빈번한 DB서버용으로 거의 사용되지 않는다.
내장 디스크 -> DAS -> SAN 순으로, 뒤로 갈 수록 더 고사양 고성능이며 구축 비용도 올라간다.
대부분의 저장 매체는 디스크 드라이브의 플래서를 회전시켜 읽고 쓰는 기계적인 방식을 사용한다.
5.1.2 디스크 드라이브와 솔리드 스테이트 드라이브
컴퓨터에서 CPU나 메모리와 같은 주요 장치는 대부분 전자식 장치이지만, 디스크 드라이브는 기계식 장치이다. 그래서 DB서버에서는 항상 디스크 장치가 병목 지점이 된다.
SSD는 기존 디스크 드라이브에서 저장용 플래서인 원판을 제거하고, 플래시 메모리를 장착했다. 그래서 원판을 기계적으로 회전시킬 필요가 없어 훨씬 빠르게 데이터를 읽고 쓸 수 있는 것이다. 이러한 SSD의 처리시간은 디스크에 비해 1000배 이상 차이가 난다.
순차 I/O(디스크의 헤더를 움직이지 않고 한 번에 많은 데이터를 읽는 방식)에서는 SSD가 디스크 드라이브보다 조금 빠르거나 거의 비슷한 성능을 보인다. 하지만, 랜덤 I/O는 SSD가 훨씬 빠르다.(원판에서 물리적으로 이동하는 횟수가 많아서 그런게 아닐까?)
5.1.3 랜덤 I/O와 순차 I/O
랜덤 I/O는 디스크 드라이버의 플래터(원판)을 돌려서 읽어야 할 데이터가 저장된 위치로 디스크 헤더를 이동시킨 후, 다음 데이터를 읽는 것을 의미한다. 그렇다면 왜 성능상 차이를 보이는걸까? 아래 예시를 들 수 있을 것 같다!
- 순차 I/O는 3개의 페이지(16 * 3KB)를 디스크에 기록하기 위해 1번 시스템 콜을 요청한다. 하지만 랜덤 I/O는 3번 시스템 콜을 요청한다.
- 즉, 디스크에 기록해야 할 위치를 찾기 위해 순차I/O는 디스크 헤드를 1번 움직였고(순서대로니까!), 랜덤 I/O는 디스크 헤드를 3번 움직인 것이다. 디스크에 데이터를 읽고 쓰는 시간은 결국 디스크 헤더를 움직여 읽고 쓸 위치로 옮기는 단계에서 결정된다.
- 결과적으로, 디스크의 성능은 디스크 헤더의 위치 이동 없이 얼마나 많은 데이터를 한 번에 기록하느냐에 의해 결정된다고 볼 수 있다.
- 쿼리를 튜닝한다고 해서 랜덤 I/O를 순차 I/O로 변경할 방법은 많지 않다. 그렇기 때문에 가급적 랜덤 I/O를 줄여주는 게 쿼리 튜닝에 도움이 된다고 볼 수 있다.
- 참고 : 인덱스 레인지 스캔은 랜덤 I/O를 사용하고, 테이블 풀 스캔은 순차 I/O를 사용한다.
5.2 인덱스란?
- 인덱스는 책의 끝에 있는 색인과 비슷하다. 책의 내용은 실제 데이터 파일로 비유할 수 있다. 책의 색인을 통해 알아낼 수 있는 페이지 번호는, 데이터 파일에 저장된 레코드 주소라고 생각할 수 있다.[이 책에서는 키와 인덱스를 혼용해서 사용한다]
- 인덱스는 데이터의 INSERT, DELETE, UPDATE 성능을 포기하고 SELECT 속도를 향상시키기 위해 주로 사용된다.
- 그렇기 때문에 테이블의 인덱스를 추가할지 말지 여부는 데이터의 저장 속도를 어느정도 희생할 수 있는지, 읽기 속도를 얼마나 더 빠르게 해야하는지 여부에 따라 결정해야 한다.
인덱스는 데이터를 관리하는 방식과 중복 값 허용 여부에 따라 여러가지로 나뉠 수 있다. 역할로 구분해본다면 PK와 Secondary Key로 나눌 수 있다.
- PK(프라이머리 키) : 그 레코드를 대표하는 컬럼의 값으로 만들어진 인덱스를 말한다. 보통 이 컬럼은 테이블에서 해당 레코드를 식별할 수 있는 기준 값이 된다. 추가로 PK는 중복 불가, Not Null 하다.
- Secondary Key(보조 인덱스) : PK를 제외한 나머지 인덱스를 말한다.
인덱스도 여러 알고리즘에 의해 저장될 수 있지만, 크게 아래 알고리즘을 통해 저장되며 관리되고 있다 :
- B-Tree 인덱스 : 가장 일반적으로 사용되는 인덱스 알고리즘이다. 컬럼의 값을 변형하지 않고, 원래의 값을 이용해 인덱싱하는 알고리즘이다.
- Hash 인덱스 : 컬럼의 값으로 해시 값을 계산해 인덱싱하는 알고리즘이다. 매우 빠른 검색을 지원하지만 값을 변형해 인덱싱하기 때문에 prefix 일치와 같이 일부만 검색하고자 할 때는 해시 인덱스를 사용하기 적합하지 않다. 주로 메모리 기반 DB에서 많이 사용된다.
- Fractal-Tree 알고리즘 : B-Tree의 단점을 보완하기 위해 고안된 알고리즘이다. 값을 변형하지 않고 원래의 값을 인덱싱하여 사용가능하지만(B-Tree와 동일), 데이터가 저장되거나 삭제될 때 처리 비용을 상당히 줄일 수 있게 설계되었다.