본문 바로가기

엔지니어링(TA, AA, SA)/성능과 튜닝

[성능튜닝] 대규모 데이터 처리(메모리와 디스크, 웹 애플리케이션의 부하)

대규모 서비스의 데이터 규모



한 서비스의 데이터 규모는 다음과 같습니다. 한 테이블당 레코드 건수가 1,500만이나 5,000만 정도가 됩니다. 데이터 크기로는 entry라는 테이블이 3GB, bookmark라는 테이블이 5.5GB, tag 테이블이 4.8GB, 그리고 HTML 텍스트 데이터도 압축해서 저장하고 있는데, 이것이 200GB를 넘고 있습니다. 전체적으로 기가바이트 단위가 됩니다. Google이나 Yahoo!가 사용하고 있는 규모가 되면 더 나아가 테라바이트, 페타바이트가 되므로 이는 초대규모입니다. 


보통 웹 애플리케이션을 만들면서 좀처럼 기가바이트 단위의 DB는 나오지 않으므로 일반적인 웹 애플리케이션이라는 관점에서 보면 큰 규모인 것입니다. 


 레코드 수

데이터 크기 

  entry 테이블: 1,520만 엔트리

  엔트리: 3GB

  bookmark 테이블: 4,500만 북마크

  북마크: 5.5GB

  tag 테이블: 5,000만 태그

  태그: 4.8GB

  HTML: 200GB 이상




대규모 데이터로의 쿼리 대규모 데이터를 다루는 감각



위 표에서 본정도의 규모인 DB가 되면 쿼리를 던져 결과가 나올 때의 느낌도 달라집니다. use index(hoge)는 일부러 인덱스를 태우지 않고 쿼리를 던지고 있는 예인데, 1건 검색하는 데 200초가 경과해도 검색결과가 나오지 않습니다.


레코드 수가 수천만 건 단위, 데이터 크기는 수 GB부터 수백 GB. 이 정도의 데이터가 되면 아무 생각없이 던진 쿼리에 대해 응답하지 않습니다. 디버그 목적으로 데이터를 출력해보니 엄청난 부하가 걸렸다는 말도 실제 상황에서 발생할 수 있습니다.


데이터가 많으면 처리하는 데 시간이 걸리게 됩니다. 직감으로는 알아도 왜 그런지 이해해두는 것이 중요합니다.


매일 발생하는 미지의 문제: 시행착오를 겪으며 노하우 축적하기

개발자는 다양한 트러블을 접해봐야 노하우가 생기기 마련입니다. 트러블이 발생하지 않았다면 애초에 그런 문제가 시스템에 내재하고 있다는 것을 좀체 알 수 없었을 것이고, 데이터가 많아지면 실제로 어떤 노고가 있는지는 해보기 전까지는 몰랐을 것입니다.

'이렇게 되면 이렇게 해야 해!'라는 원리는 누군가가 알고 있는 것이 아니라 시행착오의 연속으로 노하우가 축적되어 지금에 이르렀다는 느낌입니다. 실제로 발생하는 문제는 미지의 문제도 많은 것이 현실입니다. 기본적인 사항은 파악해두고 '문제가 발생하면 그자리에서 생각하자'라는 단순 명쾌한 생각도 필요하기도 합니다.




대규모 데이터 처리의 어려운 점 메모리 내에서 계산할 수 없다



메모리 내에서 계산할 수 없다는 점이 어려운 점인 이유는 메모리에 올리지 않으면 기본적으로 디스크를 계속 읽어가면서 검색하게 되어 좀처럼 발견할 수 없는 상태가 되기 때문입니다. 데이터 건수가 많으면 그만큼 입력 데이터 건수가 늘어나므로 계산량이 많아진다는 점도 당연한 이유지만, 이 점보다도 문제가 되는 것은 '디스크를 읽고 있다'는 점입니다.

