왜 SI쪽에서는 DB의 키를 꼭 문자로 하지요?

umean2me의 이미지

게임쪽에서 길지 않은시간 일했었고, 나머지 경력을 SI로 채웠습니다.
양쪽 다 나름 더하고 뺄것들이 있겠지만 그 중에서도 DB의 아쉬움입니다.

확신하던것을 제 자신조차 의심의 눈으로 자신을 보고 있는데요, 게임쪽에서는 키를 거의 NUMBER로 잡습니다. 부차적으로 필요한것을 INDEX로 다시 잡구요.

저는 이때 나름 NUMBER를 PK로 하면서 많은 장점을 보았습니다. 프로그램의 효율성,편의성이 증대되고 FK도 NUMBER로 잡히기 때문에 쿼리가 단순해지는 특징도 있습니다. 물론 역정규화 과정을 거쳐 잘개 쪼개지지 않도록 하는게 먼저겠구요. 또 JOIN을 하기 보다 대부분 프로그램상으로 JOIN을 처리하도록 하고요,

-여기서 프로그래밍으로 JOIN을 하는게 DB로 JOIN을 하는것보다 뭐가 효율적이냐라고 하시는 분도 있겠지만, 제 테스트 결과로 평균 약 10~20% 빨라진 경험을 했고, 또 통상 JOIN으로 처리하던것을 비지니스 로직의 특징으로 프로그램상의 알고리즘 개선을 통해 80%이상 빨라진 결과도 맛 보았습니다.- 이때 문자로 프로그래밍 JOIN을 시도하려면 엄청난 코딩수고와 메모리, 속도에 문제가 분명 있다고 생각됩니다.

이처럼 제 나름 경험에 비추어 분명 PK를 NUMBER로 주므로써 주는 이득은 꾀나 크고, 설계에 있어서나 변형에 있어서도 일관성을 둘 수 있다고 생각됩니다. 그럼에도 왜 우리 사회의 SI는 무조건 키는 문자로 잡을까요? 그 이유나 아니면 PK를 NUMBER로 잡는 부분에 대해 반대하시는 생각도 알고 싶습니다.

어쩌면 여기서 버텨보려는 이유를 이 게시판을 통해 찾는지 도 모르겠네요.

select99의 이미지

꼭? 반드시? 는 아니겠죠..
char 도 있지만. number도 많습니다. 거의 반반있는거 같군요..
한마디로 별생각없이 쓰는것 같네요..

kwon37xi의 이미지

저도.. 그냥 별 생각없이 그러는거다에 한표.

academic의 이미지

1.

프로그램에서 join했다고 속도가 더 빨라진다면...

DB 쪽에 문제가 있는 거겠죠. 인덱스를 잘못 설정했거나... SQL 문을 비효율적으로 썼거나...

물론, 모든 경우가 그렇다는 건 아니고 일반적인 경우라는 단서는 붙습니다.

특수한 경우에선 프로그램에서 join 하는 것이 속도가 더 빨라질 수 있다고 봅니다.

2.

그리고 number로 했을 때 속도가 빨라지는 것은 데이터의 길이가 짧기 때문일 겁니다.

2147483647 이라는 데이터를 정수형으로 저장하면 4바이트, 문자형으로 저장하면 10바이트이므로....

당연히 숫자로 저장하는게 빠르겠죠.

특별한 이유가 없을 땐 숫자를 문자형으로 저장하는 건 어리석은 짓이 맞다고 봅니다.

--
academic은 제 고등학교 때 동아리 이름입니다.
academic, 아주 가끔은 저도 이랬으면 좋겠습니다.

----
academic은 제 고등학교 때 동아리 이름입니다.
academic, 아주 가끔은 저도 이랬으면 좋겠습니다.

umean2me의 이미지

삭제는 어케해요

소타의 이미지

어떤 DBMS를 주로 쓰셨는지요?
전 뭘 하던지 8byte인 bigint(bigserial)로 PK를 잡습니다.
char형으로 PK를 잡는건 음....;;

join.. A와 B테이블의 데이터를 가져올 때 데이터의 관계를 생각해야만 어플에서 성능 상의 이점이 있을터입니다. A:B의 관계가 1:1 또는 1:n(> A), inner join이라면 DB에서 join하는 것이 맞겠고 그렇지 않으면 어플에서 해도 되긴 되겠죠.. DB 모델링의 문제일 수도 있고 잘못된 쿼리플랜이 나왔을 수도 있습니다. 저는 데이터에 대한건 DBMS에게 맡기자는 주의입니다. 어플에서는 뷰 이름만 던져주면 select * from 뷰; 정도로 쿼리를 쉽게 써도 되도록 DBMS에서 모두 처리(복수의 테이블을 뷰로 미리 join해 놓는다거나)합니다.

