사용자 정의 함수(UDF) 는 SQL++에서 지원되는 매우 유용한 기능입니다. Couchbase 7.6에는 UDF 실행에 대한 디버깅 기능과 가시성을 강화하는 개선 사항이 도입되었습니다.
이 블로그에서는 Couchbase 7.6의 새로운 기능 중 UDF와 관련된 두 가지를 살펴봅니다.
-
- 자바스크립트 UDF에서 실행되는 SQL++ 문에 대한 프로파일링
- UDF 내에서 SQL++ 문의 쿼리 계획에 액세스하는 함수 설명하기
이 게시물의 예제에서는 여행 샘플 데이터 세트 를 설치해야 합니다.
자바스크립트 UDF에서 실행되는 SQL++ 프로파일링
쿼리 프로파일링은 SQL++가 제공하는 디버깅 기능입니다.
문 실행에 대해 프로파일링이 활성화된 경우 요청 결과에는 문 실행의 각 단계에 대한 타이밍 및 메트릭이 포함된 자세한 실행 트리가 포함됩니다. 프로파일링 정보는 문 실행 결과에서 반환되는 것 외에도 요청에 대한 액세스 권한이 있는 시스템:활성_요청 그리고 시스템:완료_요청 시스템 키 공간.
다음은 다음과 같은 게시물입니다. 요청 프로파일링에 대해 자세히 알아보기.
Couchbase 7.0에서는 다음과 같은 하위 쿼리에 대한 프로파일링이 포함되었습니다. 인라인 UDF.
그러나 Couchbase 7.6의 새로운 기능을 통해 프로파일링이 다음과 같은 SQL++ 문으로 확장되었습니다. 자바스크립트 UDF.
이전 버전에서는 JavaScript UDF 내의 문을 프로파일링하려면 사용자가 함수 정의를 열고 UDF 내에서 각 문을 개별적으로 실행한 후 프로파일을 수집해야 했습니다. 7.6.0에서는 이 추가 단계가 더 이상 필요하지 않습니다!
이제 프로파일링이 활성화되면 문에 JavaScript UDF 실행이 포함된 경우 UDF에서 실행된 모든 SQL++ 문에 대한 프로파일도 수집됩니다. 그리고 이 UDF 관련 프로파일링 정보는 요청 출력에서 확인할 수 있습니다, 시스템:활성_요청 그리고 시스템:완료_요청 시스템 키 공간도 마찬가지입니다.
예 1:
자바스크립트 UDF 생성 js1 글로벌 라이브러리에서 lib1 REST 엔드포인트 또는 UI를 통해.
1 2 3 4 5 6 7 8 9 10 11 12 |
함수 js1() { var 쿼리 = 선택 * FROM 기본값:`여행-샘플`.인벤토리.항공사 LIMIT 1; var res = []; 에 대한 (const 행 의 쿼리) { res.push(행); } 쿼리.닫기() 반환 res; } |
해당 SQL++ 함수를 생성합니다:
1 |
만들기 기능 js1() 언어 자바스크립트 AS "js1" AT "lib1"; |
프로파일링을 활성화한 상태에서 UDF를 실행합니다:
1 |
실행 기능 js1(); |
에서 프로필 섹션에 반환된 응답의 실행 타이밍 하위 섹션에 포함된 필드 ~udfStatements.
~udfStatements 는 JavaScript UDF 내의 모든 SQL++ 문에 대한 항목이 포함된 프로파일링 정보 배열입니다.
내의 모든 항목 ~udfStatements 섹션에 포함되어 있습니다:
-
- 실행 타이밍 – 문에 대한 실행 트리입니다. 여기에는 문 실행의 모든 단계에 대한 메트릭 및 타이밍 정보가 있습니다.
- 문 – 문 문자열입니다.
- 함수 – 문이 실행된 함수의 이름입니다. 중첩된 UDF 실행이 있는 경우 문을 실행한 UDF를 식별하는 데 유용합니다.
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
{ "요청ID": "2c5576b5-f01d-445f-a35b-2213c606f394", "서명": null, "결과": [ [ { "항공사": { "콜사인": "MILE-AIR", "country": "미국", "iata": "Q5", "icao": "MLA", "id": 10, "name": "40마일 에어", "type": "항공사" } } ] ], "status": "성공", "metrics": { "elapsedTime": "20.757583ms", "실행 시간": "20.636792ms", "resultCount": 1, "결과 크기": 310, "serviceLoad": 2 }, "프로필": { "phaseTimes": { "authorize": "12.835µs", "fetch": "374.667µs", "인스턴스화": "27.75µs", "parse": "251.708µs", "plan": "9.125µs", "primaryScan": "813.249µs", "primaryScan.GSI": "813.249µs", "프로젝트": "5.541µs", "run": "27.925833ms", "stream": "26.375µs" }, "phaseCounts": { "fetch": 1, "primaryScan": 1, "primaryScan.GSI": 1 }, "phaseOperators": { "authorize": 2, "fetch": 1, "primaryScan": 1, "primaryScan.GSI": 1, "프로젝트": 1, "stream": 1 }, "cpuTime": "468.626µs", "요청 시간": "2023-12-04T20:30:00.369+05:30", "servicingHost": "127.0.0.1:8091", "실행 타이밍": { "#operator": "Authorize", "#계획준비시간": "2023-12-04T20:30:00.369+05:30", "#stats": { "#phaseSwitches": 4, "execTime": "1.918µs", "servTime": "1.125µs" }, "권한": { "목록": [] }, "~어린이": { "#operator": "시퀀스", "#stats": { "#phaseSwitches": 2, "execTime": "2.208µs" }, "~어린이": [ { "#operator": "ExecuteFunction", "#stats": { "#itemsOut": 1, "#phaseSwitches": 4, "execTime": "22.375µs", "kernTime": "20.271708ms" }, "identity": { "name": "js1", "네임스페이스": "default", "type": "글로벌" } }, { "#operator": "Stream", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 2, "execTime": "26.375µs" }, "serializable": true } ] }, "~udfStatements": [ { "실행 타이밍": { "#operator": "Authorize", "#stats": { "#phaseSwitches": 4, "execTime": "2.626µs", "servTime": "7.166µs" }, "권한": { "목록": [ { "Priv": 7, "Props": 0, "Target": "default:travel-sample.inventory.airline" } ] }, "~어린이": { "#operator": "시퀀스", "#stats": { "#phaseSwitches": 2, "execTime": "4.375µs" }, "~어린이": [ { "#operator": "PrimaryScan3", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 7, "execTime": "22.082µs", "kernTime": "1.584µs", "servTime": "791.167µs" }, "bucket": "travel-sample", "index": "def_inventory_airline_primary", "index_projection": { "primary_key": true }, "키스페이스": "항공사", "limit": "1", "네임스페이스": "default", "optimizer_estimates": { "카디널리티": 187, "cost": 45.28617059639748, "fr_cost": 12.1780009122802, "size": 12 }, "범위": "인벤토리", "사용": "gsi" }, { "#operator": "Fetch", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 10, "execTime": "18.376µs", "kernTime": "797.542µs", "servTime": "356.291µs" }, "bucket": "travel-sample", "키스페이스": "항공사", "네임스페이스": "default", "optimizer_estimates": { "카디널리티": 187, "cost": 192.01699202888378, "fr_cost": 24.89848658838975, "size": 204 }, "범위": "인벤토리" }, { "#operator": "InitialProject", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 7, "execTime": "5.541µs", "kernTime": "1.1795ms" }, "discard_original": true, "optimizer_estimates": { "카디널리티": 187, "cost": 194.6878862611588, "fr_cost": 24.912769445246838, "size": 204 }, "보존_주문": true, "결과_기간": [ { "expr": "self", "별": true } ] }, { "#operator": "제한", "#stats": { "#itemsIn": 1, "#itemsOut": 1, "#phaseSwitches": 4, "execTime": "6.25µs", "kernTime": "333ns" }, "expr": "1", "optimizer_estimates": { "카디널리티": 1, "cost": 24.927052302103924, "fr_cost": 24.927052302103924, "size": 204 } }, { "#operator": "수신", "#stats": { "#phaseSwitches": 3, "execTime": "10.324833ms", "kernTime": "792ns", "state": "running" } } ] } }, "진술": "SELECT * FROM default:`travel-sample`.inventory.airline LIMIT 1;", "function": "기본값:JS1" } ], "~버전": [ "7.6.0-N1QL", "7.6.0-1847-enterprise" ] } } } |
설명 함수가 있는 쿼리 플랜
SQL++는 EXPLAIN 문을 사용하여 문의 플랜에 액세스할 수 있는 또 다른 훌륭한 기능을 제공합니다. 그러나 EXPLAIN 문은 인라인이나 JavaScript UDF와 같은 UDF 내의 문 플랜에는 확장되지 않습니다.
이전 버전에서는 UDF 내에서 SQL++에 대한 쿼리 계획을 분석하려면 사용자가 함수의 정의를 열고 UDF 내의 모든 문에 대해 개별적으로 EXPLAIN을 실행해야 했습니다.
Couchbase 7.6에서는 이러한 추가 단계가 새로운 문을 도입하여 최소화되었습니다.설명 기능. 이 문은 EXPLAIN과 동일한 기능을 수행하지만 UDF 내의 SQL++ 문에 대해 수행합니다.
EXPLAIN FUNCTION 문을 사용하는 방법을 살펴봅시다!
구문
1 |
설명_기능 ::= '설명' 'FUNCTION' 함수 |
여기, 함수 는 함수의 이름을 나타냅니다.
구문에 대한 자세한 내용은 문서를 참조하세요.
전제 조건
UDF에서 설명 함수를 실행하려면 사용자에게 충분한 RBAC 권한 에 실행 기능을 사용할 수 있습니다.
또한 사용자는 UDF 함수 본문 내에서 SQL++ 문을 실행하는 데 필요한 RBAC 권한이 있어야 합니다.
다음은 카우치베이스에서 지원되는 역할.
인라인 UDF
인라인 UDF의 EXPLAIN FUNCTION은 정의 내에 있는 모든 하위 쿼리의 쿼리 계획을 반환합니다.
예제 2 - 인라인 함수에 함수 설명하기
인라인 UDF를 생성하고 EXPLAIN FUNCTION을 실행합니다:
1 2 3 |
만들기 기능 inline1() { ( 선택 * FROM 기본값:`여행-샘플`.인벤토리.공항 어디 도시 = "자카르 베이" ) }; |
1 |
설명 기능 inline1(); |
위 문장의 결과에는 다음이 포함됩니다:
-
- 함수 - 실행된 함수의 이름은 EXPLAIN FUNCTION입니다.
- 요금제 - 인라인 UDF 내의 모든 하위 쿼리에 대한 항목이 포함된 플랜 정보 배열입니다.
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
{ "function": "기본:인라인1", "계획": [ { "카디널리티": 1.1176470588235294, "cost": 25.117642854609013, "plan": { "#operator": "시퀀스", "~어린이": [ { "#operator": "IndexScan3", "bucket": "travel-sample", "index": "def_inventory_airport_city", "index_id": "2605C88C115DD3A2", "index_projection": { "primary_key": true }, "키스페이스": "공항", "네임스페이스": "default", "optimizer_estimates": { "카디널리티": 1.1176470588235294, "cost": 12.200561852726496, "fr_cost": 12.179450078755286, "size": 12 }, "범위": "인벤토리", "spans": [ { "정확한": true, "범위": [ { "high": "\\"Zachar Bay\\"", "포함": 3, "index_key": "``도시``", "low": "\\"Zachar Bay\\"" } ] } ], "사용": "gsi" }, { "#operator": "Fetch", "bucket": "travel-sample", "키스페이스": "공항", "네임스페이스": "default", "optimizer_estimates": { "카디널리티": 1.1176470588235294, "cost": 25.082370508382763, "fr_cost": 24.96843677065826, "size": 249 }, "범위": "인벤토리" }, { "#operator": "Parallel", "~어린이": { "#operator": "시퀀스", "~어린이": [ { "#operator": "필터", "조건": "((`에어포트`.`시티`) = \\"Zachar Bay\\")", "optimizer_estimates": { "카디널리티": 1.1176470588235294, "cost": 25.100006681495888, "fr_cost": 24.98421650449632, "size": 249 } }, { "#operator": "InitialProject", "discard_original": true, "optimizer_estimates": { "카디널리티": 1.1176470588235294, "cost": 25.117642854609013, "fr_cost": 24.99999623833438, "size": 249 }, "결과_기간": [ { "expr": "self", "별": true } ] } ] } } ] }, "진술": "select self.* from `default`:`travel-sample`.`inventory`.`airport` where ((`airport`.`city`) = \\"Zachar Bay\\")" } ] } |
자바스크립트 UDF
JavaScript UDF 내의 SQL++ 문은 두 가지 유형이 있을 수 있으며, SQL++ 문이 호출되는 방식에 따라 EXPLAIN FUNCTION이 다르게 작동합니다.
다음에 대한 문서 참조는 다음과 같습니다. 자바스크립트 함수에서 SQL++ 호출.
임베디드 SQL++
-
- 임베디드 SQL++는 함수 본문에 '임베디드'되며, 그 감지는 자바스크립트 트랜스파일러가 처리합니다.
- 설명 함수는 내장된 SQL++ 문에 대한 쿼리 계획을 반환할 수 있습니다.
N1QL() 함수 호출로 실행되는 SQL++
-
- SQL++는 문자열 형식의 문을 N1QL() 함수에 인수로 전달하여 실행할 수도 있습니다.
- 함수를 구문 분석할 때 EXPLAIN을 실행할 잠재적인 SQL++ 문에 대해 함수 인수의 동적 문자열을 가져오기가 어렵습니다. 이 문제는 런타임에만 안정적으로 해결할 수 있습니다.
- 이러한 추론에 따라 EXPLAIN FUNCTION은 N1QL() 호출을 통해 실행된 SQL++ 문에 대한 쿼리 계획을 반환하지 않습니다. 대신 N1QL() 함수 호출이 수행된 줄 번호를 반환합니다. 이 줄 번호는 함수 정의의 시작 부분에서 계산됩니다.
- 그런 다음 사용자는 실제 함수 정의에서 줄 번호를 매핑하고 더 자세히 조사할 수 있습니다.
예제 3 - 외부 자바스크립트 함수에 대한 함수 설명
자바스크립트 UDF 생성 js2 글로벌 라이브러리에서 lib1 REST 엔드포인트 또는 UI를 통해:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
함수 js2() { // N1QL() 함수 호출로 실행되는 SQL++ var 쿼리1 = N1QL("UPDATE default:`travel-sample` SET test = 1 LIMIT 1"); // 임베디드 SQL++ var 쿼리2 = 선택 * FROM 기본값:`여행-샘플` LIMIT 1; var res = []; 에 대한 (const 행 의 쿼리2) { res.push(행); } 쿼리2.닫기() 반환 res; } |
해당 SQL++ 함수를 생성합니다:
1 |
만들기 기능 js2() 언어 자바스크립트 AS "js2" AT "lib1"; |
SQL++ 함수에서 EXPLAIN FUNCTION을 실행합니다:
1 |
설명 기능 js2; |
위 문장의 결과에는 다음이 포함됩니다:
-
- 함수 - 실행된 함수의 이름은 EXPLAIN FUNCTION입니다.
- 라인 번호 - N1QL() 함수 호출이 있는 JavaScript 함수 정의의 시작 부분에서 계산된 줄 번호 배열입니다.
- 요금제 - 모든 항목이 포함된 요금제 정보의 배열입니다. 임베디드 SQL++ 문을 사용합니다.
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
{ "function": "기본값:JS2", "line_numbers": [ 4 ], "계획": [ { "카디널리티": 1, "cost": 25.51560885530435, "plan": { "#operator": "Authorize", "권한": { "목록": [ { "Target": "기본:여행 샘플", "Priv": 7, "Props": 0 } ] }, "~어린이": { "#operator": "시퀀스", "~어린이": [ { "#operator": "시퀀스", "~어린이": [ { "#operator": "시퀀스", "~어린이": [ { "#operator": "PrimaryScan3", "index": "def_primary", "index_projection": { "primary_key": true }, "키스페이스": "travel-sample", "limit": "1", "네임스페이스": "default", "optimizer_estimates": { "카디널리티": 31591, "cost": 5402.279801258844, "fr_cost": 12.170627071041082, "size": 11 }, "사용": "gsi" }, { "#operator": "Fetch", "키스페이스": "travel-sample", "네임스페이스": "default", "optimizer_estimates": { "카디널리티": 31591, "cost": 46269.39474997121, "fr_cost": 25.46387878667884, "size": 669 } }, { "#operator": "Parallel", "~어린이": { "#operator": "시퀀스", "~어린이": [ { "#operator": "InitialProject", "discard_original": true, "optimizer_estimates": { "카디널리티": 31591, "cost": 47086.49704894546, "fr_cost": 25.489743820991595, "size": 669 }, "보존_주문": true, "결과_기간": [ { "expr": "self", "별": true } ] } ] } } ] }, { "#operator": "제한", "expr": "1", "optimizer_estimates": { "카디널리티": 1, "cost": 25.51560885530435, "fr_cost": 25.51560885530435, "size": 669 } } ] }, { "#operator": "Stream", "optimizer_estimates": { "카디널리티": 1, "cost": 25.51560885530435, "fr_cost": 25.51560885530435, "size": 669 }, "serializable": true } ] } }, "진술": "SELECT * FROM default:`travel-sample` LIMIT 1 ;" } ] } |
제약 조건
JavaScript 함수 정의에서 N1QL() 함수가 별칭이 지정된 경우, EXPLAIN FUNCTION은 이 별칭이 지정된 함수가 호출된 줄 번호를 반환할 수 없습니다. 예를 들어
1 2 3 4 |
함수 js3() { var alias = N1QL; var q = alias("SELECT 1"); } |
UDF에 중첩된 UDF 실행이 포함된 경우, EXPLAIN FUNCTION은 이러한 중첩된 UDF 내에서 SQL++ 문의 쿼리 계획 생성을 지원하지 않습니다.
요약
Couchbase 7.6은 UDF 디버깅을 위한 새로운 기능을 도입하여 사용자가 UDF 실행을 쉽게 들여다볼 수 있도록 도와줍니다.
자세한 내용은 다음 문서 링크를 참조하거나 다른 Couchbase 7.6의 혁신 사항을 확인해 보세요: