본문 바로가기

DB/Oracle

[Oracle] Chapter 1. I/O와 디스크의 관계

1.1 오라클을 이해하기 위한 필수 키워드

 

1. 병렬 처리를 가능케 하고 높은 처리량을 실현한다.

2. 응답 시간(response time)을 중시 한다.

3. 커밋(COMMIT)한 데이터는 지킨다.

 

오라클을 포함한 모든 DBMS(DataBase Management System, 데이터베이스 관리 시스템)의 내부 구조는 매우 복잡하다. DBMS가 복잡해지는 이유는 위에서 이야기한 세 가지 특성을 모두 충족시켜야 하기 떄문이다. 이 세 가지 특성은 상반된 성향이 있어서 동시에 모두 만족시키가 매우 어렵다. 예를 들어, '커밋(COMMIT)한 데이터는 지킨다'를 만족하기 위해 커밋하는 순간 데이터를 디스크에 기록하고 싶지만, 그렇게 하면 응답 시간이 나빠지게 된다.

'병렬 처리를 가능케 하고 높은 처리량을 실현'하는 것도 간단하지 않다. 병렬 처리할 때는 모순되는 처리가 발생하지 않도록 Lock이 필요하며, 그로 인해 성능이 저하되는 경우도 발생한다.

 

1.2 오라클과 디스크(하드디스크)

 

오라클은 DBMS이며, 오라클의 관점에서 데이터베이스는 오라클이 디스크에 저장하고 관리하는 데이터를 의미한다. 오라클은 디스크에서 데이터를 읽어오고, 필요한 처리를 한 후 다시 디스크에 기록한다. 즉, 오라클이 다루는 데이터는 디스크에서 꺼내오고 다시 디스크로 돌아간다. 그래서 오라클과 디스크는 뗴려야 뗼 수 없는 관계이다.

 

디스크는 거의 항상 회전하고 있으며, 그 위로 헤드가 움직여서 데이터를 읽거나 기록한다. 레코드판과 다른 것은 원반이 여러 개 겹쳐 있다는 것과 액추에이터가 양면을 사용한다는 것이다. 또 하나의 차이점으로는 속도이다. 음악에 비유하자면, 트랙의 첫머리를 찾는 작업을 1초에 100번 할 수 있을 정도로 헤드가 빠르게 움직인다. 그리고 1분에 1만 번 정도의 엄청난 속도로 회전한다.

 

1.3 디스크의 동작

 

데이터를 읽기(또는 기록하기) 위해서는 원하는 데이터가 저장되기 시작한 첫머리를 반드시 찾아야 한다. 이렇게 원하는 위치를 찾는 작업을 디스크 용어로 '탐색'(seek, 시크)'이라고 부른다. 그 후에 디스크에서 원하는 정보를 읽을 수 있는 위치가 회전해서 다가올 때까지 기다리다가 해당 위치가 다가오면 데이터를 읽고 쓴다. 이렇게 원하는 위치가 다가올 때까지 기다리는 시간을 '회전 대기 시간(rotational latency time)'이라고 부른다.

 

메모리에 접근할 떄는 나노초(ns) 단위로 수행할 수 있으나, 디스크에 접근할 때는 밀리초(ms) 단위의 시간이 필요하다. 사람의 입장에서 보면 디스크에 접근하는 밀리초라는 단위도 매우 빠른 것처럼 보이겠지만, 컴퓨터의 입장에서는 매우 느린 속도이다. 이유를 간단하게 설명하자면 메모리는 전기 신호로 작업을 처리하지만, 디스크는 기계 동작이 필요하기 때문이다. DBMS의 입장에서 보면 디스크I/O는 반드시 필요한 것이면서도 처리 시간을 단축하기 위해서는 가능한 줄여야만 하는 부분이기도 하다. 

 

1.3.1 어떻게 I/O의 대기 시간을 줄일까?

 

