계속 지켜보셨다면, 제가 MongoDB 기반 Node.js 애플리케이션을 Couchbase로 변환하는 방법에 대한 몇 가지 튜토리얼을 작성했던 것을 기억하실 것입니다. 여기에는 몽고DB 쿼리 언어를 N1QL로 전환하기 튜토리얼과 몽구스에서 오스만까지 튜토리얼을 참조하세요. 애플리케이션 관점에서 보면 훌륭한 마이그레이션 자습서이지만, MongoDB의 기존 컬렉션을 JSON 파일로 가져오는 방법에 대해서는 설명하지 않습니다.
따라서 이 튜토리얼에서는 MongoDB 수집 데이터를 Couchbase로 가져오는 방법을 살펴보겠습니다. 개발 언어는 크게 중요하지 않지만, Golang은 매우 빠르고 강력하기 때문에 이 작업에 완벽한 후보입니다.
데이터 마이그레이션 스크립트 작성에 대해 고민하기 전에 작업 중인 샘플 데이터 집합을 파악해 보겠습니다. 여기서 목표는 스크립트에서 범용성을 확보하는 것이지만 예제가 있으면 도움이 됩니다.
몽고DB 컬렉션 모델
라는 컬렉션이 있다고 가정해 보겠습니다. 코스 학교가 제공하는 코스에 대한 정보를 담고 있습니다. 이러한 문서 중 하나의 문서 모델은 다음과 같이 보일 수 있습니다:
1 2 3 4 5 6 7 8 |
{ "이름": "바구니 직조 101", "학생": [ "nraboy", "mgroves", "hgreeley" ] } |
각 문서는 등록한 학생 목록이 있는 단일 코스를 나타냅니다. 각 문서에는 ID 값이 있으며 등록한 학생은 일치하는 ID 값을 가진 다른 컬렉션의 문서를 참조합니다.
MongoDB를 설치하면 다음과 같이 액세스할 수 있습니다. 몽고 수출 유틸리티를 사용하세요. 이렇게 하면 모든 컬렉션에 있는 문서를 JSON 데이터 파일로 내보낼 수 있습니다.
예를 들어, MongoDB 데이터베이스에 대해 다음 명령을 실행할 수 있습니다:
1 |
몽고익스포트 --db 예제 --collection courses --out courses.json |
문제의 데이터베이스는 다음과 같습니다. 예제
그리고 우리는 코스
컬렉션을 courses.json. 이 JSON 파일을 열려고 하면 다음과 유사한 데이터가 표시됩니다:
1 2 |
{"_id":{"$oid":"course-1"},"name":"Basket Weaving 101","students":[{"$oid":"nraboy"},{"$oid":"mgroves"}]} {"_id":{"$oid":"course-2"},"name":"TV Watching 101","students":[{"$oid":"jmichaels"},{"$oid":"tgreenstein"}]} |
각 문서는 파일에서 새로운 줄이 되지만 스키마가 모델링된 방식과 정확히 일치하지는 않습니다. MongoDB는 모든 문서 참조를 가져와서 $oid
객체 ID를 나타내는 속성입니다.
그렇다면 이제 우리는 어디로 가야 할까요?
카우치베이스 버킷 모델 계획하기
이미 알고 계시겠지만, Couchbase는 컬렉션이 아닌 버킷을 사용합니다. 그러나 버킷은 컬렉션과 동일한 기능을 하지 않습니다. MongoDB처럼 모든 문서 유형당 하나의 버킷을 갖는 대신 모든 애플리케이션에 대해 하나의 버킷을 갖게 됩니다.
즉, MongoDB 내보내기를 변경하여 Couchbase 내부에서 의미가 있도록 해야 합니다.
Couchbase에서는 모든 문서에 문서 유형을 나타내는 문서 속성이 있는 것이 일반적입니다. 다행히도 우리는 이전 컬렉션의 이름을 알고 있고 약간의 마법을 부릴 수 있습니다. 최종적으로 Couchbase 문서는 다음과 같이 보일 것입니다:
1 2 3 4 5 6 7 8 9 10 |
{ "_id": "course-1", "_유형": "코스", "이름": "바구니 직조 101", "학생": [ "nraboy", "mgroves", "hgreeley" ] } |
위의 예제에서는 모든 $oid
값을 추가하고 _id
그리고 _유형
속성입니다.
Golang 컬렉션 가져오기 스크립트 개발
이제 어디로 향하고 있는지 알았으니 조작과 로딩을 수행할 스크립트에 집중할 수 있습니다. 하지만 작업을 수행하는 방법에 대한 Golang 로직에 대해 생각해 봅시다.
우리는 JSON 파일에서 한 줄씩 읽을 것이라는 것을 알고 있습니다. 읽은 모든 줄에 대해 조작한 다음 저장해야 합니다. 파일에서 읽는 것과 Couchbase에 삽입하는 것은 모두 블로킹 작업입니다. 읽기는 매우 빠르지만, 테라바이트 단위의 데이터에 대해 한 번에 하나의 문서를 블로킹 방식으로 삽입하는 작업은 상당히 느릴 수 있습니다. 따라서 고루틴을 시작하여 작업을 병렬로 수행해야 합니다.
프로젝트의 어딘가에 새 프로젝트를 만듭니다. $GOPATH 라는 파일을 만들고 main.go 를 다음 코드로 대체합니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
패키지 메인 가져 오기 ( "bufio" "encoding/json" "flag" "fmt" "os" "동기화" "github.com/couchbase/gocb" ) var waitGroup sync.WaitGroup var 데이터 찬 문자열 var bucket *gocb.Bucket func main() {} 함수 일꾼(컬렉션 문자열) {} 함수 cbimport(문서 문자열, 컬렉션 문자열) {} func compressObjectIds(mapDocument map[string]interface{}) string {} |
위의 코드는 우리가 달성하고자 하는 목표에 대한 청사진일 뿐입니다. 그리고 메인
함수는 여러 고루틴을 시작하고 JSON 파일을 읽는 일을 담당합니다. 애플리케이션이 종료되는 것을 원하지 않습니다. 메인
함수가 종료되므로 WaitGroup. 이렇게 하면 모든 고루틴이 종료될 때까지 애플리케이션이 종료되지 않습니다.
그리고 worker
함수는 각 고루틴이 될 것이며, 호출은 cbimport
를 호출합니다. 압축객체아이디
를 사용하여 $oid
를 압축된 값으로 대체합니다. 압축이란 래핑을 포함하지 않는다는 의미입니다. $oid
속성입니다.
이를 살펴보겠습니다. 메인
함수입니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
함수 main() { fmt.Println("가져오기 프로세스 시작 중...") flagInputFile := flag.String("입력 파일", "", "문서가 포함된 경로가 있는 파일") flagWorkerCount := flag.Int("workers", 20, "데이터 가져오기를 위한 동시 작업자") flagCollectionName := flag.String("컬렉션", "", "몽고DB 컬렉션 이름") flagCouchbaseHost := flag.String("couchbase-host", "", "couchbase 클러스터 호스트") flagCouchbaseBucket := flag.String("카우치베이스 버킷", "", "카우치베이스 버킷 이름") flagCouchbase버킷비밀번호 := flag.String("couchbase버킷비밀번호", "", "카우치베이스 버킷 비밀번호") flag.Parse() 클러스터, _ := gocb.Connect("couchbase://" + *flagCouchbaseHost) 버킷, _ = cluster.OpenBucket(*flagCouchbase버킷, *flagCouchbase버킷암호) 파일, _ := os.Open(*flagInputFile) defer file.Close() data = make(chan 문자열) 스캐너 := bufio.NewScanner(파일) scanner.Split(bufio.ScanLines) for i := 0; i < *flagWorkerCount; i++ { { waitGroup.Add(1) 작업자(*flagCollectionName) 이동 } for scanner.Scan() { data <- scanner.Text() } close(data) waitGroup.Wait() fmt.Println("가져오기가 완료되었습니다!") } |
위의 함수는 애플리케이션 구성에 사용될 일련의 명령줄 플래그를 받습니다. 대상 카우치베이스 서버와 버킷에 대한 연결이 설정되고 입력 파일이 열립니다.
고루틴을 사용하기 때문에 잠금 시나리오를 피하기 위해 채널 변수를 사용해야 합니다. 읽은 모든 줄은 각 고루틴이 읽을 채널에 대기열에 대기합니다.
고루틴을 스핀업하면 파일이 읽혀지고 채널이 채워집니다. 파일을 완전히 읽은 후에는 채널이 닫힙니다. 즉, 고루틴이 모든 데이터를 읽으면 고루틴이 종료될 수 있습니다. 애플리케이션을 종료하기 전에 고루틴이 종료될 때까지 기다릴 것입니다.
이제 worker
함수입니다:
1 2 3 4 5 6 7 8 9 10 |
함수 일꾼(컬렉션 문자열) { defer waitGroup.Done() 에 { 문서, 확인 := <-data if !ok { break } cbimport(문서, 컬렉션) } } |
몽고DB 컬렉션 이름이 각 워커에 전달되고 워커는 채널이 닫힐 때까지 루프에서 계속 작동합니다.
채널에서 읽은 모든 문서에 대해 cbimport
함수가 호출됩니다:
1 2 3 4 5 6 7 |
함수 cbimport(문서 문자열, 컬렉션 문자열) { var mapDocument map[string]interface{} json.Unmarshal([]byte(document), &mapDocument) mapDocument["_유형"] = 컬렉션 압축객체아이디(맵도큐먼트) bucket.Insert(mapDocument["_id"].(string), mapDocument, 0) } |
파일의 각 줄은 인터페이스 맵으로 마샬링 해제해야 하는 문자열이 됩니다. 컬렉션 이름을 알고 있으므로 특정 이름을 담을 프로퍼티를 만들 수 있습니다. 그런 다음 전체 맵을 압축객체아이디
함수를 사용하여 $oid
래퍼.
그리고 압축객체아이디
함수는 다음과 같습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func compressObjectIds(mapDocument map[string]interface{}) string { var objectIdValue 문자열 키, 값 := 범위 맵도큐먼트 { 스위치 값.(유형) { 대/소문자 문자열입니다: if key == "$oid" && len(mapDocument) == 1 { 반환값.(문자열) } 케이스 맵[문자열]인터페이스{}: objectIdValue = compressObjectIds(value.(map[string]interface{})) if objectIdValue != "" { 맵도큐먼트[키] = 객체Id값 } 케이스 []인터페이스{}: 인덱스의 경우 요소 := 범위 값.([]인터페이스{}) { objectIdValue = compressObjectIds(element.(map[string]interface{})) if objectIdValue != "" { value.([]interface{})[index] = objectIdValue } } } } 반환 "" } |
위에서는 기본적으로 문서의 모든 키를 반복하고 있습니다. 값이 중첩된 객체 또는 JSON 배열인 경우, 키가 다음과 같은 문자열에 도달할 때까지 동일한 작업을 재귀적으로 수행합니다. $oid
. 이 조건이 충족되면 해당 문서의 해당 레벨에서 유일한 키인지 확인합니다. 이렇게 하면 안전하게 압축할 수 있는 ID임을 알 수 있습니다.
나쁘지 않죠?
MongoDB에서 Couchbase로 가져오기 실행하기
Go 프로그래밍 언어가 설치 및 구성되었다고 가정하고 이 애플리케이션을 빌드해야 합니다.
명령줄에서 모든 종속성을 가져와야 합니다. 프로젝트를 현재 작업 디렉터리로 설정하고 다음을 실행합니다:
1 |
go get -d -v |
위의 명령은 Go 파일에 있는 모든 종속성을 가져옵니다.
이제 애플리케이션을 빌드하고 실행하거나 그냥 실행할 수 있습니다. 단계는 크게 다르지 않지만 코드를 실행하기만 하면 됩니다.
명령줄에서 다음을 실행합니다:
1 2 3 4 5 6 |
./collectionimport \ --input-file FILE_NAME.json \ --컬렉션 COLLECTION_NAME \ --couchbase-host localhost \ --couchbase-bucket default \ --근로자 20 |
위의 명령을 사용하면 Couchbase 서버 정보, 작업자 고루틴 수 등과 같은 모든 플래그를 애플리케이션에 전달할 수 있습니다.
성공했다면 이제 MongoDB 내보내기가 Couchbase NoSQL 데이터베이스에 있어야 합니다.
결론
방금 Golang과 MongoDB로 작업하여 컬렉션 데이터를 Couchbase로 가져오는 방법을 살펴보았습니다. 물론 우리가 본 코드가 최적화될 수도 있지만, 단순성의 관점에서 보면 우리가 달성하고자 했던 것이 무엇인지 알 수 있을 것입니다.
이 임포터 프로젝트를 다운로드하여 직접 사용해 보고 싶으신가요? 다음 주소에 업로드했습니다. GitHub에서 실행에 대한 자세한 지침을 확인하세요. 비공식적이며 대량의 데이터에 대해 테스트되지 않았다는 점에 유의하세요. 데이터 마이그레이션 요구 사항을 충족하는 방법을 배우기 위한 예시로만 사용하세요.
Couchbase Server 또는 Golang SDK에 대해 자세히 알아보고 싶으시면 카우치베이스 개발자 포털.