ironiris의 이미지

그 키가 ID인 경우가 많고 ID는 영문자로 이루어지는 경우가 많아서 그렇지요.
PK니까 당연히 중복입력도 안될테고 인덱스는 기본. 나름 편합니다.
가입자 아니 SI니까 회사 직원들의 인원도 그리 많지 않구요. 나름 장단점이 있습니다.

umean2me의 이미지

상황에 따라 프로그램 JOIN을 하게 되는데 뭐 이걸 어케 설명해야 할까요. 프로그램은 대부분 반복적인 상황이죠. 그래서 데이터를 가져와 프로그램으로 반복적인 쿼리대신 join을 수행하게 되니 빨라지게 되더군요. 대부분이 그럴텐데...이렇게만 해도 인덱스잡고 뭐하고 DB에 별짓을 다하더라도 상황에 따라 전체 수행속도가 10~30%는 올라 가는 경험을 하게 되었습니다.

키가 ID인 경우?
키가 ID경우를 안 만들었었는데. UNIQUE INDEX를 따로 잡을 지언정 NUMBER를 PK로 잡거든요. ID가 발급되면 그 사람은 NUMBER로 PK도 하나 갖게 됩니다. 그때 소켓통신을 주로 했었는데 훨 더 편한 효율성을 갖게 되었습니다. 그 4바이트나, 8바이트인 SEQ하나면 그 사람이 가진 모든걸 다 확인할 수 있으니까요.

killereco의 이미지

제가 생각해볼때
number로 할때의 단점
PK 설정시 가상키와 같은 개념이 필요 없을때는 ( 주문일자 +당일주문숫자)는 쓰기가 어렵다.
표시할 수 있는 숫자의 범위를 넘어섰을때( int등 프로그램의 데이터 타입 문제 )
DBMS에 따라서 number의 처리 방법이 약간씩 다른 걸로 알고 있습니다.
오라클은 가변형으로 처리하는 것으로 알고 있습니다.
가변형으로 처리할 경우 DBMS에 따라서 row chaining 같은 문제에 부딪힐 수 있습니다.
PK에 의미를 부여하기 힘들다( 예: 주문번호:20080101_111 )

SI의 성격상 업무 로직이 디비기반으로 이루어지는 것이 많기 때문에 프로그램보다 디비에 더 많은 역할을 부여하고
디비 데이터를 점검할때 PK가 의미있는 문자일때 좀 더 직관적으로 보기 편하지 않아서 그렇지 않을까 생각해 봅니다

JOIN의 문제
쿼리와 프로그램 둘다 잘 짰다면 같은 사양의 하드웨어라면 속도가 그리 차이가 나지 않을것이라고 생각하구요,
디비에서 조인 처리 잘하는걸 배우는 것이 프로그램에서 처리하는것을 배우는 것보다 좀더 쉽지 않을까 생각하구요,
대부분의 경우 디비 서버가 성능이 훨씬 더 좋은 경우가 많기에 디비를 활용하는 것이 낫지 않을까 생각하구요,

이와 비슷한 성격의 문제가 주문일자와 같은 PK에 속하게 되는 컬럼의 datatype의 선택문제입니다.
date type과 char(8) 타입 같은 문제도 아주 어려운 고민 거리 중의 하나입니다.

atie의 이미지

이 답변은 무슨 의미인지를 알겠는데... 예시가 좀 잘못된 듯 합니다. 주문일자를 PK로 쓴다는 것도 그렇고, 주문번호에 _가 있다고 한 필드로 잡는 것도 어색합니다. 둘로 나누고 view에서나 프로그램 상으로 _를 가진 형식으로 보여줘야 하겠죠. 무슨 점검일 때도 view로 보면 불필요한 _를 없애고 두 개의 숫자 필드면 충분하겠는데요.
----
I paint objects as I think them, not as I see them.
atie's minipage

----
I paint objects as I think them, not as I see them.
atie's minipage

jick의 이미지

C에서야 숫자가 문자열보다 훨씬 효율적이지만 DB에서는 어느 쪽이든 그냥 bytestream data일 뿐입니다. 한 개의 컬럼으로 만들 수 있는 것을 _를 경계로 두 컬럼으로 나누는 게, 경우에 따라 더 비효율적일 수 있습니다.