시퀀셜(sequential)은 '순서를 따라서'라는 의미의 '순차'를 뜻하며, 시작점에서부터 마지막까지 중간 부분을 빠트리지 않고 전부 액세스(읽기/쓰기)하는 것을 의마한다. 메모리에 테이블의 데이터가 없으면 풀 스캔(Full Scan, 테이블의 모든 데이터를 읽어오는 것)할 때 시퀀셜 액세스가 발생한다.

 

- 읽으려는(쓰려는) 데이터가 원 둘레에 집중적으로 배치됨

- 헤드를 움직이지 않고 데이터를 읽고 쓰는 것이 가능하므로 데이터양의 관점에서는 I/O를 최대의 효율로 수행할 수 있음

- 음악의 첫머리를 찾지않고 연속해서 듣는 것과 같은 느낌이다. 단, 현실의 데이터는 이렇게 읽어오기 좋은 형태로 디스크에 배치되지 않는다. '탐색하는 시간이 필요하며, 읽고 쓰기를 어느 정도 연속으로 할 수 있다' 정도로만 생각하자.

 

탐색 시간과 회전 대기 시간을 합쳐 10밀리초가 걸린다고 가정하자. 또한, 디스크의 전송 속도(헤드를 움직이지 않고 계속 데이터를 읽고 쓸 수 있을 때의 처리량)은 20MB/초라고 가정하자. 테이블의 크기를 1GB라고 가정하고 모든 데이터를 시퀀셜 액세스로 가져온다면 50초(크기/전송 속도 = 1,000MB/20MB/초)가 걸린다. 테이블의 크기가 100MB라고 하면 5초가 걸린다. 데이터를 가져오는 시간이 이렇게 오래 걸리면 수행하는 대부분의 SQL 성능이 만족스럽지 못한다. 그래서 인덱스(index, 색인)라는 발상이 나왔다. 책에서 무엇인가를 조사하고 싶을 때 책의 페이지를 처음부터 끝까지 읽어 나가면서 찾는 분은 거의 없을 테고, 대부분은 색인(목차)를 이용할 것이다. 색인에는 키워드가 순서대로 나열되어 있으며, 해당 페이지 번호도 함께 기재되어 있다. 그 페이지 번호를 이용해서 원하는 페이지를 빠르게 찾은 후 필요한 정보를 얻을 수 있다.

 

데이터베이스의 인덱스도 마찬가지이다. 데이터베이스의 인덱스에는 검색할 때 사용하는 키 값(SQL문의 WHERE절에 적는 조건의 값을 말함)과 그 키가 존재하고 있는 위치가 기록되어 있다.

- 여기에서 말하는 주소는 데이터가 들어가 있는 위치를 의미한다(정확히는 ROWID라고 부른다). 이 주소를 알고 있으면 오라클에서 디스크에 읽고 쓰는 위치를 구체적으로 지시할 수 있다.

 

1.3.2 인덱스의 사용 예

 

예를 들어, 래리에 관한 내용을 책에서 찾아보고 싶을 때는 인덱스에서 해당 내용이 240페이지에 쓰여 있다는 것을 확인후 해당 페이지를 펼쳐서 래리의 정보를 확인할 수 있다. SQL문도 마찬가지므로, WHERE절에 '래리'의 정보를 찾고 싶다는 것을 작성하고 SELECT절 뒤에 알고 싶은 항목(예를 들면 '소속 회사')을 적는다.

SELECT "소속 회사" FROM "개인 데이터" WHERE "이름" = '래리';

 인덱스를 사용해서 이 SQL문을 처리할 때는 우선 '이름'이 기록되어 있는 인덱스를 조사한다. 그 결과로 주소(ROWID)를 알아낼 수 있고, 그 주소를 토대로 데이터를 읽어온다. 읽어온 데이터 안에는 래리의 데이터가 전부 모여 있으므로 그 안에서 '소속 회사'의 데이터를 사용자에게 반환한다.

- 오라클은 데이터의 캐시를 메모리에 가지고 있다. 따라서 운이 좋다면 디스크에 가서 데이터를 읽지 않고도 작업을 끝낼 수 있다. 데이터를 읽어올 때는 '블록'이라고 불리는 단위로 I/O를 수행한다. '블록'은 '일정한 크기의 데이터 모음'이라고 생각하자.

 

만약 인덱스 자체의 크기가 커지면 크기가 큰 테이블을 조회하는 것과 마찬가지로 작업에 필요한 시간이 늘어날까? 정답은 "그렇지 않다" 이다. 왜냐하면, 오라클의 인덱스는 '인덱스에 인덱스를 추가하는 것'처럼 여러 계층으로 구성되기 떄문이다(트리구조) 이런 부분이 책에서 사용하는 색인과 다르다. 트리 구조의 장점은 인덱스의 푤어 없는 부분을 읽지 않고 검색(작업)을 끝낼 수 있다는 것이다.

 

1.3.3 랜덤 액세스

 

다시 I/O 이야기로 돌아오면, 인덱스를 사용할 때는 필요한 부분만 읽어오면 충분하지만, 필요한 부분이 디스크 위에 연속적으로 존재하는 경우는 거의 없다. 따라서 헤드를 움직여가면서 띄엄띄엄 접근하게 된다. 이렇게 접근하는 방식을 '랜덤 엑세스(Random Access)'라고 하며, 시퀀셜 액세스와 반대의 의미가 있다. 디스크의 관점에서 생각해보면 랜덤 액세스는 비효율적인 부분이 존재한다. 탐색 하는 작업과 회전 대기로 인해 데이터에 띄엄띄엄 접근할 떄마다 어쩔 수 없이 시간이 소모되기 떄문이다. 가령 오라클의 블록 크기를 8KB, 1초에 탐색할 수 있는 횟수를 100회라고 가정한다면, 겨우 800KB밖에 읽어올 수 있다. 이것은 디스크의 전송 속도(하드를 움직이지 않고 읽고 쓸 수 있을 때나 가능한 처리량)를 20MB라는 크기에 비하면 25분의 1에 불과하다. 이것을 음악 청취에 비유한다면, 첫머리를 찾아가는 것을 빈버하게 반복하다 보니 한 시간 중 3분 정도밖에 음악을 듣지 못한 것이다. 남은 57분은 첫머리를 찾아다니느라 낭비한 셈이다.

 

- 디스크의 수가 적으면 디스크 자체가 병목이 됨

- 트랙의 첫머리를 최대 초당 200회 정도로 찾을 수 있는 CD플레이어를 여러 명이 함께 사용하는 상황에서 서로 자신이 원하는 트랙을 찾아 듣기 위해 1초에 200번 넘게 트랙의 첫머리를 찾으려하는 것과 같다. 기능 자체에 무리가 있으므로 프로세스는 기달릴 수 밖에 없다.

 

실제로 데이터 전송의 효율 측면에서 바라보면 DBMS의 I/O도 마찬가지이다. 탐색을 반복하기 떄문에 DBMS(특히 OLTP 시스템)에서 사용하는 디스크는 IOPS(I/O PerSec, 초당 수행 가능한 I/O의 횟수) 지표가 중요하다. 그리고 대부분의 디스크는 IOPS가 100회나 200회 정도이기 떄문에 한 개나 두 개의 디스크만을 사용해서 데이터베이스를 구성하면 부하가 집중적으로 발생했을 때 요청이 오는 것을 탐색이 따라잡을 수 없으므로 디스크에 병목 현상이 발생한다.

 

- OLTP(OnLine Transcation Processing) : 철도의 좌석 예약 시스템이나 은행의 입출금 처리라고 생각하면 된다. 많은 단말에서 온라인(네트워크를 통해서)으로 크지 않은 데이터를 읽거나 쓰는 작업을 하고 결과를 즉시 회신해야 하는 시스템 형태를 말한다. 데이터가 크지 않다는 것과 '즉시'라는 부분에서 알 수 있듯이, 인덱스를 사용해야 하는 시스템 형태이다.

 

TIP

'인덱스를 사용하는 것이 유리한 경우는 어쨰서 필요한 데이터가 전체 데이터의 15%미만' 이라고 하는 걸까?

그 이유는 시퀀셜 액세스와 랜덤 액세스의 특성에 있다. 테이블의 데이터가 대량이고 그중 한 개의 로우(행)를 꺼내야 한다면 당연히 인덱스를 통한 액세스가 빠르다. 그에 비해 모든 데이터를 보려고 할 떄 매번 인덱스를 찾은 후 데이터를 찾아가면 오히려 속도가 느려진다(책 목차를 일일히 찾아가면서 책의 내용 전체를 읽는 독자는 없을 것이다).

만약 데이터가 50%라면 어떨까? 여기에서 중요한 점은 '디스크에서의 랜덤 액세스는 데이터를 읽어오는 효율성이 시퀀셜 액세스보다 떨어진다'라는 특성이다. 예를 들어, 테이블에 2만 건인 데이터가 저장되어 있다고 하고 그중 절반인 1만 건을 꺼낸다고 가정해보자. 여기서 한 로우는 8KB이다. 이떄 지금까지 사용해 왔던 디스크의 성능을 기준으로 계산해보면, 랜덤 액세스로 약 100초가 걸린다(인덱스는 빈번하게 사용되고 있으므로 캐시(Cache)에 보관되어 있다고 가정한다). 그것과 비교하면 2만 건 전부를 읽어오는 시퀀셜 액세스로는 모든 데이터(2만 건)를 디스크에서 읽어온다고 해도 약 8초면 끝난다. 즉 디스크 특성상 모든 데이터가 아니더라도 일정 크기 이상의 데이터를 읽는다면 시퀀셜 액세스를 사용하여 테이블을 풀 스캔하는 편이 빠르다. 단, 실제로는 캐시에 데이터가 보관된 경우도 있으며, 한 개의 블록에 여러 로우의 데이터가 보관되어 있어서 1회의 I/O로 많은 데이터를 읽을 수 있는 경우도 있다. 더욱이 인덱스의 데이터를 디스크에서 읽어 와야 하는 경우도 있으므로 '15%가 임계치'라고 단순하게 말할 수는 없다. 어디까지나 대략적인 기준 정도이다.

 

1.4 데이터를 보증하기 위한 디스크

 

데이터는 오라클의 프로세스가 비정상적으로 종료되었어도 무사해야 한다. 이 점이 DBMS와 다른 프로그램과의 차이점 중 하나이다. 예를 들어, 엑셀(Excel)에서는 저장한 시점 이후의 데이터는 없어진다. 이미 알고있는 바와 같이 데이터는 프로그램이 비정상적으로 종료되거나 전원 버튼을 눌러서 전원이 끊어져도 사라진다. 그것과 비교하면 DBMS는 어떤 장애가 발생해도 견뎌내야만 한다. 데이터는 CPU나 메모리가 고장이 나거나 정전이 발생했다 하더라도 손실되어서는 안 된다. 또한, 컴퓨터의 주기억장치 내의 정보는 전기이므로 정전이 발생하면 메모리에서 사라진다. 이런 가혹한 조건 속에 키워드에서 소개한 '커밋(COMMIT)한 데이터는 지킨다'라는 특성을 어떻게 구현하고 있는 걸까? 답은 간단하다. 오라클은 데이터를 변경한 후에 COMMIT라고 입력하면 데이터를 디스크에 기록한다. '그러면 느리지 않을까?'라고 생각할 수도 있지만, 다행히 속도를 빠르게 하기 위한 장치가 마련되어 있다. 오라클을 필두로 하는 DBMS들은 성능 향상을 위한 이런 장치들이 존재해서 그 구조가 복잡하다고 볼 수 있다.