다시 말해 대규모 데이터의 어려운 점을 단적으로 말하면, '메모리 내에서 계산할 수 없다'는 것입니다. 메모리 내에서 계산할 수 있다면, 아무리 무식한 방법으로 하더라도 그런대로 계산은 빨라져서 200초나 기다리는 일은 없을 것입니다. 그러나 강의 4에서 본 예 정도의 규모가 되면 데이터가 너무 많아서 메모리 내에서 계산할 수 없으므로 디스크에 두고 특정 데이터를 검색하게 됩니다. 그리고 디스크는 메모리에 비해 상당히 느립니다.


대규모 데이터의 어려움은 메모리 내에서 계산할 수 없다는 점

- 메모리 내에서 계산할 수 없게 되면 디스크에 있는 데이터를 검색할 필요가 생깁니다.

- 하지만 디스크는 느리므로 I/O(Input/Output)에 시간이 걸립니다.

- 어떻게 대처할 것인가가 연구 대상이 됩니다.



메모리와 디스크의 속도차 메모리는 10^5~10^6배 이상 고속



메모리와 디스크의 속도차는 10만~100만배 입니다. 이런 수치 감각은 꽤 중요합니다. 메모리는 디스크보다 10^5 ~ 10^6 배 이상 빠릅니다.


디스크상의 탐색이 왜 느린가?

우선 메모리는 전기적인 부품이므로 물리적 구조와 탐색속도와 그다지 관계 없습니다. 실제로는 아래와 같은 저장방식을 취하고 있지는 않습니다. (1)부분에 1바이트 '5'라는 숫자가 들어있다고 하고 (2)에 '0'이라는 숫자가 들어 있을 때, (2)부분을 탐색하다가 (1)부분을 확인하고자 할 때에도 마이크로초 단위로 포인터를 이동시킬 수 있습니다.


디스크는 동축 상에 '원반'(disk)이 쌓여 있습니다. 이 원반이 회전하고 있고 여기서 데이터를 읽어냅니다. 즉, 메모리와는 달리 회전등의 물리적인 동작을 수반하고 있습니다. 이 물리적인 구조가 탐색 속도에 영향을 줍니다. 디스크 좌측에는 자기를 읽어들이는 '헤드'가 있습니다. 이 헤드가 달라붙는지 아닌지에 따라 자기를 읽어내 데이터를 읽어내는 구조로 되어 있습니다. 먼저 헤드를 (4)의 원반 상의 바깥쪽 위치에서 원반 안쪽, 즉 읽어야 할 데이터가 놓여있는 위치인 (3)으로 옮기는 작업이 필요합니다. 그렇지만 실제로는 마이크로초, 밀리초 세계의 얘기이므로 헤드의 움직임이 빠르다고 할 수 는 없습니다.

더구나 여기서 데이터를 읽어들일 때 (5)와 같이 회전하고 있다고 하면 원반상의 원반상의 (3)위치가 이미 헤드보다 조금 앞으로 가버려서 원하는 위치를 읽기위해 원반을 한 바퀴 더 돌려야합니다.



탐색속도에 영향을 주는 다양한 요인

디스크에서는 (4)헤드의 이동과 (5)원반의 회전이라는 두 가지 물리적인 이동이 필요하지만, 역시나 오늘날의 기술로도 원반의 회전속도를 빛의 속도까지 근접시킬 수는 없습니다. 디스크에서는 (4),(5) 각각 밀리초(10^-3초) 단위, 합해서 수 밀리초나 걸리게 됩니다. 메모리는 1회 탐색할 때 마이크로초면 되지만, 디스크는 수 밀리초가 걸리는 것입니다.


다음으로 (6)과 같이 데이터가 뿔뿔이 흩어져서 배치되어 있고 이분탐색 등 여기저기에서 찾아야 하는 알고리즘을 사용한다고 하면, 한 바퀴 회전해서 이쪽으로 이동하고 또 한 바퀴 회전해서 저쪽으로, 또다시 회전해서 이쪽으로와 같이 원반을 빙글빙글 돌려야 합니다. 또한 경우에 따라서는 헤드도 움직여야 합니다. 결과적으로 상당히 시간이 걸리게 됩니다. 그러나 데이터가 메모리상에 있다면 탐색할 때 물리적인 동작 없이 실제 데이터 탐색 시의 오버헤드가 거의 없으므로 빠른 것입니다.


