IT 이야기2009. 4. 2. 04:55

다시 퍼포먼스 얘기를 해보자.

문제는 아래와 같다.

권한 설정문제인데...
약 10만개의 Tree 형태의 Category Node 가 있다. 권한 정보도 트리 형태로 관리되고 있다. 권한의 종류는 대략 100여가지 정도이다. 그룹과 사용자는 M:N의 관계이며 하나의 그룹은 사용자 혹은 다른 그룹을 하위로 가지고 있다. 사용자는 최대 1000명이 가능하며 그룹은 아마도 수백내외 정도이다.

이제 특정 사용자(혹은 그룹)은 특정 카테고리에 특정 권한을 설정할 수 있다.
이를테면 Category_Read권한을 가진 사용자(혹은 그룹에 속한자)만이 해당 카테고리를 볼 수 있고 해당 권한(혹은 상위 권한)을 가지지 않은 사용자는 해당 Category가 화면에 보이지 않아야 한다.



즉 3개의 Tree 형태를 가지고 권한 정보를 설정하게 된다. 만약 catB에 GroupA가 category_root 권한을 설정하게 되면 GroupA 하위의 모든 그룹에 속하는 사용자는 catB 이하의 모든 카테고리에 대해서 category_root 권한 이하의 모든 권한을 가지게 된다. 단 이때 권한정보에 카테고리 상속여부를 False로 설정하게 되면 catB의 카테고리에 대해서만 권한을 가진다.

그러다가 bleujin 사용자만 catE 카테고리에 대해서는 category_read 권한을 제외하고 싶다면 다시 catE에 bleujin이 category_read 권한을 revoke여부를 true로 설정하게 되면 기존의 권한트리는 변하지 않은채 bleujin의 권한만 재 설정할 수 있다. (이는 DB 모델과 비슷하게 권한이 없다라는 것도 일종의 권한이다 라고 추상화 모델이다. 이경우 상위 카테고리인 catB에는 권한을 주고 하위 카테고리인 catE에는 revoke권한을 주면 catB,catF는 권한을 가지고 catE는 권한이 없다.)

일반적인 시스템처럼 권한정보가 사용자 그리고 카테고리 별 권한별로 row 복제가 되지 않는 다는 점에 주의 하여야 한다. 잘못하면 하나의 권한 정보를 설정할때 수첫만 row가 설정되야 할지도 모르며 이후 관리가 무척 어렵기 때문이다. 권한 정보를 수정하지 않고 특정 사용자가 특정 그룹에서 제외되면 그룹이 가지는 권한은 더이상 적용되지 않는다.

처음 대하면 약간 머리가 핑 돌지만 이 모델은 권한트리에서 생각할 수 있는 가장 유연한 모델이다. 특정 사용자가 특정 카테고리에 특정 권한을 가지는가를 체크하는 것은 그리 어려운 일이 아니다. 특정 사용자의 모든 상위 그룹과 특정 권한의 모든 상위 권한 그리고 특정 카테고리의 모든 상위 카테고리를 읽어서 카테고리 상속여부와 rovoke권한 여부를 잘 조합하면 하나의 SQL로 0.1초 이내의 응답속도를 보장할 수 있다.

문제는 바로 이것이다. 특정사용자가 접근했을때 category_read 이상의 권한을 가진 카테고리 리스트를 Tree 형태로 보여 줄수 있는가... 이다. 이때 특정 사용자가 catE에만 category_read 권한(혹은 그 상위 권한)을 가지고 있다면 catA와 catB에는 category_read 권한이 없어서 내용은 볼수 없지만 카테 고리의 이름은 보여야 한다. 상위 카테고리가 보이지 않으면 Tree 형태의 category모델에서 catE에 접근할 수 조차 없기 때문이다.

이 문제를 더욱 어렵게 하는 것은 카테고리 리스트를 보는 것은 매우 자주 일어나므로 반응 시간이 2초 이내여야 한다는 사실이다. 이게 왜 문제가 되냐면 아무런 특정 로직이 없이 10만개의 category를 Tree 형태로 읽는데에만 약 4-5초가 걸린다는 사실이다. 게다가 단순히 트리 형태의 category를 읽는 것은 아무런 의미가 없고 상위 그릅과 상위 권한도 고려해야 하며 카테고리 상속여부와 revoke 권한도 고려를 해야 하는 로직을 생각하면 일찌감치 날 샌 문제와 마찬가지이다.

catB가 보여야 하나 말아야 하는 정보는 단순히 catB와 관련된 권한 정보만 가지고는 알 수 없다. 어쩌면 catB의 11단계 밑의 카테고리에 해당 사용자가 속하는 상위그룹에게 root 권한이 설정되어 있으면 catB는 Tree 리스트에 보여야 하기 때문에 하위의 모든 카테고리 정보도 보아야 한다.