경우에 따라 나눈 게 효율적이라고 해도, 그 차이는 사실 미미하고 기껏해야 숫자로 바꾸면서 한 row의 길이가 몇 바이트 줄어들어 DB 크기가 몇 % 줄어드는 정도 이상을 기대하기 힘들 겁니다. 컬럼 하나를 쓸 곳마다 전부 두 개를 쓰는 불편을 감수할 가치가 있을지는... 글쎄요...

C에서 문자열을 숫자 두 개로 바꾸는 것 정도의 확 눈에 띄는 차이를 내기는 힘듭니다.

* 이상 DBA 경력은 전무하지만 모사에서 DB 개발한답시고 몇 년 동안 이것저것 주워들은 사람의 풍월이었습니다.

atie의 이미지

필요없는 _를 빼는 것 외에는 필드의 크기를 줄이고자 두 개의 필드로 구분하라는 뜻이 아닙니다. 두 개의 필드는 각각의 의미를 가지고 있다는 것이 둘로 나누는 이유이고 (_는 의미가 없죠 단지 포맷을 위한 문자이고), 데이터의 정렬이나 합산 등의 연산을 할 때도 하나의 필드에서 앞 뒤를 구분하는 것이 한 필드로는 필요한 경우가 생기지만 둘인 경우는 불필요할 수 있습니다.

그리고 _같은 형식이나 날짜별 일련번호의 크기가 다른 필드를 쓰는 곳과 데이타 교환시 저렇게 하나인 필드는 둘로 다 쪼개고 또 합치는 로직이 필요합니다.

언어의 관점이 아니라 데이타베이스 구성에서의 이야기 입니다.
----
I paint objects as I think them, not as I see them.
atie's minipage

----
I paint objects as I think them, not as I see them.
atie's minipage

shyblue의 이미지

DB 얘기가 나오면 항상 하는 얘기지만, 프로그래머의 실수를 DB의 잘못 혹은 저성능으로 오해하는 경우가 참 많습니다.

JOIN을 처리 하는 방법은 정말 많습니다. 하지만, DB에서 처리하는 join가 프로그램에서 처리하는 join은 생각의 출발부터가 다릅니다.
DB에서의 JOIN은 집합적 사고에서 출발합니다. 이 부분을 이해하지 못한 상태에서 프로그램적인 시각으로 DB의 조인을 바라본다면, 절대로 제대로 된 성능의 조인을 얻어 낼 수 없을 것입니다.
필요에 따라 가끔 프로그램적으로 조인을 처리하는 사람과, 엔진내의 핵심 중에 하나가 조인을 처리하는 사람이 있다면, 과연 누가 더 효율적인 알고리즘을 사용할까요??
DB를 DB답게 사용하지 못했기 때문에, 성능이 나오지 않은것입니다.

그리고, 전 개인적으로 DB가 처리할 수 있는 일은 DB에 맞겨야 한다고 생각합니다. DB를 처리하는데 있어서, 프로그래머가 아무리 효율적으로 처리한다고 해도, DBMS에서 구현된 알고리즘보다 좋기는 어렵습니다.

PK가 Number이거나, char인것은 그 키값이 가진 속성에 따를것이지, PK의 성능 보장을 위해 NUMBER를 유지한다라는것은 DB의 입장에서 보면 그다지 와닫지 않는 말입니다. 고정크기 값이기 때문에 약간의 이득을 얻을 수 있지만, 같은 고정크기 값이라면 NUBMER의 이득이 크지는 않을 것입니다.

PK는 무조건 Clusted index가 되는 DB(예를 들어, MS SQL Server)의 경우는 조금 더 영향을 받겠지만, 그래도 큰 차이는 없을 것입니다.

중요한건, 정말 제대로 된 키값을 PK로 잡았느냐 아니냐 입니다.

時日也放聲大哭

時日也放聲大哭

academic의 이미지

전적으로 말씀하신 바에 동의합니다.

다만, 예를 들어주신 부분에만 조금 사족을 달께요.

MS SQL Server에서 PK가 무조건 Clustered Index가 되지는 않습니다.

Clustered로 할지, Non Clustered로 할지 지정이 가능합니다.

--
academic은 제 고등학교 때 동아리 이름입니다.
academic, 아주 가끔은 저도 이랬으면 좋겠습니다.

