대용량도 아닌 거대 용량 아키텍처 끝 무렵에 이베이 시스템을 간단하게 평했다. 이에 5throck님께서 거대 용량 아키덱처 설계 시의 데이터베이스의 역할라는 글로 답변해주셔서 좀더 많은 이야기를 풀어나갈 기회가 생겼다.
이베이는 극단적인 상황에서 극단적인 조치를 취한 셈인데, 과연 올바른 선택이었냐는 데 의문이 생긴다. 성능을 위해서라고 하지만 데이터베이스의 핵심 기능을 모조리 쓰지 못하는 상황은 지나친 게 아닌가 싶다.
이베이 아키텍쳐요? DB를 SAM파일 수준으로만 사용하고 그 위에 OR매핑 올린 거 말씀이죠? 몇 년 전에는 몰라도 지금은 시대에 뒤떨어진 아키텍쳐지요. 이런 아키텍쳐의 근본적인 사상은 옛날 TP모니터 시절, 모든 트랜잭션은 TP모니터에서 관리해야 한다는 사상에서 나온 건데 이건 너무 이상적인 것이죠.
출처: 거대용량 시스템 아키텍쳐
데이터베이스의 부담을 줄이고자 계산 작업을 애플리케이션 계층으로 분산시키자는 의도는 알겠다. 다만 데이터베이스 관리 시스템의 최적화 기능을 활용 못하게 된다는 점도 생각해야 한다. MSSQL 같은 제품에선 텍스트 SQL 문보단 저장 프로시저를 쓰는 편이 좋다. 최적화기는 자주 실행되는 명령의 실행계획을 캐싱하는데, 이때 저장 프로시저가 텍스트 SQL문보다 처리하기 월등히 쉽다.
또 하나, 로직을 전부 상위 계층에 이전하면 오히려 성능 개선이 어려워지고 관리상의 난점만 더해질지 모른다는 걸 지적하고 싶다. MSSQL의 경우를 예로 들자면, 성능 개선에 앞서 프로파일링부터 한다. 소규모 사이트라 하더라도 쿼리문을 100여 개 이상 사용한다. 포스의 힘을 믿고 이 놈이 문제다
라고 찍어도 되겠지만, 좋은 결과가 나올지는 보장 못한다. 그러니 어떤 쿼리가 얼마나 자주 실행되는지, CPU를 얼마나 사용하는지, 디스크 입출력은 어느 정도나 되는지 통계부터 낸다. 그리고 나서 못된 놈부터 골라 차례대로 혼내주면 된다. 로직을 전부 상위 계층으로 넘기면 이런 분석 작업이 힘들어진다. 저장 프로시저엔 이름이 있다. 그러니 매개변수 값이 다르게 들어오더라도 똑같은 저장 프로시저라는 걸 쉽게 알 수 있다. 그러나 텍스트 SQL엔 이름이 없다. 어떤 변수가 바뀌면 복잡한 패턴 분석 작업 없이는 똑같은 SQL 구문인지 파악하기 힘들다. (사실상 불가능하다.)
저장 프로시저는 데이터베이스 관리 시스템이 관리한다. 그러니 데이터베이스에서 어떤 작업이 이뤄지는지 파악하기 쉽다. 반면 애플리케이션에 올리면, 누구 자식인지도 모르는 쿼리가 난잡하게 번창하는 사태가 벌어지기 쉽다. 특별히 관련 규정을 마련해서 준수해야 문제가 발생하지 않는다.
소모성, 단순 반복적인 명령이라면 상위 계층에서 처리하는 편이 낫겠지만, 계층화에 지나치게 집착하다 보면 데이터베이스 관리 시스템 특유의 장점을 놓치게 된다.
물론 데이터베이스를 SAM 파일 수준으로만 사용하면 장점도 있다.
다시 말해 Logic 부분은 절대로 데이터 부분에서 처리를 하지 않는다는 것이죠. 이 부분이 전 개인적으로 중요하다고 생각하는데, 그렇게 생각하는 이유는 회사의 사정이나 기타 이유로 인해 데이터베이스를 바꿀 경우 데이터 부분을 단순 마이그레이션만으로 전환을 할 수 있는 장점이 있기 때문입니다.
이 문제에 대해선 어떻게 대답해야 할지 아직 모르겠다. 위험 분산이라는 비즈니스적 계산이 깔려 있기 때문에 틀렸다고 반박하기 쉽지 않다. 상황에 따라 마이그레이션이 무엇보다 중요한 이슈가 될 수도 있기 때문이다. 그래도 소심하게나마 반박해보겠다.
마이그레이션의 장점이 있다고 하더라고 이런 용도로 데이터베이스 관리 시스템을 사용하는 건 낭비가 아닐까? 이럴 바엔 구글처럼 대용량 처리에 적합한 파일 시스템을 하나 더 만드는 편이 나을 듯 하다. 데이터베이스(Database)는 있어도 데이터베이스 관리 시스템(Database Management System)이 빠진 셈이니, 굳이 값비싼 제품을 구입할 필요가 있을까? 직접 개발하면 자사 서비스에 가장 적합한 시스템을 구축할 수 있다. 제품 라이센스 비용을 감안하면 비용도 충당할 수 있을 듯 하다.
마지막으로 데이터베이스의 분산화에 관한 이슈를 생각해봐야겠다.
최근에 일부 데이터베이스의 경우 여러 대의 서버를 이용해서 하나의 데이터베이스를 운영할 수 있다고 하지만, 대규모 시스템의 경우에는 검증되지 않은 기술인 최신의 기술을 함부로 적용하는 것은 아직 시기상조라 생각이 됩니다. 게다가 아직 실제적으로 어느 정도 수준까지 지원이 가능한지 잘 모르겠습니다.
이 문제에 대한 해답은 대용량도 아닌 거대 용량 아키텍처에서 잘 설명되었다고 생각한다. 수백 개의 데이터베이스 인스턴스가 돌아가는 사례는 의외로 많다. 물론 초거대용량 시스템이라면 좀 더 신중하게 설계해야 할 것이다. 용도별로 최대한 데이터베이스를 분리시키고 캐시 데이터베이스를 붙이는 등의 조치가 필요하다. Flickr에도 유사한 설계가 사용되고 있다. 마이스페이스의 사례에서처럼 자체 기술이 필요하게 되겠지만, 그 정도는 어쩔 수 없는 수고라고 생각한다.
알립니다. 이렇게 반박성 글을 쓰긴 했어도, 제가 옳다고 주장하는 바는 아닙니다. 단지 사람들이 계층화 등 몇몇 확장성 이슈에 대해 비판 없이 기존 주장을 답습하는 게 아닐까 하는 생각이 들어서 문제 제기를 해보고 싶었습니다.
아름다운 구현이 최고인가?
"거대 용량 시스템에서의 설계 이슈 from kaistizen" 에 대한 저의 대답입니다. 좀더 최적화 할 수 있겠지만, 그 프로젝트는 성공적이었다. 엔지니어가 할 수 있는 것은 "이렇게 하면 좀더 좋았지 않았을까요?"라고 말해보는 것이 전부일 것이다. DBMS를 파일 시스템 수준으로 사용하는 것은 어떻게 보면 거대한 낭비일 수 있습니다. 하지만 해당 프로젝트를 수행한 집단의…
거대 규모로 가게 되면 정말 절실한 것이 용도별 DB분산과 scale out 입니다. 전세계를 대상으로 서비스를 만든다고 쳤을때, 통짜 DB에 넣으면 스토리지의 물리적인 한계때문에 IO경합을 감당할 수가 없습니다. 그래서 당연히 용도별로 분리를 하게 됩니다.
그런데 인터넷 기업같은 경우 트래픽은 무지막지한데 비즈니스 로직은 계속 변경이 이뤄지는 골때리는 상황입니다. 이베이 아키텍쳐가 상당히 극단적이기는 하지만 이해가 되는 측면도 분명히 있습니다. 마틴 파울러책을 보셨으면 아시겠지만 비즈니스가 거대해지고 변경이 잦은 상황에서 저장 프로시저에 절차지향 로직 들어가면 거의 죽음입니다. 끝없는 if-else의 미로가 우리를 기다리게 되죠. 그리고 수십명 개발자가 동시에 저장 프로시저 개발을 한다치면 중복코드 엄청나게 생깁니다. 저장 프로시저 개발은 비즈니스 로직을 객체단위로 쪼개고, 아키텍쳐 관점에서 적절한 객체를 배치하는 방향으로 사고를 하기가 힘듭니다. 그냥 테이블에 데이터 넣었다 뺐다 하는 수준에 그치고 말죠. 이런 측면에서 보면 로직을 위로 빼는게 충분히 이해가 가죠.
제 생각은 이렇습니다. 용도별 DB분산, 당연히 해야됩니다. 로직은 위로 뺍시다. 제대로 된 OR매핑 레이거 잘 갖춰서요. DB의 join이나 FK, 트랜잭션은 사용합시다. 로직이 위로 올라가니까 SP는 필요가 없죠. 참고로 Inside SQL Server 2005 T-SQL Programming을 보시면 while 루프를 만번 도는데 T-SQL 문과 CLR코드로 도는거 테스트한 사례가 있습니다. 절차형 로직은 확실히 CLR코드가 빠른걸 알 수 있습니다.
SP안쓴다고 하더라도 PreparedStatement로 실행계획 재사용은 할 수 있을겁니다. 그건 큰 문제가 아닙니다. 오히려 지적하신대로 SP단위로 실행계획 제어가 힘들어지고 deadlock이나 blocking을 어떤놈이 유발했는지 파악이 힘들다는 어려움이 클겁니다. 실제로 KT NeOSS는 SP와 미들티어에 로직이 분산되어 있는데, 미들티어단에서 로직이 DB쪽에 문제일으키는 경우 많았다고 합니다. 특히나 분산 트랜잭션 상황에서 n개의 DB서버중 하나에 문제생기면 MSDTC의 성능이 급격히 떨어지고 AP서버랑 DB서버 CPU올라가고 트랜잭션이 길어져서 딴 서버 블러킹 생기고 난리도 아니었다는군요. 근데 이건 수평적 확장성을 얻는 대가라고 생각해야 할 듯 합니다. (쓰다보니 생각이 나는데, SQL2005 의 서비스 브로커가 이런 상황을 우아하게 해결해주기 위해서 나온 솔루션이 아닐까.. 하는 생각이 드는군요.)
OR매핑 레이어가 있으면 또 하나의 큰 장점도 있습니다. 비즈니스 객체와 DB를 분리함으로써 DB를 추가적으로 용도별 분리해도 OR매핑 레이어만 수정해주면 비즈니스 로직은 수정이 필요없다는 사실입니다. 이베이 아키텍쳐 pdf에도 지적한 사항이죠. 급격하게 성장하는 사이트에서는 이게 대단한 장점을 가질걸로 생각이 됩니다. 하지만 OR매핑 레이어를 구성하는거도 난이도가 높은 작업이고 객체 세계와 DB 세계를 모두 깊이 있게 이해하는 사람이 없다는 문제가 있죠.
아, 이건 좀 논외인데요 진위는 확인할 수 없습니다만, 옥션이 이베이 자회사자나요? 이베이에서 돈줄테니까 오라클 자바로 가라고 했는데 이베이 엔지니어가 와서 보고는 이베이보다 더 잘 해놨다~ 하면서 놔두고 갔더랩니다. 옥션도 몇년정에 한바탕 뒤집어 엎으면서(인터넷기업이긴 하지만 확고부동한 1위였기 때문에 시간 여유가 많았죠) 시스템을 잘 분산시켰다고 하네요. 하지만 그때 분산한거는 OR매핑 레이어 개념이 들어간건 아니었고 일단 DB만 쪼갰다고 합니다. 레이어 개념이 들어오고 OR매핑이 조금씩 쓰이기 시작한거는 작년에 닷넷 엑스퍼트 컨설팅을 받고난 후라고 하네요. MS기반 시스템중에 옥션이 잘되어 있는건 사실인 것 같습니다.
“수백 개의 데이터베이스 인스턴스가 돌아가는 사례”는 아마도 설계 시에 데이터베이스를 분리해서 사용하시는 경우를 염두해 두고 이야기하신 것 같은데, 제가 말씀드린 “일부 데이터베이스의 경우 여러 대의 서버를 이용해서 하나의 데이터베이스를 운영할 수 있다”는 이야기는 한 대의 데이터베이스를 여러 대의 하드웨어를 통해서 운영하는 것을 이야기하는 것입니다.(참고적으로 부연설명을 드리자면, 오라클의 RAC 정도의 기능이 될 것 같습니다…)
추신: 제 글에 오타가 있어서 수정을 부탁드립니다. ^^
“대규모 시스템의 경우에는 검증되지 않은 기술은 최신의 기술을 함부로 적용하는 것은 아직 시기상조라 생각이 됩니다.”
—>
“대규모 시스템의 경우에는 검증되지 않은 기술인 최신의 기술을 함부로 적용하는 것은 아직 시기상조라 생각이 됩니다.”
RE maceo: DB의 join이나 FK, 트랜잭션은 사용합시다라고 하셨는데, 사실 성능을 중시하는 사람들 대부분이 Join이나 FK 같은 건 빼야 한다고 주장합니다. 이에 대해선 어떻게 생각하시나요? 데이터 종류에 따라 달라질까요? 아니면 이같은 제약조건은 Integrity를 위해 필수적인 걸까요?
SP에 대해서 말씀하신 바에 공감합니다. SP 열어서 소스 코드를 보다가 현기증 난 적이 한 두번이 아니었죠. 결제 로직 등이 들어가면 고쳐주기도 난감하고, 흑.
하성희 컨설턴트님 세미나에서 옥션 이야기를 들었던 기억이 납니다. 그때부터 웬지 옥션의 데이터베이스는 잘 갖춰져 있을 것 같다는 환상을 품게 됐습니다. ㅎㅎ
RE 5throck: 오타는 수정했습니다.
데이터베이스 분리를 염두에 두고 쓴 글은 아니고, MSSQL의 트랜잭션 복제와 같은 걸 생각했습니다. 마이스페이스의 경우에 수백 개의 인스턴스가 전부 클러스터나 복제 기술로 동기화됐으리라 생각하지는 않았습니다. 단지 Flick 사례를 보더라도 (부분적이라는 단서가 붙지만) 복제 기술이 도움이 될 것 같습니다. 전부 동기화한다는 건 무리겠지만요. (그럼 엔지니어가 할 일이 없을지도…)
제가 오라클은 깊게 다뤄본 적이 없어서 모르겠습니다만, MSSQL(그리고 MySQL)의 트랜잭션 복제나 병합 복제는 어느 정도 보편화되지 않았나 싶습니다. 어느 정도 규모로 쓰일 수 있는지는 모르겠습니다만…… 혹시 이 문제에 대해선 아시는 게 있으면 좀 알려주세요. ^^
RE 5throck: 오라클 10g의 RAC가 상당히 혁신적인 개념이긴 한데 블로그에서 지적하신대로 검증되지 않았다는 문제, MS컨설턴트 얘기에 의하면 엄청난 트래픽이 쏟아지는 사이트에서 8node 이상 붙이면 DB간 메모리 동기화 로드 때문에 성능 향상에 한계가 있다는 문제, DB를 쪼개지 않으면 어차피 IO경합일어나니까 DB분리는 똑같이 필요하고 그렇게 한다면 굳이 RAC까지 갈것도 없이 클러스터 구성 정도만 해도 fail-over가 가능할겁니다. 요즘 기계가 하도 좋아서 scale up 전략으로도 상당히 높은 트래픽을 감당해낼 수 있는게 사실인데요, 그걸로도 한계가 오는 사이트라면 DB분산은 무조건 해야할거고(안하면 IO경합오니까요) 그렇게 되면 RAC의 용도가 모호해지지 않을까 생각됩니다.
RE kaistizen: FK걸어서 성능떨어진다고 하는 사람들은 보통 FK가 걸리는 컬럼에 index를 안걸어서 그런 얘길 하는걸겁니다-_-;;;; SQL서버 기준으로 말씀드리죠. 1:n 관계의 테이블에서 n쪽의 FK컬럼에 인덱스 걸지말고 1쪽의 테이블에서 한 행을 지우는 delete 문의 실행계획을 보세요. 아마 n쪽 테이블을 full scan할겁니다. 인덱스가 있으면 인덱스 타면서 left semi join 으로 끝나죠. exists 체크를 한다는 얘깁니다. FK가 걸려있으니 지워도 되나 당연히 체크해봐야 하겠죠. 이걸 빠르게 하기 위해서 FK컬럼에 인덱스가 필요합니다. 사실 쿼리를 제대로 만고 검수받으면 join 할 때 FK컬럼으로 조인을 하므로 자연스럽게 FK컬럼에 인덱스를 걸게됩니다.
join문제는 음… 무조건 join을 할 수 밖에 없는 요구사항이 있다고 가정하고 이야기를 풀어가야겠습니다. DB보다 join 더 빨리 하는 로직을 만들 능력이 되면 DB에 join을 맡길 필요가 없겠습니다. 그렇지 않으면 DB의 join을 쓰면되죠. 근데 join빨리 하기 위해서 인덱스가 대부분 필요할테니 이거도 사실 튜닝이슈가 될겁니다.
트랜잭션이 좀 골아픈데… 데드락, 블러킹 유발문제 때문에 그렇죠. 근데 게시판 수준의 사이트면 데이터 정합성이 크리티컬하지 않으니까 트랜잭션을 칼같이 지키지 않아도 되겠는데 돈이 오가는 사이트는 무조건 써야한다고 봅니다. 단, OR매핑 레이어에서 트랜잭션 보장을 DB만큼 잘 하게 만들 능력이 되면 DB트랜잭션 안써도 되겠죠. 파울러의 책을 보면 optimistic locking이나 pesimistic locking을 구현하기 위한 기초적인 디자인 패턴들을 소개하고 있는데요 그걸 시작으로 깊이 파면 데이터 정합성을 보장하는 OR매퍼를 만들 수 있을까 모르겠네요. 그럴바엔 hibernate같은걸 쓰는게 나을텐데, 이것또한 난이도가 대단히 높다는 어려움이 있겠네요.
감당해야할 트래픽이나 동원할 수 있는 장비, DB나 객체에 대한 숙련도에 따라 답이 전부 달라지는 문제가 역시 쉽지가 않군요
마이스페이스나 싸이월드나 비슷하다는걸 가정하고 말씀드리면, 그런 유저기반의 사이트는 쪼개기가 상대적으로 쉽습니다. 유저1~10000 까지는 A서버 10001~2000까지는 B서버 이런식으로 쪼개면 됩니다. 하성희 사장님이 저희 회사와 싸이를 모두 컨설팅 하셨는데 싸이는 모델도 구조가 단순한 편이라 쪼개기가 용이한 편이었다고 하더군요. G마켓이나 옥션같은 돈이 오가는 사이트가 정말 쪼개기 어렵죠. 물론 시중 은행이나 증권사, 제조업체들은 시스템을 한번 완전 갈아엎으면서 쪼개는데 인터넷 기업은 그러기도 힘들고-_-;;;;;
그리고 SQL2000의 복제는 테이블이나 컬럼 변경이 빈번하고 트랜잭션이 무지막지한 곳에서는 현실적으로 사용이 불가능합니다. 저희도 대단히 하고 싶은데 MS컨설턴트가 말리는 상황입니다. ㅋㅋㅋ 하지만 SQL2005의 복제는 2000에서 받은 클레임을 많이 반영해서 많이 좋아졌다네요. SQL2005 넘어가면 복제 한번 해보자고 동일한 컨설턴트가 이야기하고 있습니다. MySQL/Oracle은 제가 잘 몰라서… MySQL 은 lock이 테이블 락 밖에 안되는게 맞나요? 그러면 당연히 복제해서 select는 복제 DB에만 날리는 구조로 갈 수 밖에 없겠죠. 플리커도 그런 것 같고… 싸이 재팬도 그렇게 운영한다고 하더군요.
가끔 블로그에 달린 댓글이 순서가 뒤바뀌는 경우가 있어서 손 좀 봤습니다.
RE maceo: 사실 FK 문제에 대해선 저도 똑같은 의견입니다. 단지 무조건 안 된다는 분과 일해본 적이 있어서, 똑같은 일이 벌어지면 어떻게 설득해야 될까 싶어 여쭤봤습니다. ^^
문제를 좀더 확대해서 생각해보면 FK 외에도 Check Constraint 같은 제약조건도 데이터 정합성을 위해 필요하다고 생각하는데요. 데이터 확인 작업은 상위 계층에서 하자라고 하는 분도 많습니다. 개인적으론 ‘그렇게 했다가 1, 2년 지나고 나니 데이터가 엉망이더라’라는 경우를 많이 봐서 동의하지 않습니다만, 이 문제는 어떻게 생각하시나요?
DB와 객체 숙련도도 골 아프죠. 말씀하신대로 둘 다 제대로 이해하는 사람이 많지 않은 상황이니까요. 잘 하는 사람 기준으로 설계해서 유지보수 때 각종 문제에 시달려선 안 될테고, 반대로 못 하는 사람 기준으로 설계하려고 하다 보면 적절한 해결책이 안 나올 수도 있을테니까요. 저는 로버트 L. 글래스씨의 영향을 많이 받아서 항상 유지보수 문제부터 걱정을 하게 됐습니다. 큭.
트랜잭션 복제는 생각보다 한계치가 낮은가 보네요. 약간 실망했습니다. ㅎㅎ
유저 기반의 사이트라고 하면 역시 게임 쪽이 극단적인 사례가 아닐까 싶네요. 여러 개의 서버를 독립적으로 운영하는 경우가 많으니까요. 잘하고 있는 곳을 보면 여기도 나름대로의 노하우가 있는 듯 합니다. 7월부턴 제대로 노하우를 전수 받을 수 있지 않을까 기대하고 있습니다. ㅎㅎ
싸이월드의 사례는 본격적으로 일하기 시작하면 한번 여쭤봐야겠습니다. ^^
체크 조건도 당연히 빡세게 걸어야죠. 테이블에 체크 걸어서 생기는 부하는 SQL을 파싱할때 일부 발생합니다. SQL을 파싱하면서 테이블의 체크 조건을 함께 엮어서 parse tree를 생성하는데 CPU에 조금 부하를 줄 수 있다는게 예상이 되지요. 하지만 체크 때문에 데이터 정합성이 보장되는건 너무 상식적인거니까 말할 필요도 없고 또 하나 좋은 점은 테이블에 체크 조건이 많으면 보다 더 효율적인 실행계획을 생성할 수 있다는 장점이 있습니다. 예를 들어 특정 컬럼에 값이 1~1000 까지만 들어와야 한다고 체크를 걸었을 때, 옵티마이저가 이 사실을 미리 알 수 있기 때문에 실행계획 생성할 때 도움이 됩니다. 정리하자면 약간의 CPU부하는 생기지만 얻을 수 있는 이득이 훨씬 크기 때문에 체크는 미친듯이ㅋ 걸자는게 제 생각입니다. 개발하기 힘들다 그러면 애써 무시합시다. 나중 생각하면 어쩔 수 없어요-_-;;;;; 글고 그 정도 CPU부하는 사실 무시해도 될만한 수준으로 생각이 됩니다. 차라리 엄한 SQL 몇개 튜닝해서 CPU부하 줄이면 체크로 인한 부하 감당하고도 남지 않을까 생각이 되네요
요구사항이나 가용자원에 따라 매번 다른 해결책을 생각해 내야겠습니다만, 그래도 FK나 Check Constraint 같이 ‘이게 정답이다’ 싶은 부분도 있는 듯 합니다. 그래도 다른 사람에게 내보일 수 통계 자료를 구해놓거나 만들어 놓는 편이 좋다라는 생각도 드네요. 100만건 짜리 테이블에 FK를 추가해보고, 빼보면서 측정해본다든가 말이죠. 서로 의견이 맞지 않을 때도 가끔 있고, 아무래도 숫자로 보여주면 좀더 설득력이 있으니까요. ㅎㅎ