우리 모두 알다시피 코드는 유지 관리 비용이 많이 들며, 코드가 복잡할수록 유지 관리 비용도 높아집니다. 따라서 소프트웨어 개발자들은 초기부터 다음과 같은 목표를 달성하기 위해 매우 열심히 노력해 왔습니다. 코드 재사용성.
Couchbase Mobile 3.0은 SQL++ 쿼리에 대한 지원을 도입했습니다. 개발자로서 가장 먼저 떠올랐던 생각은 Couchbase Server와 Couchbase Mobile 애플리케이션을 대상으로 하는 프로젝트에서 사용되는 쿼리를 공유하는 것이었습니다.
이것이 좋은 생각일까요? 샘플 데이터와 개념 증명 모바일 애플리케이션을 사용하여 이 질문에 대한 답을 찾아보겠습니다.
샘플 데이터
이 문서의 모든 코드는 다음에서 사용할 수 있습니다. GitHub. 를 참조하세요. README 를 참조하여 컴퓨터에서 프로젝트를 설정하는 방법에 대한 정보를 확인하세요.
샘플 데이터는 오픈스트리트맵 프로젝트에 따라 라이선스가 부여되며 오픈 데이터 커먼즈 오픈 데이터베이스 라이선스(ODbL) 오픈스트리트맵 재단에 의해 개발되었습니다.
이 데이터 세트에는 미국에서 아이스크림을 판매하는 모든 상점이 포함되어 있습니다. JSON 문서의 예는 다음과 같습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ "type": "기능", "id": "node/472242349", "속성": { "addrCity": "Austin", "addrHousenumber": "4477", "addrPostcode": "78745", "addrState": "TX", "addrStreet": "사우스 라마 대로", "addrUnit": "#790", "어메니티": "ice_cream", "요리": "ice_cream", "name": "에이미의 아이스크림", "전화": "+1-512-891-0573", "id": "node/472242349" }, "지오메트리": { "type": "포인트", "좌표": [ -97.7998856, 30.230688 ] } } |
보시다시피, 이 데이터 세트는 문서에 속성이 내장되어 있기 때문에 테스트에 다용도로 사용할 수 있습니다.
모바일 애플리케이션 샘플
그리고 모바일 애플리케이션 샘플 로 작성되었습니다. SwiftUI 와 함께 결합 는 미국에서 아이스크림을 판매하는 모든 다양한 상점을 표시하는 iOS/macOS 애플리케이션입니다. 이 애플리케이션에는 사전 구축된 데이터베이스 편의상 이미 동일한 JSON 문서를 가져온 상태입니다. 이 모바일 애플리케이션은 최신 버전의 카우치베이스 라이트 SDK. src 폴더에 있는 프로젝트 파일에서 XCode로 프로젝트를 열어 따라할 수 있습니다.
간단한 쿼리 비교
테스트를 시작하기 위해, 모든 아이스크림 가게 주소를 표시하고 가게 이름으로 주문할 수 있는 간단한 쿼리를 Couchbase Server에 작성해 보겠습니다:
1 2 3 4 5 6 |
선택 id, 속성.addrCity, 속성.addrHousenumber, 속성.addrPostcode, 속성.addrStreet, 속성.addrState, 속성.이름 FROM 아이스크림 어디 속성.addrCity <> "" AND 유형 = "기능" 주문 BY 속성.이름 |
이 쿼리에는 속성의 객체가 다음과 같은 문서만 반환하는 필터가 있습니다. addrCity 필드에 값이 있고 문서 유형 은 다음과 같습니다. 기능. 왜 우리가 addrCity 필터를 사용하면 이 데이터 집합에 이름이 없거나 정보가 누락된 더티 데이터 또는 문서가 있기 때문입니다. 데이터 집합에 유형 속성은 모바일 애플리케이션에서 다양한 유형의 문서를 필터링하는 데 일반적으로 사용됩니다.
우리는 쿼리 편집기 카우치베이스 서버에서 웹 콘솔 를 사용하여 이 쿼리를 실행했습니다. 개인용 Macbook Pro M1 Max에서 이 쿼리를 실행했을 때 결과는 38.4ms로 반환되었습니다. 이 쿼리는 adv_properties_addrCity_type 색인을 클릭하면 확인할 수 있습니다. 인덱스 어드바이저 버튼을 클릭합니다.
쿼리를 Couchbase Lite로 포팅하기
이제 모바일 앱에서 동일한 쿼리를 실행해 보겠습니다. 모바일 앱을 보려면 XCode를 사용하여 모바일 앱의 IceCreamLocator.xcodeproj 파일에 있는 src 폴더를 엽니다. Xcode에서 열면 아이스크림 위치 저장소 스위프트 파일에 공유\데이터 폴더로 이동합니다.
그리고 init 함수를 사용하면 데이터베이스에 대한 상세 로깅을 켜서 쿼리 정보를 검사할 수 있습니다. 상세 로깅을 사용하면 쿼리 성능을 포함하여 Couchbase Lite 내에서 어떤 일이 일어나고 있는지 검토할 수 있습니다.
Couchbase Server의 간단한 쿼리를 검토하고 새로운 SQL++ 쿼리 API를 사용하여 Swift에서 어떻게 작동하는지 살펴 보겠습니다. 간단한 쿼리를 위한 함수의 이름은 유형별 도시 주문 이름 가져오기를 클릭하면 아래에 코드가 표시됩니다:
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 유형별 도시 주문 이름 가져오기() -> Void { do { 만약 let 쿼리 = 시도 _db?.createQuery("SELECT id, properties.addrCity, properties.addrHousenumber, properties.addrPostcode, properties.addrStreet, properties.addrState, properties.name FROM _ WHERE properties.addrCity \"\" AND type = \"Feature\" ORDER BY properties.name") { var 결과: [아이스크림 위치] = [] let 설명 = 시도 쿼리.설명() 인쇄 ("**설명** \(설명)") 에 대한 결과 in 시도 쿼리.실행() { 만약 let 데이터 = 결과.toJSON().데이터(사용: .utf8){ let 위치 = 시도 JSONDecoder().디코딩(아이스크림 위치.self, 에서: 데이터) 결과.추가(위치) } } self.아이스크림 위치 목록.보내기(결과) self.아이스크림 위치 목록.보내기(완료: .완료) } } catch { 인쇄("**Error**: \(오류)") self.아이스크림 위치 목록.보내기(완료: .실패(DbError.알 수 없음)) } } |
Swift에서 SQL++ 쿼리를 작성하는 방법에는 몇 가지가 있습니다. 이 예제에서는 데이터베이스 createQuery 함수를 사용하여 쿼리를 생성합니다. Couchbase Lite에서는 데이터베이스 이름 앞에 밑줄 문자(_)를 사용하고 있습니다. FROM 문을 사용할 수 있습니다. 데이터베이스 이름 변경은 SQL++ 문을 재사용할 때 예상되는 변경 사항 중 하나입니다. 또한, 따옴표를 따옴표로 바꾸려면 어디 절에 스위프트 문자열 이스케이프 문자(\)를 따옴표 앞에 붙입니다.
실행 중 이 쿼리를 테스트하려면 이 쿼리를 실행하는 동안 아이스크림 목록 보기 모델 파일에 있는 ViewModels 폴더로 이동합니다. 폴더를 찾습니다. init 함수를 호출하는 함수 하단에 있는 첫 번째 예제 줄의 주석 처리를 해제하고 유형별 도시 주문 이름 가져오기 함수에서 _저장소 변수입니다:
1 2 |
//첫 번째 예제 _저장소.유형별 도시 주문 이름 가져오기() |
이제 Xcode를 사용하여 iOS 시뮬레이터 또는 macOS 앱에서 애플리케이션을 실행할 수 있습니다. 그런 다음 디버그 콘솔 를 클릭하여 Xcode에서 디버깅 표시 아이콘을 클릭하면 아래에 강조 표시된 것처럼 Xcode 편집기의 오른쪽 하단에 있습니다.
자세한 로깅은 방대한 양의 로깅 정보를 표시합니다. 로그에서 올바른 정보를 찾으려면 콘솔 아래의 필터 상자를 사용하세요. 예를 들어 "정보 조회'를 입력한 다음 키보드의 엔터 키를 눌러 쿼리에 대한 정보를 확인합니다. 디버그 콘솔은 Couchbase Lite가 반환하는 데이터를 필터링합니다.
로그를 검사하면 다음과 같은 내용이 표시됩니다. 정보 조회 로그 항목과 쿼리에서 찾은 문서 수, 쿼리에 소요된 바이트 수 및 시간을 확인할 수 있습니다. 지금 보고 있는 수치는 실제 기기가 아닌 시뮬레이터에서 나온 수치라는 점을 기억하세요. 모바일 앱의 성능을 조정하려면 실제 기기에서 Apple의 도구를 사용해야 합니다.
이 쿼리의 실행 속도는 매우 인상적입니다. 더욱 인상적인 것은 아직 인덱스가 생성되지 않았기 때문에 이 쿼리는 인덱스 없이 실행되며 테이블 스캔을 수행하므로 각 문서에서 데이터베이스를 살펴보고 쿼리의 요구 사항과 일치하는지 확인해야 한다는 것입니다. Couchbase Lite는 인덱스가 필요하지 않으며 필요한 경우 테이블 스캔을 수행하지만, Couchbase Server는 테이블 스캔으로 인해 전체 클러스터의 성능이 저하될 수 있으므로 인덱스가 필요합니다.
인덱스를 추가하면 쿼리 성능이 크게 향상되는 경우도 있으므로 애플리케이션 실행을 중지하고 적절한 인덱스를 추가한 후 성능을 다시 확인해 보겠습니다.
열기 아이스크림 위치 저장소 을 클릭하고 createIceCreamIndexes 함수를 추가합니다. 첫 번째 쿼리 인덱스를 생성하기 위한 첫 번째 코드 블록의 주석을 해제합니다:
1 2 |
let simpleIndex = 값 인덱스 구성(["properties.name", "properties.addrCity", "type"]) 시도 _db?.createIndex(simpleIndex, 이름: "IDX_LOCATION_TYPE_CITY_NAME") |
쿼리가 우리가 만든 인덱스를 사용했는지 테스트하려면 어떻게 해야 하나요? 에서 유형별 도시 주문 이름 가져오기 함수를 실행하면 설명 함수의 결과를 콘솔에 출력하는 두 줄이 표시됩니다:
1 2 |
let 설명 = 시도 쿼리.설명() 인쇄 ("**설명** \(설명)") |
쿼리 설명 함수는 SQLite 쿼리 계획 설명 명령을 실행하고 인쇄 기능을 사용하여 콘솔로 보내는 문자열에 결과를 저장합니다. 다음에 대해 필터링할 수 있습니다. 스캔 테이블 를 입력하여 결과를 확인합니다. 결과를 검토할 때 콘솔 창에서 이전 필터를 제거했는지 확인하세요. 이제 애플리케이션을 다시 실행하고 로그를 살펴 성능 변화를 확인합니다.
다음과 같이 표시되어야 합니다. 인덱스 사용 를 생성된 인덱스 이름과 함께 표시하면 쿼리가 적절한 인덱스를 사용하고 있음을 알 수 있습니다. 인덱스는 쿼리에 도움이 될 수 있지만 문서 삽입 및 업데이트 속도가 느려질 수도 있습니다. 인덱싱에 대한 자세한 내용은 Couchbase Lite에서 확인할 수 있습니다. 문서.
다른 쿼리 비교
두 번째 비교에서는 결과를 필터링하여 조지아 주에 있는 아이스크림 가게만 도시 및 주별로 정렬하여 표시합니다:
1 2 3 4 5 6 7 8 |
선택 id, 속성.addrCity, 속성.addHousenumber, 속성.addrPostcode, 속성.addrStreet, 속성.addrState, 속성.이름 FROM 아이스크림 어디 속성.addrCity IS NOT NULL AND 속성.addrState = 'GA' AND 유형 = "기능" 주문 BY 속성.이름 |
이 쿼리는 15개의 문서를 반환하며, 다른 쿼리를 사용하기 때문에 properties.addrState 그리고 properties.addrCity 속성의 어디 절을 사용합니다.
확인 방법을 변경했습니다. properties.addrCity 속성을 사용하고 IS NOT NULL 문을 사용해 보세요. 이는 SQL++ 언어의 유연성과 개발자가 다양한 구문을 사용하여 동일한 결과를 생성할 수 있는 방법을 보여줍니다.
이제 모바일 앱으로 돌아가서 어떤 차이점이 있는지 살펴보겠습니다. 두 번째 쿼리에 대한 함수의 이름은 getListByStateGeorgia를 클릭하면 아래에 코드가 표시됩니다:
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 getListByStateGeorgia() -> Void { do { 만약 let 쿼리 = 시도 _db?.createQuery("SELECT id, properties.addrCity, properties.addrHousenumber, properties.addrPostcode, properties.addrStreet, properties.addrState, properties.name FROM _ WHERE properties.addrCity NOT NULL AND properties.addrState = \"GA\" AND type=\"Feature\" ORDER BY properties.name") { var 결과: [아이스크림 위치] = [] let 설명 = 시도 쿼리.설명() 인쇄 ("**설명** \(설명)") 에 대한 결과 in 시도 쿼리.실행() { 만약 let 데이터 = 결과.toJSON().데이터(사용: .utf8){ let 위치 = 시도 JSONDecoder().디코딩(아이스크림 위치.self, 에서: 데이터) 결과.추가(위치) } } self.아이스크림 위치 목록.보내기(결과) self.아이스크림 위치 목록.보내기(완료: .완료) } } catch { 인쇄("**Error**: \(오류)") self.아이스크림 위치 목록.보내기(완료: .실패(DbError.알 수 없음)) } } |
이 새 쿼리를 테스트하려면 아이스크림 목록 보기 모델. 를 찾습니다. init 함수를 호출하는 함수 하단에 있는 첫 번째 예제 줄을 주석 처리하고 유형별 도시 주문 이름 가져오기 함수에서 _저장소 변수에서 새 함수를 호출하는 줄의 주석 처리를 해제합니다:
1 2 3 4 5 |
//첫 번째 예제 //_repository.getListByTypeCityOrderName() //두 번째 예제 _저장소.getListByStateGeorgia() |
Xcode를 사용하여 앱을 디버그하고 디버그 영역을 확인할 수 있습니다. 쿼리를 변경했지만 아직 새 인덱스를 만들지 않았기 때문에 원하는 동작이 아닌 테이블 스캔을 다시 수행하게 됩니다.
이 문제를 해결하려면 다시 아이스크림 위치 저장소 을 클릭하고 createIceCreamIndexes 함수를 추가합니다. 두 번째 쿼리 인덱스를 생성하기 위한 두 번째 코드 블록의 주석을 해제합니다:
1 2 3 |
let secondIndex = 값 인덱스 구성(["properties.name","properties.addrCity", "type", "properties.addrState"]) 시도 _db?.createIndex(secondIndex, 이름: "IDX_LOCATION_TYPE_시_주_이름") |
이 두 가지 간단한 쿼리에서 코드를 재사용할 수 있다는 것을 알았지만 테이블 스캔을 피하기 위해 인덱스를 추가해야 했습니다.
쿼리 재사용으로 인한 캐치
Couchbase Server와 Couchbase Mobile은 동일한 스토리지 또는 쿼리 엔진을 사용하지 않습니다. 따라서 Couchbase Mobile에 최적화하기 위해 약간의 수정이 필요한 Couchbase Server 쿼리가 발생할 수 있습니다.
카우치베이스 서버에 대한 자세한 내용은 다음 문서에서 확인하세요. 비용 기반 최적화 도구 를 사용하여 쿼리 엔진을 개선할 수 있습니다. 카우치베이스 개발자 포털에도 개선에 관한 좋은 문서가 있습니다. 쿼리 성능 일부 쿼리가 특정 방식으로 작성되는 이유를 설명할 수 있습니다.
문제 해결에 대한 Couchbase Mobile 설명서를 읽어보시기 바랍니다. 쿼리 최적화된 쿼리를 작성하는 데 도움이 되었으며 SQLite 쿼리 계획 출력에 대해 자세히 설명해줍니다.
요약
Couchbase Mobile 3.0 릴리즈의 코드 예제에서 볼 수 있듯이, Server와 Mobile 간에 SQL++ 코드를 재사용할 수 있습니다. 그러나 최적의 성능을 위해 쿼리와 인덱스를 수정해야 할 수도 있습니다. 이 점을 감안하여 다음에 대한 훌륭한 문서를 읽어보실 것을 적극 권장합니다. 모바일용 SQL++ - 서버용 SQL++와의 차이점 그리고 SQL++ 쿼리 문자열. 여기에는 모바일 애플리케이션에서 쿼리를 사용하기 위해 쿼리를 변경해야 할 수 있는 유용한 정보가 포함되어 있습니다.
다음은 게시물 전체에서 참조한 주요 리소스 중 일부입니다:
- GitHub의 코드 샘플
- 오픈스트리트맵 프로젝트
- Couchbase Mobile - 사전 구축된 데이터베이스 문서
- Swift의 Couchbase Lite - 빠른 시작 문서
- Couchbase Lite - 문서 색인화
- 카우치베이스 서버 - 비용 기반 최적화 도구 문서
- 카우치베이스 서버 - 튜닝 팁 및 조언
- Couchbase Mobile - 쿼리 문제 해결