----
academic은 제 고등학교 때 동아리 이름입니다.
academic, 아주 가끔은 저도 이랬으면 좋겠습니다.

umean2me의 이미지

뭐 저야 같은 이야기 반복하고 있지만, 전 DB는 하나의 디스크라고 생각하고 있습니다. 다만 그 기능적 차이가 디스크보다 넓은 의미로 바라보고 있습니다.

"DB가 처리할 수 있는 일은 DB에게 맞겨야 한다고" 라는 부분이 너무 애매모오해서 어디까지 인지 모르겠습니다. 실로 DB가 처리할 수 있는 부분은 어마어마합니다. 서비스되는 웹하드 조차도 통째로 DB만으로도 구현할 수가 있는 세상입니다(실제로 그런 상용 서비스가 잠깐 존재했었다는..) 과연 DB에게 맞겨야 한다는 부분이 어디까지 인지 모르겠습니다.

아무리 DB일지언정 못피하가는 부분이 있지요. 소켓통신과 디스크 I/O등 그것 또한 OS나 HW에 종속적인 부품이지 그 이상은 못벗어 날텐데요. DB가 가지는 소켓통신에 드는 비용만해도 큽니다. DB의 소켓통신이 분명 성능이나 기능상 뛰어나지만 그것이 우리가 몇줄로 끝내는 소켓통신 프로그램과는, 우리가 밟고 있는 땅과 저편 은하계만큼이나 다른 이야기 입니다. DB의 소켓에서 성능이 빠르고 기능이 우수하다는 말은 결코 상대적이지 절대적 이야기가 아닌것으로 이해하고 있습니다.

더 단순한 예로 배열과 STL의 벡터를 비교하더라도 배열이 빠릅니다. FOR문을 돌때 배열보다 훨 느린것이 분명합니다(10만건인가 단순테스트에서 약 1.5~2배정도 느렸습니다. reserve를 사용했음에도 불구하고). 그럼에도 불구하고 CPP에서 벡터를 더 애용하는 것은 그만한 안정성을 갖추고 있으면서도, 같은 기능을 내가 구현한 것 보다 빠르고, 시간도 덜 들이는 부분 때문에 쓰는 것이죠.(기타 OOP특징들)

DBMS의 그런 부분들은 엄청난 기능과, 우수한 성능을 보유하고 있지만 위의 단순한 예에서 배열이 더 빠른 것처럼 그런 기능 떼어 버리고(버퍼니 파싱이니 뭐니뭐니....) 단순 쿼리로 DB에서 두 테이블을 MAP으로 가져와 JOIN처럼 해버리릴때, 그래도 이런 부분들 조차도 DBMS에게 맞겨야 한다고 보시나요.

한 쿼리(각 2개 테이블에 1만개씩)에 대략 데이터 100~500개를 가져오는데, 이 반복작업을 1만번 한다 치면 적어도 1000000개의 데이터가 들어오는 것입니다. 그것도 1만번에 걸쳐 쪼개져 오지요. 여기에 소켓통신도 포함되겠죠. 즉, 소켓 함수콜 1000000*2(와따가따)과 DB 콜10000 에 따르는 오버헤드만 따저도 수배에 이릅니다(버퍼쓰고 있겠지만..).-(소켓 또한 아직까지 커널상 ZeroCopy가 지원되지 않는것으로 압니다(윈도우 및 일부 유닉스빼고) )

단순 상식에서 라도 두번만 다녀와서 프로그램상으로 한다면 더 빠르지요. 유명한 자료구조 알고리즘 라이브러리만 사용하더라도 말입니다(STL따위). 그러자면 좀 더 단순해지기 위한 DB설계가 따라야겠지만.. 프로그래머가 그런 특징들을 잘 이용한다면 두개 시스템으로 구성될 DB 한개 시스템으로도 충분합니다.

제 주장이 지나친거 아닌가 모르겠습니다.

jick의 이미지

죄송하지만 핀트가 약간 어긋난 말씀을 하시는 것 같습니다. 소켓통신의 오버헤드야 조인을 DB가 하든 클라이언트가 하든 어느 쪽이나 마찬가지로 필요한 것 아닙니까?

"Join 조건이 equal이 아니라서 조인하고 났더니 행의 갯수가 제곱에 비례해 늘어나더라..." (극단적 예를 들면 원래 A table이 100 row, B table이 200 row인데 결과가 cartesian product가 되어서 20000 row라든지...) 이런 경우가 아니라면 join을 해서 통신 오버헤드가 줄면 줄었지 어떻게 늘 수가 있는지 잘 이해가 안갑니다. -.-

