와우, 이것은 간단한 질문이며, 가능한 많은 답변이 있습니다. 질문의 더 명확한 부분은 데이터베이스와 직접 또는 웹 서비스를 통해 인터페이스하는 것이 더 확장 가능한지 묻습니다. 그 대답은 간단합니다. 데이터베이스를 직접 쿼리하십시오. 웹 서비스를 거치면 방화벽 뒤에서 (대부분의) 코드 작동에 완전히 불필요한 대기 시간이 추가됩니다. 예를 들어 웹 서비스는 일부 구성 요소가 요청을 받고, 역 직렬화하고, DB를 쿼리하고, 응답을 직렬화하고 반환해야합니다. 따라서 코드가 모두 방화벽 뒤에서 작동하는 경우 문제를 해결하고 DB를 직접 쿼리하십시오.
그러나 웹 사이트를 확장 가능하게 만드는 것은 처음 제기 한 질문을 넘어선 것입니다. 여기서 탄젠트를 타면 저를 용서해주십시오.하지만 특히 페이스 북을 언급 한 것을 고려하면 도움이 될 것이라고 생각했습니다.
Brad Fitzpatrick (LiveJournal의 설립자이자 현재는 Google의 창립자)가 구축 한 작업과 도구에 대해 읽어 보는 것이 좋습니다. Six Apart에서 그와 함께 일할 때, 그에게서 배운 것들과 LiveJournal의 아키텍처에 대한 확장 성이 매우 뛰어납니다.
넓은 데이터베이스 테이블과 달리 좁은 데이터베이스 테이블을 사용하십시오 . 어떤이에 대한 매력이었다 쉽게이었다 시스템 생성 된이 아키텍처, 동기가 무엇을 학습했다 신속을업그레이드되었습니다. 넓은 테이블 또는 각 필드 또는 속성이 테이블의 열인 테이블을 사용하는 경우 데이터베이스 스키마를 업그레이드 할 때 (예 : 새 열 추가) 시스템은 스키마 동안 테이블을 잠 가야합니다. 변경이 구현됩니다. 대규모로 운영 할 때 데이터베이스 스키마를 간단히 변경하면 데이터베이스가 크게 중단 될 수 있습니다. 분명히 짜증나. 반면에 좁은 테이블은 단순히 개체와 관련된 각 개별 속성을 데이터베이스에서 단일 행으로 저장합니다. 따라서 데이터베이스에 새 열을 추가하려면 잠금이 아닌 작업 인 레코드를 테이블에 삽입해야합니다. 자, 그것은 약간의 배경입니다. LiveJournal과 같은 작업 시스템에서이 모델이 실제로 어떻게 변환되는지 봅시다.
개인의 블로그에 마지막 10 개의 저널 항목을로드하고 각 저널 항목에 10 개의 속성이 있다고 가정 해 봅시다. 일반적인 넓은 테이블 레이아웃에서 각 속성은 테이블의 열과 관련이 있습니다. 그런 다음 사용자는 테이블을 한 번 쿼리하여 필요한 모든 데이터를 가져옵니다. 쿼리는 10 개의 행을 반환하고 각 행은 필요한 모든 데이터를 갖습니다 (예 : SELECT * FROM 항목 ORDER BY 날짜 제한 10). 좁은 테이블 레이아웃에서는 상황이 약간 다릅니다. 이 예에는 실제로 두 개의 테이블이 있습니다. 첫 번째 테이블 (표 A)에는 항목의 ID, 작성자의 ID, 항목의 날짜 등으로 검색하려는 간단한 기준이 저장됩니다. 두 번째 테이블 그런 다음 (표 B)는 항목과 관련된 모든 속성을 저장합니다. 이 두 번째 테이블에는 entry_id, key 및 value의 세 열이 있습니다. 테이블 A의 모든 행에 대해 테이블 B에 10 개의 행이 있습니다 (각 속성에 대해 한 행). 따라서 마지막 10 개의 항목을 가져 와서 표시하려면 11 개의 쿼리가 필요합니다. 첫 번째 쿼리는 항목 ID 목록을 제공 한 후 다음 10 개의 쿼리는 첫 번째 쿼리에 반환 된 각 항목과 관련된 속성을 가져옵니다.
"신성한 몰리!" "어떻게 지구상에서 어떻게 확장 할 수 있을까요?" 완전히 반 직관적 인 권리? 첫 번째 시나리오에서는 데이터베이스 쿼리가 하나 뿐이지 만 두 번째 "확장 성있는"솔루션에는 11 개의 데이터베이스 쿼리가 있습니다. 말이되지 않습니다. 그 질문에 대한 답은 전적으로 다음 글 머리 기호에 의존합니다.
memcache를 자유롭게 사용하십시오. 모르는 경우 memcache는 분산 된 상태 비 저장 지연 시간이 적은 네트워크 기반 캐싱 시스템입니다. Facebook, Google, Yahoo 및 지구상의 모든 인기 있고 확장 가능한 웹 사이트에서 사용됩니다. 좁은 테이블 데이터베이스 설계에 내재 된 데이터베이스 오버 헤드를 상쇄하기 위해 Brad Fitzpatrick이 부분적으로 발명했습니다. 위의 # 1에서 설명한 것과 동일한 예제를 살펴 보도록하겠습니다. 이번에는 memcache를 소개하겠습니다.
사용자가 처음 페이지를 방문하고 캐시에 아무것도 없을 때 시작합시다. 페이지에 표시하려는 10 개의 항목 ID를 리턴하는 테이블 A를 조회하여 시작합니다. 그런 다음 각 항목에 대해 데이터베이스를 쿼리하여 해당 항목과 관련된 속성을 검색 한 다음 해당 속성을 사용하여 코드와 인터페이스 할 수있는 개체 (예 : 개체)를 구성합니다. 그런 다음 memcache에서 해당 오브젝트 (또는 해당 오브젝트의 직렬화 된 양식)를 숨 깁니다.
두 번째로 동일한 페이지를로드하면 같은 방법으로 시작합니다. 표시 할 항목 ID 목록에 대해 테이블 A를 쿼리하면됩니다. 각 항목에 대해 먼저 memcache로 이동하여 "캐시에 항목 #X가 있습니까?"라고 말합니다. 그렇다면 memcache는 입력 객체를 반환합니다. 그렇지 않은 경우, 데이터베이스를 다시 조회하여 해당 특성을 페치하고 오브젝트를 구성한 후 memcache에 숨겨야합니다. 대부분의 경우 두 번째로 누군가 동일한 페이지를 방문하면 하나의 데이터베이스 쿼리 만 있고 다른 모든 데이터는 memcache에서 바로 가져옵니다.
실제로 대부분의 LiveJournal에서 발생하는 결과는 대부분의 시스템 데이터, 특히 덜 휘발성 인 데이터가 memcache에 캐시되었으며 좁은 테이블 스키마를 지원하는 데 필요한 데이터베이스에 대한 추가 쿼리가 모두 완전히 오프셋 된 것입니다.
이 디자인은 모든 친구와 관련된 게시물 목록을 스트림으로 모으는 것과 관련된 문제를 훨씬 쉽게 "벽"으로 해결했습니다 .
다음으로 데이터베이스 파티션을 고려하십시오. 위에서 논의한 모델은 또 다른 문제점을 나타내며, 좁은 테이블은 매우 크거나 길다. 그리고 해당 테이블에있는 행이 많을수록 다른 관리 작업이 어려워집니다. 이를 상쇄하기 위해 테이블을 어쨌든 분할하여 테이블 크기를 관리하는 것이 합리적 일 수 있으므로 사용자 클러스터는 한 데이터베이스에서 제공되고 다른 사용자 클러스터는 별도의 데이터베이스에서 제공됩니다. 이렇게하면 데이터베이스에로드가 분산되고 쿼리가 효율적으로 유지됩니다.
마지막으로 멋진 인덱스가 필요합니다. 쿼리 속도는 데이터베이스 테이블의 인덱스 수준에 크게 좌우됩니다. 나는 건초 더미에서 바늘을 더 효율적으로 찾는 거대한 카드 카탈로그 시스템과 비슷하다는 것을 제외하고는 색인이 무엇인지에 대해 너무 많은 시간을 소비하지 않을 것입니다. mysql을 사용하는 경우 느린 쿼리 로그를 켜서 수행하는 데 오랜 시간이 걸리는 쿼리를 모니터링하는 것이 좋습니다. 레이더에 쿼리가 나타나면 (예를 들어 속도가 느리므로) 속도를 높이기 위해 테이블에 어떤 인덱스를 추가해야하는지 파악하십시오.
"이 위대한 배경에 대해 감사하지만, 성스러운 일이다. 그것은 내가 작성해야 할 많은 코드이다."
반드시 그런 것은 아닙니다. memcache와의 인터페이스가 정말 쉬운 많은 라이브러리가 작성되었습니다. 또 다른 라이브러리는 위에서 설명한 전체 프로세스를 체계화했습니다. Perl의 Data :: ObjectDriver는 그러한 라이브러리입니다. 다른 언어의 경우 자체 연구를 수행해야합니다.
이 답변이 도움이 되었기를 바랍니다. 내가 발견 한 것보다 자주 발견 한 것은 시스템의 확장 성이 코드에 따라 점점 줄어들고, 건전한 데이터 저장 및 관리 전략 / 기술 설계에 점점 더 많이 발생한다는 것입니다.