- 커밋을 수신하면 데이터를 디스크에 기록한다(정확히는 로그를 기록함).

 

칼럼

'시퀀셜'이란 어떤 의미인가?

 

오라클은 여러 정보를 대량으로 보관하고 있다. 그중에는 I/O를 얼마만큼 수행했는지에 대한 정보도 기록되어 있지만, 내용을 상세하게 살펴보면 헷갈리기 쉬운 부분들이 있다. 예를 들어, 시퀀셜 액세스는 'db file scattered read'라고 표시되며, 랜덤 액세스는 'db file sequential read'라고 표시된다. 'scattered'는 '분산됐다'라는 의미를 가지고 있으며, 'sequential'은 '순차적인, 연속적인'이라는 의미를 가지고 있다. 액세스하는 방식을 생각해보면 반대로 되어 있는 것이 아닌가라는 생각도 들지만, 표시가 잘못되어 있는 것은 아니며 다음과 같은 의미가 있다.

오라클은 블록단위로 데이터를 읽고 쓰며, 메모리에 배치해둔다. 시퀀셜 액세스는 '순차로(순서대로) 읽는다'라는 의미로, 여러 블록을 한꺼번에 빠짐없이 읽어온다. 이때 읽어온 여러 블록은 메모리상에 연속되지 않은(분산된) 형태로 놓인다. 따라서 'scattered'라고 표현한다. 그에 반해 랜덤 액세스에서 읽어오는 데이터 블록은 한 번에 한 개이며, 메모리 공간에 반드시 연속적으로 놓인다. 따라서 'sequential'이라고 표현한다. 한번 읽어온 블록을 메모리에 어떻게 배치하는지에 따라 표현하는 방법을 다르게 한 것이다.

 

1.5 요약

 

- 디스크가 회전하고 있는 이미지

- 헤드가 움직이는 것과 회전해 오기를 기다리고 있어서 I/O에 시간이 걸리는 이미지

- 인덱스로 인해 해당 데이터에 빠르게 접근할 수 있다(테이블을 맨 앞에서부터 끝까지 풀 스캔하지 않아도 된다)는 이미지

 

이번 장에서는 디스크가 느리다는 것, 그리고 인덱스로 인해 높은 효율로 데이터에 접근할 수 있다는 것을 알게 되었다.

 

현장에서 사용하는 용어

- 24365, 24/7 : 24시간 365일 가동되는 시스템을 의미한다. 고가용성과 보안을 유지하기 위한 대책이 중요하다.

- 온-프레미스(On-premise) : 장비들을 직접 보유하고 IDC 등에서 운영하는 형태를 의미한다. 최근에는 인터넷에서 가상 서버를 두고 사용하는 '클라우드' 환경도 유행하고 있는데, 이를 구분하기 위한 용어라고 생각하자.

 

칼럼

저장 장치(스토리지)의 트렌드

 

데이터를 보관해두는 저장 장치로는 HDD(Hard Disk Drive)가 가장 오래 사용되어 왔다. 최근에는 이 HDD를 대신해서 빠르게 보급되고 있는 것이 SSD(Solid State Drive)이다. SSD는 플래시 메모리를 사용한 저장 장치이다. HDD의 자기 디스크와는 다르게 주소로 표시되는 저장 공간에 데이터를 보존한다. 따라서 디스크의 회전 등 물리적인 동작을 필요로 하지 않고, 빠르게 데이터를 보존하고 꺼내올 수가 있다. 또한, 디스크에서 필요한 회전 등의 기계적인 구조 역시 필요 없으므로 충격에도 강하다. 그렇지만 SSD는 용량 대비 가격이 고가이다. 최근에는 용량이 많이 필요한 시스템에서는 HDD를 사용하고, 성능을 중시하는 시스템에서는 SSD를 선택하는 경우가 많아지고 있다.