umean2me의 이미지

다른분들도 단순한 시각으로 보는것 같습니다. 제가 잘못적었나요. 잠을 못자서 그런지..
의도하는 글의 의미는 짧게 코드로 하자면 이렇습니다.너무 확 짧게 표현한거고 실무는 더
복잡하며 반복적으로 돌아 가겠죠.

//일반적으로 많이 사용되어지는 꼴...
void foo()
{
   for (int i = 0; i < 10000; i++)
   {
      결과 = SELECT ... FROM A, B WHERE ...;
   }
}
 
 
//프로그램 join
void foo()
{
   테이블A = SELECT ... FROM A;
   테이블B = SELECT ... FROM B;
 
   for (int i = 0; i < 10000; i++)
   {
      결과[] = 프로그램JOIN(테이블A,테이블B);
   }
}

뭐 수번만 한다면야 DB에서 처리하는게 낫다고 생각되는데 보통 실무에서는 이런 과정을 만번, 십만번, 백만번, 하루에도 엄청난 횟수를 기록하고 있지 않나요?

잘 변하지 않는 기준테이블 데이터들을 미리 올려놓고 시작하면 훨 빠르고, 배치 작업에서도 첨 실행시 올려놓고 해도 그렇고...

PK의 NUMBER에 대한 성능은 직접 테스트 해봐야 겠습니다.

hayarobi의 이미지

쿼리를 잘 짜서 아래처럼 구현하지 않을까요?

void foo()
{
   결과셋 = SELECT ... FROM A, B WHERE ...;
   for (int i = 0; i < 10000; i++)
   {
      처리
   }
}

같은 쿼리 천번 날리는 것과 각각 천 row씩 받아오는 것과 쿼리 한 번 날려서 100만row를 처리하는 것... 어느게 더 빠를지는 비교가 필요없을 것 같네요.

---------- 시그 *****
저도 세벌식을 씁니다.
M$윈도우즈, 리눅스, 맥 오에스 텐, 맥 오에스 클래식을 모두 엔드유저 수준으로 쓴답니다.
http://psg9.egloos.com

=================
잠못자는 한솔아빠

umean2me의 이미지

.

shyblue의 이미지

void foo()
{
   rs = SELECT ... FROM A, B WHERE ...;
   for (int i = 0; i < row_count(rs); i++)
   {
      처리
   }
}

로 하는게 일반적이지요.

A,B 테이블을 따로 가져와서 비교하는 것은 정말 말했듯이, 집합적 사고가 아닌, 순차적 사고로 DB를 보는 가장 대표적인 시각입니다.

時日也放聲大哭

時日也放聲大哭

sDH8988L의 이미지

글쎄요...

제가 잘못 읽고 있는 건 지는 잘 모르겠는데요...

100~500개의 데이터를 가져오고 (DB -> Client) 이 반복 작업을 1만번 한다고 하셨습니다...

이것의 연산, 통신 비용을 1만 개의 데이터를 가진 테이블 2개를 가져와서 프로그램 상에서 join을 하는 것의 비용과 비교하신 거 같습니다... 맞습니까?

흠... 솔직이 어떤 Client에서 이런 엄청난 작업을 하는 걸까요?

저도 SI에서 ERP를 좀 짜봤었는데, 저런 식의 엄청난 작업을 하는 Client를 짜본 적은 없습니다...

제가 해본 제일 큰 작업들은 대부분 Client에서 명령만 내리고 실행 결과만 간략하게 받아 보는 형식이었는데요...(생산 관리에서 스케쥴 + 부품 조달 관련 프로그램들이 좀 지랄 맞죠...)

많은 수의 Client들이 DB에 접속하여 작업을 할 경우에 테이블을 날리는 등의 작업은 실제로 문제가 될 수도 있습니다...

그리고 이 쓰레드의 주제가 얼마나 빠르냐로 가고 있는데, 사실 얼마나 빠르냐 보다도 얼마나 쉽게 구현될 수 있고 얼마나 쉽게 유지 보수가 가능한 지가 기업 업무에서는 더욱 큰 문제라고 봅니다...

저렇게 데이터를 Client 프로그램에서 다룰 경우, 나중에 유지 보수에 엄청 문제가 생기게 됩니다...

데이터 로직은 DB나 Middleware에 두시는 것이 휠씬 좋겠죠...

