현재 진행 중인 교육 시리즈에서 매번 여러 가지 질문이 나오는데, 아래에 각 질문에 대한 답변을 나열해 보았습니다!
Couchbase 103 - 문서 모델링
Q: 문서를 JSON이 아닌 XML로 저장할 수 있다고 가정했을 때, Couchbase에서 이 두 가지 방식에 대해 어떻게 생각하시나요?
A: 문자열을 저장할 수 있듯이 XML도 저장할 수 있습니다. 그러나 JSON처럼 점 표기법을 사용할 수 있도록 XML 서버 측을 파싱하지 않기 때문에 인덱싱 및 쿼리를 위한 맵-리듀스 뷰를 수행하기는 어렵습니다. XML은 자바스크립트의 기본 기능이 아닙니다. XML 구문 분석을 위한 E4X 확장은 자바스크립트 맵 함수를 처리하기 위해 Couchbase에서 사용하는 V8 엔진의 일부가 아닙니다.
질문: Couchbase에서 대량 삽입/업데이트를 할 수 있나요?
A: 일반적으로 Couchbase에서 다른 CRUD 작업을 할 때와 같은 방식으로 SDK를 사용합니다. 디스크에서 JSON 파일을 읽을 수 있는 명령줄 도구인 cbtransfer가 있습니다. 빠른 VM(예: Ruby 대신 Java/C/C++/Go)을 사용하고 많은 양의 문서인 경우 작업을 병렬화할 것을 권장합니다.
질문: 여러 문서 패턴을 사용할 때 '관계형' 패턴으로 돌아가지 않나요?
A: 반드시 그런 것은 아니고, 우려 사항을 분리하는 것에 가깝지만 여전히 조인은 없습니다. 사용자 행동을 별도의 문서로 분리하는 것일 뿐 정규화가 아닙니다. 이 둘 사이에는 큰 차이가 있습니다. 많은 사용자 행동에 대해 하나의 문서를 사용하는 대신, 특정 사용자 행동에 대한 키별로 문서를 분리하고 여전히 비정규화된 데이터를 저장하는 것입니다. 테이블 대신 JSON을 사용하고 정규화된 데이터 대신 비정규화된 데이터를 사용하는 이유는 데이터를 배포할 수 있기 때문입니다. 정규화된 테이블을 사용하면 데이터를 배포하고 조인을 계산하는 것이 매우 어렵습니다. 여러 서버에서 큰 테이블을 쉽게 분할할 수 없으며, 분할할 경우 컴퓨팅 조인을 하려면 여러 서버에 걸쳐 큰 테이블을 집계해야 하므로 계산 및 데이터 전송량이 많아지고 많은 쿼리를 견딜 수 없게 됩니다. 물론 분산된 RDBMS 상황에서의 CRUD 작업 로직도 마찬가지로 복잡합니다.
질문: 경험상 데이터베이스에서 동일한 이름으로 항목이 참조되는 것을 최대한 보장하는 가장 좋은 방법은 무엇인가요?
A: 우선, 키에 불변 특성을 선택하면 JSON 문서 내의 참조 ID를 업데이트할 필요가 없습니다. 다른 문서에서 참조된 키를 변경해야 하는 경우에는 키 변경 후 해당 문서를 찾아서 업데이트하는 보기를 만들어야 할 것입니다.
Q: CB의 장점 중 하나로 앱과 데이터가 DB에 저장되기 때문에 임피던스 불일치가 발생하지 않는다는 점을 꼽으셨어요. W앱이 변경되어 다른 페이지에서 다른 데이터가 사용되거나 여러 앱이 동일한 데이터를 보고 있는 경우(더 나쁜 경우)에도 마찬가지입니다. 그렇다면 RDBMS 접근 방식이 더 낫지 않을까요? 아니면 여전히 옹호하시겠습니까? 그렇다면 그 이유는 무엇인가요?
A: JSON 문서의 다른 부분이 다른 페이지에서 사용되는 경우 문제가 되지 않습니다. 서로 다른 사용자 동작이 동일한 문서를 수정하여 경합 조건이 발생할 가능성이 있는 경우 CAS 작업을 사용하거나 문서를 여러 개로 분리할 수 있습니다. 여러 앱이 동일한 데이터를 보고 있는 경우에도 문제가 되지 않습니다. 이러한 앱이 데이터에 대한 쓰기를 수행할 수 있는 타사 앱인 경우, 형식과 데이터 무결성을 유지하기 위해 JSON 키의 서식 지정 및 기타 유효성 검사를 수행할 수 있도록 API를 통과하는 것이 가장 좋습니다. RDBMS 시스템에서는 데이터베이스에 의존하여 유형 유효성 검사를 수행하지만, Couchbase를 사용하면 이 책임이 DB에서 앱 계층으로 이동합니다. 이는 매우 쉽게 해결할 수 있으며, 타사 앱이 직접 데이터베이스에 연결하지 않고 API를 통해 처리하도록 하는 것이 더 나은 방법입니다. 동일한 데이터베이스와 상호 작용하는 여러 애플리케이션이 있는 경우에도 SOA를 사용하는 것이 더 나은 아키텍처일 수 있습니다.
질문: 특정 문서의 크기(예를 들어 디스크의 바이트 단위)를 어떻게 '찾나요'?
A: 개별 문서에 대해 이 작업을 수행할 수 있는 방법은 없습니다. 문서는 Snappy를 사용하여 디스크에서 압축되므로 클라이언트 측의 문서 크기(압축되지 않은 원시 json)에서 평균 압축률을 구한 다음 40%로 임의로 압축하여 디스크 공간을 확인할 수 있습니다. 또는 항목(문서) 수를 보고 디스크 사용량을 구하여 평균을 구한 다음 이를 나누어 디스크의 평균 문서 크기를 확인할 수 있습니다.
질문: 문서 업데이트 타임스탬프와 같은 json 항목을 수동이 아닌 다른 방법을 사용하여 '업데이트'하는 방법은 무엇인가요?
A: json 문서 내에서 개별 json 키를 업데이트하는 자동 메커니즘은 없습니다. 전체 문서는 CRUD 작업을 통해 앞뒤로 전달됩니다. 새 값으로 json 키를 업데이트하는 경우 타임스탬프 업데이트를 포함하되 이에 국한되지 않고 클라이언트 측에서 수행됩니다.
Q: 데이터 업데이트는 어떻게 처리하나요, 시간이 많이 걸리지 않나요?
A: 단순히 JSON 문서를 업데이트하는 것을 언급하는 것이라면 당연히 밀리초 미만의 교체 작업은 말씀하시는 것이 아닐 것입니다. 따라서 이 질문에 답하기 위해서는 JSON 문서 구조를 다른 문서 구조로 마이그레이션하는 것을 언급하고 있다고 가정해야 했습니다. 예를 들어 사용자 문서에 몇 개의 json 키가 있고 이를 추가/제거/변환하려는 경우입니다. 웨비나에서 언급했듯이 여러 가지 방법으로 마이그레이션할 수 있습니다. 물론 두 가지 형태가 있는데, 하나는 배치/크론 작업 스타일로 문서를 반복하여 변환/변형을 수행하는 것입니다. 이 작업에 걸리는 시간은 데이터 세트의 크기, Couchbase 클러스터에 있는 머신의 수, 변환을 수행하는 머신의 수에 따라 달라집니다. 따라서 "시간이 많이 걸린다"는 것은 상대적인 평가입니다. 이를 처리하는 다른 방법은 "온디맨드", 즉 애플리케이션의 일반적인 사용량에 따라 문서를 검색할 때 문서를 변환하는 것입니다. 예를 들어 사용자가 로그인을 수행할 때 사용자 문서를 검색하여 변환한 후 Couchbase에서 대체할 수 있습니다.
Q: Jasdeep이 언급하지 않은 것은 데이터가 메모리에 있어야 한다는 것입니다(적어도 속도를 정당화하기 위해). 그렇다면 풍부한 SQL을 지원하는 관계형 인메모리 데이터베이스에 비해 어떤 이점이 있나요? 그들은 또한 좋은 클러스터링을 수행합니다 ...
A: 인메모리 관계형 데이터베이스의 수직 확장 노드를 하나만 사용하는 경우, 해당 컴퓨터의 수직 확장에 제한을 받게 됩니다. 서버가 두 대 이상이고 조인(특히 분산 조인)을 하면 순전히 물리적인 이유로 인해 디스크 바운드 조인과 유사한 성능 병목 현상이 발생합니다. 많은 대규모 테이블에서 네트워크 연결을 통해 조인을 수행하는 경우, 어떤 방식으로 조인을 수행하든 속도가 느려지고, 자주 수행하면 전체 성능이 대규모로 저하됩니다.
질문: 사용자에 대한 카운터 ID 패턴에서 사용자 수 값은 어디에 위치하나요?
A: "user::count"와 같이 키가 있는 간단한 양의 정수 값입니다. 카운터만 증가시켜야 합니다! 그리고 집합 대신 덧셈 연산을 사용하면 논리 오류가 발생하면 이를 감지할 수 있습니다.
질문: 역(반전) 인덱스 MapReduce는 어떻게 수행하나요?
A: 이 주제는 Couchbase 104: 보기 웨비나에 대한 자세한 내용입니다. 그러나 역 인덱스는 맵 리듀스에서 까다롭기 때문에 발생 시점에 대한 태그를 생성한 다음 해당 태그에 대한 범위 쿼리를 통해 생성할 수 있습니다.
질문: 언제 문서를 여러 개의 문서로 분할하나요?
답변: 일반적으로 문서를 여러 문서로 분할하는 데는 세 가지 이유가 있습니다. 첫째는 특정 사용자 작업이 다른 사용자 작업에 의해 수정될 수 있는 문서를 수정할 수 있기 때문입니다(둘 다 빈번하게 발생합니다). 여러 사용자가 특정 시점에 동일한 문서를 수정할 수 있는 경우(또는 거의 동시에 수정할 수 있는 경우) 이는 "경쟁 조건"이며, 해당 문서를 분리하고 CAS 작업을 사용해야 합니다. 두 번째는 하나의 문서가 "부풀려지거나" 매우 큰 경우로, 이 경우 코드 및 코드 유지 관리 측면에서 문서를 분할하는 것이 더 효율적일 수 있습니다. 세 번째는 객체/클래스의 특정 컴포넌트를 표현하기 위해 원자 카운터를 사용하는 경우입니다. 물론 이것은 기본 JSON 문서와는 독립적인 자체 키-값 쌍이어야 합니다.
질문: 선박 ID와 위도/경도가 있고 두 매개변수로 검색하려는 경우 어떻게 검색하나요?
A: 이 경우 지리적 좌표는 소수점 이하 세 자리까지 전제하기 때문에 어떤 키 패턴도 도움이 되지 않습니다. 하지만 원하는 종류의 정보를 검색할 수 있는 두 가지 옵션이 있습니다. 일반 보기 맵/축소도 만족스럽지만, 지리적 위치가 포함된 특수 보기도 있습니다. 이를 통해 경계 상자 검색 및 기타 고급 지리적 기술을 사용할 수 있습니다. Couchbase 104: 뷰 웨비나에서는 이에 대해 구체적으로 설명하지 않지만, 더 자세히 알고 싶거나 Geo 및 Couchbase와 관련하여 도움이 필요하시면 저에게 문의해 주세요.
질문: 사용자 수를 세는 것이 큰 난수나 GUID보다 사용자ID를 만드는 데 더 좋은 방법(.incr() 함수 사용)인가요? 초당 많은 사용자가 새로 생길 수 있는 경우, 충돌 확률이 매우 낮은 큰 무작위 키보다 카운팅이 더 많은 충돌을 일으키지 않을까요?
A: 원자 카운터는 원자 단위이므로 (단일 클러스터에서) 순서대로 실행될 것으로 기대할 수 있습니다. 앱 서버가 모두 해당 클러스터를 가리키고 있다면 모든 서버가 동시에 incr 작업을 실행하는 것입니다. 그러나 원자적이기 때문에 각 앱 서버에서 연산을 실행할 때마다 새로운 정수가 생성되므로 모두 고유합니다. decr(감소) 연산을 사용하지 않는다면 괜찮습니다. 데이터 센터가 여러 개 있고 XDCR(데이터 센터 간 복제)을 사용하는 경우에는 조금 더 복잡해집니다. 이러한 경우에는 카운터 앞에 (직접 생성한) 데이터 센터 ID를 접두사로 붙이는 것이 가장 좋습니다.
질문: Rails에서 카우치베이스 모델 젬을 사용하고 있는데, Object.find() 메서드를 사용하거나 JSON이 아닌 refdoc에 액세스/증가하는 등 Rails 패러다임에서 카운터 ID와 키의 이름 간격에 대해 이해하는 데 어려움을 겪고 있습니다. 모델의 버킷에서 증분을 포함하여 카운터 ID와 Rails를 혼합 및 일치시키는 예제를 게시해 주시겠어요?
답변: 블로그보다 더 좋은 자료가 필요할 것 같습니다. 요점을 정리하여 여기에 링크를 게시할 방법을 찾아보겠습니다. 조금만 시간을 주세요. :)
질문: 서로 다른 ID에 대해 여러 개의 문서를 가져올 수 있는 방법이 있나요? 제가 생각하는 예는 사용자 문서에 매핑하려는 Facebook 사용자 ID 목록이 있는 경우입니다. 여러 개의 Facebook ID를 전달하여 문서 배열을 가져올 수 있는 방법이 있나요?
A: 이를 위해 모든 SDK에 멀티겟 연산이 있습니다. 결과 배열에서 문서가 존재하지 않는 경우 이를 나타내는 널/닐 요소가 있습니다(Java에서는 다른 반환값이 있을 수 있으므로 확인해야 하지만 Java에도 있는 것으로 알고 있습니다).
질문: 복합 키를 사용할 수 있는 유일한 방법은 보기인가요? 보기 없이도 이러한 종류의 기능을 사용하기 위해 get 또는 setup 키와 함께 사용할 수 있는 방법이 있나요? https://www.couchbase.com/blog/understanding-grouplevel-view-queries-compound-keys
A: 이것은 Couchbase 104: 보기 및 인덱싱에 대한 주제이지만 기본적으로 키는 원하는 모든 것이 될 수 있으므로 기술적으로는 복합 키를 가질 수 있습니다(복합을 정의하는 방법에 따라 다름). 그러나 참조하는 링크는 뷰-색인 쿼리에 초점을 맞추고 있으며, 뷰-색인에도 인덱스 키, 즉 B+ 트리를 구축하는 키가 있습니다. 이 경우 키의 일부를 가져와 ","(쉼표) 구분 기호에 따라 분할하는 것입니다. 여기서 말하는 것은 전혀 다른 문제입니다.
질문: 현재 뷰를 사용하여 Facebook 사용자 ID를 조회하여 자체 앱 사용자 ID를 얻고 있습니다. 실제로 제안하신 조회 패턴을 사용하면 뷰를 사용하는 것보다 더 빠를 수 있을 것 같은데요?
A: 예, 물론 상황에 따라 다릅니다. 여기에서도 규모도 중요한 요소입니다. 클러스터에 노드가 많을수록 View 쿼리 분산 수집 프로세스가 더 넓어집니다. 즉, View 쿼리 결과 측면에서 더 많은 노드로 분산하고 수집해야 합니다. 따라서 물리적으로만 보면 조회가 더 빨라집니다. 단점은 더 많은 문서를 추가하게 되므로 RAM/메타데이터 요구 사항이 더 많아진다는 것이지만, 결과적으로 엄청난 확장성을 얻을 수 있습니다. 왜냐하면 facebook_id->app_user_id에 대한 영구 연결을 통해 한 서버로 이진 연산을 한 다음 영구 연결을 통해 동일 또는 다른 노드로 다시 이진 연산을 하는 것이 모든 노드로 뷰 쿼리를 분산하여 결과를 수집하는 것보다 항상 빠르기 때문입니다. 뷰는 키 패턴보다 유연하기 때문에 매우 강력하지만, 이 시나리오에서는 속도를 고려해야 합니다.
Q: 뷰에 오래된 옵션이 있어 두 개의 개별 가져오기 호출에 비해 불변성 문제를 완화할 수 있는 것처럼 보입니다. 실제로 잘 관리되는 하나의 뷰에 비해 DB에 두 번 호출하는 것이 더 빠른가요? 뷰에 대한 두 번의 호출과 한 번의 호출이라는 규모에 대해 걱정할 수 있습니다.
A: 일관성 문제는 완화되지 않습니다. 인덱스는 Couchbase에서 항상 궁극적으로 일관성을 유지하지만(우리가 직접 개선하기 전까지는), 위의 질문/답변을 참조하고 Couchbase 104: 보기 및 인덱스를 시청하세요! 여러분의 생각이 바뀔 수도 있습니다.
질문: 데모하는 예시에서는 단일 문서와 그 속성/속성을 JSON으로 처리하는 것에 대해 논의하고 있습니다. 여러 개체/문서에서 통계/카운트를 얻으려는 경우 어떻게 처리하나요? 예: a) 즐겨 찾기 색상이 파란색인 사용자를 모두 가져올 수 있나요? b) 비자 신용카드를 사용하는 모든 사용자를 확보할 수 있나요?
A: 이러한 쿼리를 수행할 수 있도록 모든 문서 데이터 요소를 수집하고 인덱스 구조에 넣으려면 뷰와 맵/축소 기술이 필요합니다. 이것은 키 패턴으로는 실제로 할 수 없는 일의 좋은 예이며, VIews 및/또는 Elastic Search 통합이 필요합니다. 이러한 시나리오에서는 두 가지 솔루션을 모두 사용할 수 있습니다.
Q: *필수* 조회/맵 축소? 두 번째 문서 u::uuid { email:user@domain.com }를 저장한 다음 u:user@domain.com 에 액세스하려면 어떻게 해야 하나요? 이 작업은 언제 view/map-reduce를 통해 수행해야 하나요?
답변: 이 질문은 조회 패턴이 없을 때 어떤 일이 발생하는지에 대한 질문이었습니다. 무작위로 생성된 문서 키가 있는 경우에는 일반적으로 다른 JSON 속성을 통해 문서를 조회할 수 있도록 색인을 만들어야 합니다. 그러나 조회 패턴을 만들면 이중 조회를 수행하여 기본 문서를 가져올 수 있으며, 첫 번째 조회는 알려진 정보(이메일 주소)를 기반으로 하고 값은 기본 문서의 무작위로 생성된 ID가 되며, 두 번째 조회는 해당 값을 사용하여 기본 문서를 가져옵니다.
질문: 알림 문서를 쿼리하는 가장 좋은 방법은 무엇인가요?
A: 앞서 언급한 예에서 키 자체는 알림을 전달해야 하는 시점에 대한 타임스탬프입니다. 다음은 이 패러다임을 설명하는 데 도움이 되는 요점입니다: https://gist.github.com/scalabl3/7235173
질문: 정규화의 주된 목적은 업데이트 이상을 방지하는 것인데, 모든 프로그램에는 버그가 있기 때문에 카우치베이스에서 이러한 업데이트 이상을 방지하고 개발자가 '제대로' 처리하는 것에 의존하지 않아도 되는 방법이 있나요?
A: 정규화의 주된 목적은 디스크 공간을 절약하는 것이었습니다. 단일 디스크에서는 불가능했던 약 1GB의 디스크 저장 공간을 확보하기 위해 SQL을 만들었을 때를 돌이켜보면 약 $700,000이었습니다. 정규화는 여러 위치에서 동일한 데이터를 가리키고, 그 데이터를 집계하거나 조인하여 다시 조립함으로써 데이터 중복을 줄였습니다. 정규화된 체계에서 데이터를 삽입, 업데이트 및 삭제하는 경우, 어느 한 부분이라도 실패하면 데이터 무결성이 무너진다는 사실을 실무에서 금방 알게 되었습니다. 트랜잭션이 필요한 것은 바로 정규화 때문입니다! 정규화되지 않은 형태, 즉 집계 형태에서는 트랜잭션이 훨씬 덜 필요하지만, 낙관적 동시성과 비관적 동시성을 통해 단일 문서 트랜잭션을 가질 수 있으며 2단계 커밋을 사용할 수 있습니다.
어떤 시스템에서도 개발자가 '실수'를 해도 완벽하게 보호할 수 있는 방법은 없습니다. 그 부분을 알아내면 알려주세요 :).
질문: 카운터 ID 패턴과 조회 패턴을 결합하는 것이 유리하지 않을까요?
A: 가장 확실한 장점은 해당 유형의 문서에 대한 총 개수를 알 수 있다는 것입니다. 임의의 ID를 사용하여 조회하면 총 개수를 알 수 없습니다. 총 개수가 있으면 총 개수까지 일괄적으로 키를 생성하여 컬렉션을 반복할 수 있습니다. 그렇지 않으면 컬렉션을 반복하려면 뷰(인덱스)를 만들어야 합니다.
Q: RDBMS용 인덱스가 아닌 상처도 선형 성능을 제공하나요?
A: 하나의 서버에 살면서 수직적으로 확장할 수 있다면 뛰어난 성능을 제공합니다. 트래픽을 두 개 이상의 서버로 분할해야 할 때 모든 문제가 발생합니다. 2000년대 초반에 관계형 데이터베이스가 대량의 트래픽을 처리할 수 있는 기능이 '폭발적으로' 증가하면서 새로운 데이터베이스가 탄생한 것이 바로 이 때입니다. 멤캐시드 같은 것이 먼저 등장했고, 그다음에는 NoSQL이 등장했습니다.
Q: 서버 측에서 이러한 패턴에 기반한 추상적인 기능으로 CB를 확장하지 않는 이유는 무엇인가요? 지금은 패턴을 클라이언트 측에서 개별적으로 구현해야 합니다.
A: 좋은 아이디어이고 실제로 어느 정도 타당성이 있다고 생각하지만, 다른 한편에서는 프로그래밍 방식으로 제어해야 한다는 주장도 있습니다. 새로운 기능을 추가하자마자 사람들은 그것을 손대고 싶어 하거든요.)
카우치베이스 103에 참석해 주셔서 감사합니다!
Jasdeep
확장 가능
[...] 금주의 블로그 게시물 #1: 카우치베이스 103: Q&A [...]