탐색에 사용되는 것이 CPU의 캐시에 올리기 쉬운 알고리즘이나 데이터 구조라면 메모리 내용이 그림 2.3 [3]의 CPU 캐시에 올라가므로 더욱 빨라져 나노초(10^-9) 단위로 처리할 수 있습니다.



OS 레벨에서의 연구

디스크는 느리지만 OS는 이것을 어느 정도 커버하는 작용을 합니다. (7)과 같이 OS는 연속된 데이터를 같은 위치에 쌓게 됩니다. 그리고 나서 데이터를 읽을 때 1바이트씩 읽는 것이 아니라 4KB(kilobytes) 정도를 한꺼번에 읽도록 되어 있습니다.


이렇게 해서 비슷한 데이터를 비슷한 곳에 두어 1번의 디스크 회전으로 읽는 데이터 수를 많게 합니다. 그 결과로 디스크의 회전횟수를 최소화할 수 있게 됩니다. 이러한 작용을 해서 디스크를 가능한 한 회전시키지 않아도 되도록 하고 있습니다. 그렇지만 결국 회전 1회당 밀리초 단위이므로 역시 메모리와의 속도차를 피할 수 있는 것은 아닙니다.



전송속도, 버스의 속도차

탐색속도 측면에서 메모리가 디스크에 비해 10^5~10^6배 이상 빠릅니다. 전송속도 차이를 살펴보면, 메모리나 디스크 모두 CPU와 버스로 연결되어 있습니다. 이 버스의 속도에서도 상당한 차이가 있습니다. 먼저 '탐색'과 '전송'의 차이에 유의하기 바랍니다. 앞서 본 것은 메모리 혹은 디스크상에 있는 임의의 데이터를 탐색할 때의 속도차. 여기서 살펴보고자 하는 것은 전송속도 입니다. 찾은 데이터를 디스크에서 메모리로 보내거나 메모리에서 CPU로 보내는 등 컴퓨터 내부에서 전송하기 위한 속도입니다.


hdparm이라는 Linux 툴을 사용하면 그 속도차를 알 수 있습니다.


[root@dev-web /]# /sbin/hdparm -tT /dev/sda


/dev/sda:

 Timing cached reads:   14736 MB in  1.99 seconds = 7393.43 MB/sec

 Timing buffered disk reads: 2568 MB in  3.00 seconds = 855.79 MB/sec



[root@ndev /]# /sbin/hdparm -tT /dev/sda


/dev/sda:

 Timing cached reads:   26452 MB in  2.00 seconds = 13242.40 MB/sec

 Timing buffered disk reads: 1540 MB in  3.00 seconds = 512.91 MB/sec


'Timing cahed reads'는 메모리에 있는 캐시 데이터의 전송독도로 실질 메모리 전송속도를 말하며, 'Timing buffered disk reads'는 디스크의 전송속도입니다. 대략 10배 차이가 나고 경우에 따라서는 100배까지 차이나기도 합니다.


메모리와 CPU는 상당히 빠른 버스로 연결되어 있으므로 7.3GB/초 정도 나오지만, 디스크는 855MB/초 정도밖에 나오지 않습니다. 따라서 전송해오는 중에도 시간이 걸립니다. 데이터가 많아지면 많아질수록 디스크와 메모리의 차이도 나타나게 되므로 전송속도에서도 디스크는 늦어집니다.


SSD(Solid State Drive)는 물리적인 회전이 아니므로 Seek(탐색)은 빠르지만 버스 속도가 병목이 되거나 그 밖에 구조에 기인하는 면이 있어 메모리만큼의 속도는 나오지 않습니다.