말씀하신 테이터를 가져와서 프로그램 상에서 처리하는 부분은 속도나 Load Balancing을 고려하여 Middleware에서 할 수 있습니다...

결론적으로 저런 작업을 하는 Client를 만든다는 것은 거의 피해야 할 일이라고 보고요. 물론, 저런 작업을 Middleware가 한다면, 이야기가 좀 달라 질 수도 있습니다... DB의 역할과 Middleware의 역할을 중첩적인 부분이 많이 있기 때문에 로직과 연산을 어디에 두느냐는 상황에 따라 달라집니다.

한 가지 더 말씀 드리자면, 프로그램은 속도가 다가 아니라는 것입니다... 몇 배 느리더라고 할 지라도 그 절대적인 속도가 허용할만한 속도라면, 유지 보수에 더 용이한 쪽으로 프로젝트를 진행하게 됩니다...

속도만을 생각했다면, OOP, Design Pattern은 나오지도 말아야죠... C로 짜는 것보다 느린데요...

serialx의 이미지

string으로 쓸 수 있는 Key 가 있는데 굳이 Autoincrement 등으로 int PK를 쓰실거면 이런 글도 있습니다:
http://joshua.schachter.org/2007/01/autoincrement.html

즉, autoincrement 로 PK를 생성하면 옛날 자료가 앞에오고 최신자료가 뒤에 가는데, 이게 일반적으로 보통 필요 없는 경우가 많아서, 차라리 그럴바에 적당한 string PK 로 비슷한 종류의 데이터를 가까운 위치에 두는게 훨씬 성능이 좋을 수 도 있다고 합니다. 한마디로 locality issue 죠.

Del.icio.us 개발자가 하는 얘기니 좀 신빙성이 있겠지요?

kalstein의 이미지

TEXT의 힘이란것도 있으니까요 ^^;;

예를들어...ID를 키로 하게되면 문자열이겠지요. 그럼 DB에서 유일한 키가 유저의 id가 되는것이고...의미하는바를 명확히 알기 쉽습니다.
반면...NUMBER로 SEQ_NUM을 이용해서 쓴다면...이게 누구인지 보려면 한차례 더 거쳐야하는거죠.

저도 예전엔..바이너리가 월등하지않냐. 라고 생각했었지만...Art of Unix programming인가요? 그 책을 읽고...그외에도 '실용주의프로그래머' 라던가...기타등등에서 text의 힘에 대해서 알아가면서는...점점 plain text를 좋아하게 되네요 ^^;;;