이제 생각을 해보자. 이전 성능의 첫번째 행동강령은 기준과 목표가 있어야 한다. 였다.
여기서의 기준은 10만개의 카테고리와 수천명의 사용자 그리고 수백개의 권한 정보를 아무런 로직없이 단순히 데이타베이스에서 읽는데만 5초 이상이 걸린다는 기준이 있고 목표는 2초 이내이므로 이미 정상적인 방법으로는 불가능하기 때문에 이때 아무리 멋진 프로그래밍 모델을 생각하더라도 그쪽은 답이 없다.

이때 중요한 점이 바로 이점이다. 이미 정상적인 형태의 connect by start with로는 절대 답이 나올수 없기 때문에 다른 방법을 고려해야 한다는 사실 그 자체를 아는게 중요하다. 기준이 없으면 자신이 올바른 길을 가고 있은지 아닌지를 스스로가 알수 없다.

아까 하나의 카테고리에 특정 사용자가 특정 권한이 있는지 여부를 체크하는데 0.1초가 걸린다고 하였다.(SQL에 익숙하지 않으면 이 SQL을 짜는 것도 쉽지 않다.) 단순히 catA를 보여줘야 하는지 여부를 개별적으로 체크한다면 catA의 모든 하위 카테고리를 조사해야 되고 10만 * 0.1초 이므로 약 2시간 30분이 걸린다. 따라서 이 방법도 답이 될 수 없다.

당시에 약 2달간 생각했지만 위 문제를 풀지 못했다. 그러나 수학은 풀지 못했다고 해결이 되는게 아니라 풀 수 없다고 증명할 수 있어야 하는데 그냥 카테고리만 읽는데만 5초가 걸리는데 어떻게 2초 이내의 응답속도가 가능할까? 라는 근거는 충분한 증명이 되지 못했다.

문제는 하위 카테고리를 "모두" 읽지 않고 상위 카테고리가 보여줄지 여부를 결정 할 수 있느냐의 여부였고 2달이 지난후 약 3일 걸려서 복잡 다양한 설정 Case를 분석해서 0.6초 정도의 반응속도를 보여주는 200라인 정도의 SQL을 짰는데 구문이 대단히 복잡하다거나 그런건 아닌데 발상이 어렵다. 대신 SQL에서 모든 리스트를 구해서 넘겨주므로 프로그램에서는 그냥 보여주기만 하면 된다. 위 문제는 생각하기에 따라서 아주 재미있으므로 흥미가 있다면 직접 작성해보는 것도 도움이 된다.



위 얘기를 하는 것은 성능 문제에 있어 기준과 목표는 가장 중요한 행동강령이라는 사실을 다시 한번 강조하기 위해서이다. 앞의 로그분석 프로그램의 예를 다시 얘기하면 로그가 약 100만건이라면 이를 일단 분석하는 비지니스 로직은 제쳐두고 일단 100만건을 읽는데 보통의 서버급에서 2-3초가 걸린다. 좀더 좋은 서버라면 1초 정도면 가능하다. 이때 이 기준은 매우 중요한 판단 기준이 된다.

만약 해당 프로그램이 100만건을 분석하는데 약 3분이 걸린다면 과연 그 비지니스 로직이 2분 57초가 걸릴만큼 복잡한가에 대해서 자문해볼 수 있는 여지가 있기 때문이다. 그렇지 않다면 내가 해당 데이타를 읽는 방법이 잘못됐거나 비지니스 로직이 잘못됐다는 것을 뜻한다.

따라서 일단 100만건을 분석하는데 단순히 읽는 속도인 2초에 한없이 가깝게 가는게 일차 목표가 된다. 그러나 분석 데이타가 1억건이라면 얘기가 달라진다. 단순히 곱셉을 하면(실제로는 단순곱셈이 아니지만) 1억건을 모두 읽는데 약 200초가 걸리며 200초가 수용할 수 있는 수준이 아니라면 이때는 기존의 단순히 읽는 방법으로는 이미 답이 없음을 뜻한다.

따라서 데이타 분산을 했을때의 읽기 속도, 집계 테이블을 유지했을때의 속도와 단점 등등을 미리 측정해서 알고 있다면 이러한 경우 해답을 찾는 문제가 훨씬 더 단순해진다. 만약 이러한 기준을 모른다면 말 그대로 보물이 나올때까지 삽으로 닥치는 대로 이곳저곳 땅을 파는 것과 같다. 아마도 겨우 동전 몇개를 발견하는 것에 그치겠지만 말이다.




'IT 이야기' 카테고리의 다른 글

아키텍트의 역할2  (1) 2012.06.19
아키텍트의 역할  (0) 2012.06.19
아키텍트 vs 트러블슈터 vs 컨설턴트  (0) 2009.03.26
튜닝  (0) 2009.03.26
interface  (0) 2009.03.25
Posted by bleujin