데이터베이스 이용시 주의 사항

서버 개발시에 DBMS를 사용하지 않는 것은 불가능에 가깝습니다. 

특히나 트랜잭션을 C/C++ 레벨에서 구현하기란 보통 어려운게 아닙니다. DBMS에서는 시스템 단에서 지원해주기 때문에 손쉽게 트랜잭션을 사용 할 수 있죠. 데이터를 저장하고 불러 오는 것 역시 파일 시스템보다 DBMS가 훨씬 더 효율이 좋죠.

물론 그것도 잘 만들고, 잘 사용하고, 잘 관리했을때 이야기지만 말이죠.

다음은 게임 서버에서 DBMS를 이용할 때의 유의 사항입니다.

  1. 반드시 측정하라.
    • 쿼리 프로파일러 등을 통해서 DB 부하를 측정하라. 
    • 원하는 목표치를 수립하고, 그 목표치에 달성 할 수 있게끔 노력하라.
    • 목표치가 너무 높거나 낮을 수도 있지만, 목표치가 없이 무작정 빠르게보다 동기부여도 되고, 성취감도 생기기 때문에 반드시 목표치를 두자.
  2. 미리 테스트하라.
    • 반드시 테스트 하라. 그리고 미리 테스트하라.
    • 개발 과정에서 데이터를 임의로 생성하고, 측정하라.
    • 테스트할 데이터가, 실제 유저들이 쌓을 데이터와 비슷하다면 금상 첨화다.
    • 하지만 그렇지 않다하더라도 선행 테스트는 반드시 필요하다.
  3. 쿼리 호출 횟수를 줄이는 것도 좋은 튜닝 방법이다.
    • 물론 쿼리 자체가 느리다면, 쿼리 호출 횟수가 작아도 문제가 생길 수 있다.
    • 하지만, 쿼리가 아무리 빠르더라도 쿼리 호출 횟수 자체가 많다면 그것이 부하가 될 수도 있다.
  4. 같은 동작이 겹치는 상황을 줄여라.
    • 같은 테이블에 동시에 접근하는 것은, 쿼리 수행시간 증가의 요인중 하나다.
    • 단일 큐처리, 테이블 분산 등을 통해서 최대한 병렬 작업의 효율을 높이도록 하자.
  5. 커넥션이 많다고 좋은게 아니다.
    • 4번 항목과 연관성이 있는 내용으로, 커넥션이 많다고 능사가 아니다.
    • 같은 테이블에 접근하는 SP가 여러개 커넥션에서 몰리면 블러킹으로 효율이 저하될 가능성도 높아진다.
    • 진정한 병렬수행이 될 수 있도록 설계하고, 사용하라.
    • 잘 사용하는 것은 잘 만든 것 만큼이나 중요한 것이다.
  6. 적은량의 데이터만으로 테스트 하지 말아라.
    • 2번 항목에서 언급했듯이, 테스트할 데이터가 실제 유저들이 쌓을 데이터와 비슷하면 비슷할수록 실제 서비스와 유사한 측정과 개선이 가능해진다.
    • 실제 유저들이 쌓을 데이터가 아닌, 개발자와 QA 데이터 몇개만으로 테스트 하지 말아라. 그렇게하면 논리적 구현의 테스트는 될 지언정 효율에 대한 테스트는 불가능하다.
  7. 트랜잭션을 적극 활용하라.
    • 4,5번 항목과 조금 엇갈리는 내용이지만, 효율보다 예외처리가 더 중요하다.
    • 트랜잭션을 잡아야 하는 동작이 있다면 반드시 트랜잭션을 잡아라. 효율을 위해 번거로운 예외처리를 서버에서 처리하게 된다면, 그로 인해 손해 보는 것 (예외 상황을 처리하는 개발 과정, 예외 상황 처리를 테스트 하는 과정, 예외 상황을 정상 처리로 수행 가능하게끔 만들기 위한 노력, 만약에 예외 처리에 실패했을 경우에 겪는 피해)들이 너무 크다.
    • 트랜잭션을 잡게 되면 그만큼 효율은 떨어지지만, 논리적 오류로써 겪게 되는 문제들에서 해소 될 수 있다.