------------------------------------------
Let`s Smart Move!!
http://kalstein.tistory.com/

angra의 이미지

뭐 PK로 캐릭터를 쓰는건 딱히 이유를 모르겠습니다. 하지만 어플에서 조인처리부분을 했을 때 성능이 더 빠르다는건 이해가 안되는군요.

RDBMS는 정규화를 기반으로 하기 때문에 테이블간의 조인에 최대한 성능을 발휘할 수 있어야합니다. 디비에서 성능이 안나오는걸 어플 레벨에서 성능을 냈다는건 분명 디비 SQL을 잘못 짰다고 밖에 이해가 안됩니다.

제가 개인적으로 6천만껀의 데이터를 핸들링 해본적이 있습니다. 첨에 방법을 몰라서 어플레벨에서 할려고 하다가 DBA의 조언을 듣고 디비상에서 쿼리 하나로 처리했습니다(물론 엄청난 길이의 SQL문이 되기는 했지만). 과연 6천만껀식 되는 데이터를 어플레벨에서 처리한다면 속도가 나올까요?

그냥 주저리 주저리입니다. ^^

umean2me의 이미지

주변사람들이나 여러글을 읽어보니 다분히 DB에 맹목적이 아닌가 생각되는데요. 각가의 두 테이블 10만건의 데이터를 일일이 비교하는 SQL의 minus연산을 가지고 직접 테스트 해보았습니다.
각 테이블은 똑같은 테이블이고 컬럼수는 약 90여개 입니다. 10만x10만=백억건에 테이블당 90여개의 컬럼이니 900만x900만=81조개를 비교해야하는거 맞나요? 뭐 사실 컬럼단위로 900백만번 비교하면 되는거니까...키는 각 테이블이 4개입니다. 똑같이 데이터가 로드되어 있는 상황에서 어느쪽이 빠를것 같죠??

어플에서 로드된 후 비교하는데 1초, db에서 하는데 6초 걸렸습니다. 물론 어플에서 단순 for문으로 비교하는건 아닙니다.

어플레벨에서 성능을 냈다는건 db sql을 잘못 짠것이 아닙니다. 별 어려움없는 두테이블의 조인(인덱스도 타고...)에서 조차도 db가 느립니다.

ATable에 1만건 BTable에 10만건이 있을때 다음과 같은 코드를 생각할 수 있겠죠.

      class Table //Signleton
      {
      public:
          map <int, AObject *> aTable;
          map <int, BObject *> bTable;
      };
 
      .....
      while(aObjectIterator = aTable.end())
      {
          BObject *Object = Table::getInstance()->bTable[aObjectIterator->second->first];
          resultTable.push_back(Object);
          ++aObjectIterator;
      }

이 처리를 db에서 위와 같은 코드로 처리하겠습니까? 더 많은 과정을 거치겠습니까?

업무에서 볼때, 보통의 업무처리에서는 다음과 같을 것입니다.

       foo(어떤 값들....)
       {
           db쿼리(어떤값들);
       }
 
       ....()
       {
           외부_값들 = 소켓처리따위등()...
           for (int i = 0; i < 외부_값들의 수; ++i)
               foo(외부값들[i]);
       }

요청이 많을때는 수천건의 요청이 있을텐데 이런 업무형태에서는 잘 변하지 않는 기준데이터들을 미리 로드한후 어플레벨에서 처리하는게 더 빠른게 아니던가요.

물론 만능은 아니겠지만 대부분의 업무는 저런 형태였는데요. 로드된 상태에서 프로그램을 직접 짜서 직접 테스트 해보시는게 어떻신지요들..

사실 db가 빠르다는거 막연한 상식처럼 생각하시는건 아닙니까...?? 제 상식은 DB는 저장소이다라가 다이거든요.

비행소년의 이미지

       foo(어떤 값들....)
       {
           db쿼리(어떤값들);
       }
 
       ....()
       {
           외부_값들 = 소켓처리따위등()...
           for (int i = 0; i < 외부_값들의 수; ++i)
               foo(외부값들[i]);
       }

이런 코드 라면 결코 DB가 빠를 수 없지요.

       ....()
       {
           외부_값들 = 소켓처리따위등()...
           db쿼리(어떤값들);
           for (int i = 0; i < 외부_값들의 수; ++i)
               커서 오퍼레이션();
       }

저라면 이렇게 하겠습니다.

Quote:
사실 db가 빠르다는거 막연한 상식처럼 생각하시는건 아닙니까...?? 제 상식은 DB는 저장소이다라가 다이거든요.

괞히 DBA 라는 직업이 생긴게 아닙니다. 단순 저장소 역활 밖에 못한다면 oracle 같은 건 쓸 필요도 없고, DBA를 찾을 이유도 없겠지요.

높이 날다 떨어지면.
아푸다 ㅡ,.ㅡ

atie의 이미지

어떤 연산인지 위의 코드로는 감이 반 밖에 안오는데 데이타를 미리 로드했다는 의미가 A 십만 건, B 십만 건을 읽어 놓고 A.a - B.a 마이너스를 query를 날려 계산을 한다는 뜻인가요?

만약에 그렇다면, 자주 쓰이는 연산은 DB에서는 UDF나 다른 방책을 씁니다. 데이타를 읽을 때 이미 연산한 컬럼을 읽을 수 있는데 따로 반복문에서 데이타 읽어놓고 쿼리를 날리지는 않겠죠.

제 경험으로는 다량의 연산이 필요한데 SQL 구문을 코드 상에서 직접 쓰는 것은 드문 일입니다.
----
I paint objects as I think them, not as I see them.
atie's minipage

----
I paint objects as I think them, not as I see them.
atie's minipage

select99의 이미지


DB에서하면 로드할필요도없이 내부적으로 처리하고 말죠.

어플에서 처리하려면 DB가 쿼리후 로드되는과정이 필요하지만 DB에서 하면 건수및 로드과정이줄어들죠.

예를들어 조건에 맞지 않은데이터는 아예 어플단으로 올필요조차 없다는겁니다.

어플에서 조인하면 조인시의 조건이 아맞는건까지 모두 오겠죠..더구나 어떤처리의 입력보다 출력은 대부분 작습니다.

한마디로 A테이블의 컬럼수가 100개 B테이블의 컬럼수가100 어플에서 처리하려면 도합 200여가지 정보가 날아와야하지만
DB가 처리하면 결과만줄수 있습니다

조인과정을 비교해봐도 어플에서 처리하는로직구현하는사람이 아주효율적인 코드로 구현하는경우는 드뭅니다.
위예로봐도 C++로 되어 있고 이자체가이미 일반적으로 C로되어있는 DB 보다는 언어에서부터 성능이 뒤지죠..
또한 효율적인 포인터사용법등의 최대한효율을 살려구현되진 않았다고보아집니다.
또 인덱스문제도 있죠..

따라서 DB도 100%믿을순없는건 맞는말이지만..웬만하면 DB 자체에처리할수 있게하는게 이론적으로 맞습니다.

시지프스의 이미지

.

begin{signature}
THIS IS SPARTA!!!!!n.
end{signature}

dormael의 이미지

저도 대체적으로 글을 쓰신분의 방식으로 작업해 왔습니다.

여러분들의 글을 읽어보고 판단해 보면...

역시, 상황에 따라 다르지 않을까요?

제가 주로 작업했던건 초 고사양의 DB서버 1대 + 일반 사양의 클라이언트의 요청을 처리하는 어플리케이션 서버 수백대의 환경이었습니다.
각 어플리케이션 서버는 적게는 수십개의 많게는 수천개의 클라이언트와 연결 상태를 유지하고 요청을 처리하는 구조였습니다.

이런 환경에선 아무리 DB서버가 고사양이라 하더라도 엄청나게 많은 연결과 클라이언트 마다 다른 요청을 처리해서 다른 결과를 만들어 내야하는 상황에서 단 한대의 DB서버로는 어떻게 해도 답을 얻을수가 없어서 단순하거나 테이블이 크지 않은 경우만 DB서버에서 조인, 정렬 처리하게 하고 나머지는 어플리케이션에서 처리하도록 작성했습니다.

DB서버가 메모리 부족으로 IO가 발생하는것을 최대한 줄이려는 의도에서요.

물론 데이터의 연관성에 따라 DB서버를 분리하는 방법도 있긴 하지만 이러면 작업량이 너무 커져서 위의 방법을 사용하게 되었습니다.

이것도 부족해 나중에는 미들웨어까지 만들어서 연결수를 최대한 줄이는 방법까지 썼습니다만, 이건 성공적이었다고 말하기는 힘들것 같습니다.
DB서버야 로드가 줄었지만 어플리케이션 쪽에 병목이 생기더군요... ㅡ,.ㅡ
이런걸 줄이려면 미들웨어도 좀 더 섬세하게 만들어야 되는데 거기까지는 할 수 없게 되어서...

제가 예로 든건 이런 상황에선 이렇게 할 수 밖에 없지 않을까 하는 생각에서 입니다.
혹시 다른 방법이 있을지도 모르지만...

하지만 다른 환경에선 또 다른 방법이 있을거라고 여러 분들이 써 주신 글을 보고 생각하게 되네요.

^_^

-- Signature --
青い空大好き。
蒼井ソラもっと好き。
파란 하늘 너무 좋아.
아오이 소라 더좋아.

kalstein의 이미지

간단히 예를들어서...특정조건 1000건을 땡겨서 클라이언트에 보여줘야되는데 보여줄때는 소팅된 결과를 보여줘야된다고 가정한다면... 저정도는 그냥 app단에서 소팅해도 큰 상관이 없겠죠 ^^;

괜히 DB에 부하가 가니까요...그러나...1000건정도가 아니라 한 10만건 된다. 하면...그건 DB스키마의 변경이 가해져야될 정도죠;;; 10만건을 한번에 내려서 클라이언트가 소팅할 이유는 없지요~

미들웨어 굳이 안해도...connection pool 기법은 DB단에서 제공하지않던가요? DB작업을 안해본지 몇년이 지나가다보니...가물가물하군요 ㅎㅎㅎ


------------------------------------------
Let`s Smart Move!!
http://kalstein.tistory.com/

뉴피엘의 이미지

데이터가 어디에 있느냐의 차이로 보입니다 ^^

메모리 용량 내에서 비교적 소량의 정적인 데이터를 로드해서 메모리에서 연산하는게

일반적인 디스크 기반 dbms에 매번 요청을 하는거 보다 빠르겠지요.

만약 메모리 기반 dbms 였다면 차이가 훨씬 줄었을겁니다.

결국 상황에 따라.. db 혹은 메모리를 활용하면 되는거지요 ㅋㅋ..

--------------

我不知道

我不知道