(서평) 스티브 잡스의 수퍼 업무력 - 애플의 그리고 잡스의 성공 비결

요새 내 주변에는 모두가 입을 모아 한 기기를 찬양하는 상황이 펼쳐지고 있다. 바로 아이폰이다!  아이팟 터치도 혁신적인 기기였지만, 아이폰은 그 이상이다. 3G망을 이용한 무선 네트웍 기능은 언제 어디서나 원하는 정보를 이라는 컨셉에 매우 잘 부합한다.

과연 이 것을 노린 것일까, 요행일까?

아마도 ‘그’라면 노렸을 지도 모른단 생각이 든다.

바로 ‘스티븐 잡스’다.

매킨토시를 만들어 낸 장본인으로, 새로운 것을 시도하고 실현 하는 것에 탁월한 재주가 있는 그의 ‘작품’들은 나오는 제품마다 큰 관심사가 되고 있다.

발매되자마자 매진 행렬을 보여주고 있는 아이패드가 그 반증이다.

개인적으로 스티븐 잡스와 관련된 서적은 ‘잡스처럼 일한다는 것’ (http://elky.tistory.com/203) 을 읽었었다.

잡스가 얼만큼 뛰어난 인물이고 뛰어난 리더였는지를 조명한 책이었는데, 이번책은 그의 사업적인 측면에 좀 더 집중한 느낌이 강했다.

그의 사업적인 판단이 어긋났을 때도 꽤나 많기에, 결과론 적인 이야기들이 된 것이지만, 원래 세상이란 그런 것 아닌가?

내 개인적인 애플의 제품들에 대한 느낌은, 혁신보다도 ‘섬세함’이다.

아이폰 이전에도 무선 인터넷은 있었고, 모바일 어플리케이션 들도 있었다. 어플리케이션의 네트웍 기능도, 모바일 운영체제도, SDK도 있었다. 앱스토어? 이 것도 사실 새로운 것이라 보기엔 무리지 않을까? 아이튠즈도 마찬가지고.

아이폰/아이팟 터치의 성공 비결은 기존에 존재하던 것 들을 사용자 관점에서 바라보고, 이를 쉽게 접할 수 있고 활용 할 수 있게 도와주는 것에 집중했다. 그렇다보니 기존에 어렵고 복잡한 과정으로 인해 알아도 사용하지 못하던 기능들이, 또 아이디어들이 실현되고 있기에 아이폰과 아이팟 터치가 사용자들에게 사랑받은 것이다.

‘사용자 중심’적인 잡스의 발상이야말로 분야를 막론하고 필요한 것이다. 사용자를 중심에 놓으면 어려워선 안되고 호기심을 자극해야 한다. 쉽게 접근하려면 섬세해야 한다. 섬세한 배려만이 사용자에게 편리함을 가져다 줄 수 있다.

우리가 놓치고 있는 2%. 우리는 너무 쉽게 보고 있는건 아닐까?

(서평) 누워서 읽는 퍼즐북 - 퍼즐의 쾌감

퍼즐이라 하면 많이들 어렵단 생각을 하곤한다.

나 역시 마찬가지였고, 임백준씨의 책이지만 퍼즐북이라는 제목을 보자마자 당황했으니 말 다했다.

임백준씨 책은 하나도 빼먹지 않은 나로써, 이번책도 놓칠 수 없다는 생각에 예약 구매했다.

평소에도 퍼즐에 관심이 많았던 임백준씨 답게 퍼즐북에도 재밌는 퍼즐이 많이 포함되어 있었다. ‘그리’ 어렵지 않은 퍼즐들을 부담스럽지 않게 재밌게 풀어내는 임백준씨의 글솜씨는 여전히 유효했다.

프로그래머지만 수학적인 재미를 크게 느끼지 못하는 나로썬, 퍼즐의 수학적 발상에도 좀 부족한 감이 있었는데, 그런 부분에서 적어도 ‘재미’있게 접근할 수 있는 계기가 된거 같아 아주 좋았다.

특히나 기억에 남는 퍼즐은 다리 건너기 문제가 기억에 남는다. 예전에 접했던 다리 건너기 문제보다 어려웠지만 그만큼 푸는 재미도 있었다.

7명의 죄수와 7가지 색의 모자의 경우는 앞에 나왔던 죄수 시리즈을 이해했으면 쉽게 풀 수 있는 문제였다.

100개의 수를 기억하는 방법은 발상의 전환이 유효했고.

다음책은 내년 이후에야 나오겠지만 앞으로의 책에서도 프로그래밍을 수학/퍼즐에 연관시켜 재밌는 글 많이 써주셨으면 좋겠다.

이번 책을 보니 아쉽게도 프로그래밍 실무 전선에서 조금 물러나셨다고 하는데, 그렇더라도 여러가지 측면에서의 프로그래밍적인 철학이나 발상은 꾸준히 국내 여러 프로그래머에게 전해주셨으면 하는 바램이다.

C++ 오류 발생시 원인 정리

예외가 발생하는 상황

  1. 0으로 나누기
    • 나누기를 직접 사용하지 말고, 매크로 함수로 만들어놓고 사용하는 것이 좋다. (0으로 나누기를 시도했을 경우 로그를 남기는 등의 동작) int nDivisor = 0; int nResult = rand() / nDivisor; //Divide zero.
  2. 잘못된 메모리 접근 (널포인터 접근 포함)
    • Access Violation 예외 발생
    • 주로 Access Violation이 발생하지만, 멀티 스레드라서 다른 스레드에서 같은 주소에 할당을 요청했고, 그 주소를 다시 한번 delete했을 경우는 증상이 엉뚱한 곳에서 나오기도 하니 반드시 SAFE_DELETE같은 매크로 함수 혹은 auto_ptr, shared_ptr 등의 스마트한 메모리 관리 객체를 사용하자.

바로 크래쉬 되는 상황

  1. 스택 오버플로우 (무한 재귀 또는, 스택 변수를 너무 크게 잡고 사용할 시에 발생)
    • 대책 : http://msdn.microsoft.com/ko-kr/library/aa289171(VS.71).aspx
    • 스택 오버 플로우 트랩 : http://support.microsoft.com/kb/315937
void StackOverflow(int depth)
{
    char blockdata[10000];
    printf("Overflow: %d\n", depth);
    StackOverflow(depth+1);
}
  1. 예외가 발생해 스택 되감기하는 도중 예외 발생.
    • 이런 경우가 흔치는 않은데, 안전하지 않은 코드 작성시에는 이렇게 되기도 한다. 딱히 대책은 없다. 너무 무책임한가 =_=;

멈춘 듯 보이는 상황

  1. 반복 수행되는 쓰레드에서, 익셉션에 계속 남는다. (원스레드식으로 돌릴 때)
    • 덤프 백만개 쌓이다가 하드 풀나기도 한다. 우울해지곤하지…=_=
    • 대책은 HardDisk Space 계산해서, 특정 수치 이상이면 서버 떨구는게 차라리 낫다. 
  2. 무한 루프
    • GPG3권에 있는 1.3의 C스타일 매크로의 새로운 가치를 찾아서에 나오는 while_limit 를 적극 활용하는 것이 어떨까 싶다.
    • 좋은 습관은 주로 위기 상황에서 효과를 발휘하는 법이다.
  3. 데드락
    • 실제로 멈춘상태. 주의 사항은, 데드락이 걸려있는 스레드랑 전혀 별도로 돌아가던 스레드는 여전히 계속 돌고 있다는 점이다. 이 얘기는 뭐냐하면, AliveCheck용 스레드를 따로 두었을 경우, HeartBeat가 제대로 전달되 데드락 걸린 상황을 탐지하기 어려울 수도 있다는 것이다.
  4. 멈춘 듯 보이는 현상을 해결하기 위해선, UserDump 등으로 FullDump 뜨는 것을 추천한다.
    • FullDump로 콜스택을 확인해보면, 어떤 스레드가 무한 루프에 빠졌거나, 데드락에 걸렸는지 등을 확인 할 수 있다.
    • 좀 더 확실히 하려면 Snapshot처럼 여러번 FullDump를 남겨 확인해보면 좀 더 확실해 질 것이다.

구분이 잘 안가는 상황 (인과 관계를 깨드리는 상황)

  1. 스택 조금만 덮어 씌웠을 때 * 대책 : http://msdn.microsoft.com/ko-kr/library/aa289171(VS.71).aspx

  2. 버퍼 오버플로우 (얼만큼 덮어 씌웠는지에 따라 다르다)
    • 버퍼 오버플로우 되는 상황이 나왔다는거 자체가 우울한 상황이다. 이 상황에서 원인을 찾길 바라는건 요행에 가깝다. 
    • 메모리 관련 함수는 되도록 자제하라. (memcpy, memset 등등…)
    • 네이티브 포인터도 자제하라.
  3. 자식 클래스로의 잘못된 다운 캐스트한 후, 자식 클래스의 함수 호출.
    • 해당 영역에 어떤 데이터가 있었는지에 따라 다르다. 자식 클래스의 함수에서 사용하는 데이터가, 자식 클래스 전용 멤버일 경우 잘못된 메모리 접근이 있을 것이고….혹은 메모리 관련 함수거나 하다면 더 이상한 증상을 보인다. 
    • 잘못된 다운 캐스트 자체가 정줄 놓은거지…진짜 확신한다해도 다운 캐스트 자체는 죄악. 허나 어쩔 수 없이 다운캐스트가 필요하다면  dynamic_cast를 사용하라. 근데 나는 이것도 좀 반대하는 입장.
    • 이런건 어떨까? http://www.gamedev.net/reference/programming/features/TypeSafeGenPtr/
  4. 잘못된 함수 포인터
    • 주의 해라. 뭐 별 수 있나…
  5. 삭제 중인 객체 혹은 삭제된 객체의 순수 가상 함수 호출
    • http://msdn.microsoft.com/en-us/library/t296ys27(VS.80).aspx
  6. CRT가 함수에 잘못된 인수를 전달 받았을 때에 불리는 함수
    • http://msdn.microsoft.com/en-us/library/a9yf33zb(VS.80).aspx

멀티스레드 정책

최적화된 프로그램이란, 유휴 시간없이 하고 싶은 일을 최대한 많이 하는 프로그램을 의미합니다. 여기서 중요한 것은, 하고자 하는 일을 많이 해야 된다는 점이죠.  

싱글 스레드 클라이언트 프로그램의 경우는 대게 아래와 같습니다. 

  1. 입력 받는 작업
  2. 연산 작업
  3. 화면 그리기 
  4. 1번으로 돌아감

시간을 재고, 특정 작업 시간이 오래 걸려 재 속도를 내지 못한다면, 연산량을 감소 시킬 수 있는 처리를 하거나 (초당 프레임 조정 등), 만약 연산량을 줄일 수 없는 경우라면 게임 속도가 느려지게 됩니다.   연산량을 감소시켜서라도 제속도를 낼 수 있는 임계치를 최소 사양이라고 부릅니다.   멀티 스레드 서버 프로그램의 경우는 어떨까요?

처리 스레드 종류에 대한 가정

  • 소켓 이벤트 처리 6개 스레드
  • 패킷 처리 1개 스레드
  • 디비 처리 6개 스레드
  • 디비 처리 결과 반환 1개 스레드
  • 주기적인 로직 1개 스레드

1. 모든 스레드를 자유롭게 동작하도록 풀어놓고 스레드끼리 중복된 데이터를 사용할 일이 있을 때, 동기화 객체를 사용해서 관리해주는 방식(이하스레드 데이터 동기화 방식)

네 좋습니다. 그런데, 만약 상호 데이터 교환이 많은 경우는 어떨까요? A라는 데이터를 모든 스레드에서 요구한다면? A라는 데이터 사용중에는 다른 모든 스레드가 멈춰있겠죠? 싱글 스레드와 다를 것이 없게 됩니다.  물론 데이터가 겹치는 상황이 적다면 안정적인 속도로, 안정적으로 돌아가겠지만 글쎄요. 지금 당장은 그렇겠지만 기능이 추가되면서 분명히 지속적으로 성능 저하를 가져올 겁니다.

  2. 로직은 한 스레드에서만 돌리고, 비동기로 이루어져도 되는 처리에 대해서 요청한 후, 그 처리가 끝난 후에 신호를 받아 다시 처리하는 방식 (이하 원스레드 메시지 프로그래밍 방식)

이 방식은 Win32에서의 메시지 프로그래밍 방식과 매우 흡사한 방식이죠.   이렇게 했을 경우, 이벤트간 순서 제어도 직관 적이고, 데드락 위험성도 없으며, 비동기 스레드가 몇개 되지 않고 오래 걸리지 않는다면 처리 속도도 좋습니다.  문제는 싱글 스레드 이상의 효율을 내지 못한다는 것입니다. 로직 스레드에서 해야 될 일이 많다면…? 로직 스레드도 요청을 큐에서 꺼내서 처리하는데, 이 큐에 쌓이는 속도가, 데이터를 처리하는 속도보다 오래 걸린다면 전체적인 처리속도가 계속 늦어져 결국엔 사실상 아무일도 못하는 상태가 될 겁니다.   그래서 결국 로직을 멀티스레드로 분리하는 작업이 필요해집니다.   3. 로직을 멀티스레드로 처리하고 스레드끼리 중복된 데이터를 사용할 일이 있을 때, 동기화 객체를 사용해서 관리하고, 비동기 작업은 별도 스레드에서 처리하는 방식(이하 멀티 스레드 데이터 동기화 방식)

이렇게 했을 때의 맹점은, 첫번째 방식과 같습니다. 로직 스레드끼리 겹치는 데이터가 많을땐 느려지죠.   그래서 로직을 멀티스레드로 하면서 겹치지 않도록 해야 합니다.   4. 로직을 멀티스레드로 처리하고, 이 상황에서 스레드끼리 겹칠만한 일들과 비동기 처리를 별도 스레드에 맡기고, 로직 스레드에서는 다른 스레드와 겹치지 않는 일을 함으로써 Lock-Free 한 상태로 스레드를 관리(이하 멀티 스레드 메시지 프로그래밍 방식)   두번째 방식이랑 비슷하죠? 비슷하지만 다른 점은, 비동기로 해야 될 일 뿐만 아니라, 스레드끼리 겹칠만한 동작 자체도 별도 스레드로 맡긴다는 점입니다. 그리고 로직 스레드에서는 주의 깊게 (자신에게 주어진 데이터에만 접근하도록) 코딩 하는 것이 중요합니다. 상호 스레드간에 데이터 교환이 메시지 방식으로 이루어지도록 기반이 잘 갖추어져 있다면 위에서 설명한 세가지 방식보다 우월한 처리 효율을 보여줄 수 있습니다.   여기서 원스레드던, 멀티스레드던 메시지 프로그래밍 방식을 취했을 때 주의점이 있습니다. 데이터가 쌓이는 속도보다 푸는 속도가 빨라야 지연이 발생하지 않습니다. 만약 푸는 속도가 더 느리다면 스레드를 증가 시켜야 하는데, 여기서 주의 사항이 생깁니다. 스레드간에 데이터 교환을 위한 락이 적게 걸려야만, 스레드 갯수를 증가시켜서 얻는 잇점이 커지는 것이죠.  

스레드간에 데이터가 쌓이는 속도 대비, 풀리는 속도 측정이 되야 이 데이터 처처리 흐름에 문제가 여부를 알 수 있습니다. 물론 쌓이는 속도와, 풀리는 속도의 효율이 좋다 하더라도, 데이터가 모든 처리되기까지의 시간이 오래 걸린다면 그것도 효율이 좋다고 할순 없습니다.

지금까지 일반적으로 많이 사용되고 있는 스레드 사용 방식의 장단점에 대해서 알아보았습니다. 더 좋은 방법에 대한 논의는 지금도 계속 이루어지고 있고, 의견이 분분하지만, 제 의견과 생각에 대해서 정리를 해보고 많은 분들의 의견을 듣고 싶어 글을 올려봅니다. 의견 있으시면 언제든 댓글이나 메일 주세요. 감사합니다.