MongoDB 인덱스 모범 사례
머리말
대부분의 개발자는 색인 생성이 더 빠르다는 것을 알고 있습니다. 하지만 실제 과정에서 우리는 종종 몇 가지 질문과 어려움에 직면합니다.
- 우리가 쿼리하는 필드에는 쿼리에 관련된 모든 필드를 인덱싱해야 합니까?
- 복합 인덱스와 단일 필드 중에서 선택하는 방법은 무엇입니까? 아니면 각각에 대해 단일 필드를 추가해야 합니까?
- 인덱스를 추가하면 부작용이 있나요?
- 인덱스를 추가했는데 아직 속도가 부족하다구요? 무엇을 해야 할까요?
이 글에서는 색인 생성에 대한 기본 지식을 설명하고 위 질문에 답하려고 합니다.
1. 인덱스란 정확히 무엇인가요?
대부분의 개발자는 색인을 접하고 아마도 색인이 책의 카탈로그와 유사하다는 것을 알고 있을 것입니다. 원하는 콘텐츠를 찾고 카탈로그를 통해 적합한 키워드를 찾은 다음 해당 장의 페이지노를 찾은 다음. 구체적인 내용을 찾아보세요.
데이터 구조에서 가장 간단한 인덱스 구현은 특정 콘텐츠를 찾기 위해 키워드 키를 통해 특정 위치에 매핑되는 해시맵과 유사합니다. 그러나 해싱 외에도 인덱싱을 구현하는 방법에는 여러 가지가 있습니다.
(1) 인덱스
hash / b-tree / b+-tree의 다양한 구현 방법 및 기능 redis HSET / MongoDB&PostgreSQL / MySQL
hashmap
그림의 b-tree 및 b+-tree를 참조하세요. 차이점:
- b+-tree는 데이터를 저장하고, 비-leaves는 저장 인덱스를 저장합니다. 리프 사이에는 데이터를 저장하지 않습니다. 리프가 아닌 데이터를 저장할 수 있는 링크
- b-트리가 있습니다
알고리즘 검색 복잡성 측면에서:
- 해시는 O(1)
- b에 가깝습니다. -tree O(1)~ O(Log(n)) 더 빨라짐 평균 검색 시간, 불안정한 쿼리 시간
- b+ tree O(Log(n)) 연속 데이터, 쿼리 안정성
MongoDB가 대신 b-tree를 선택한 이유 구현을 위한 b+-tree?
인터넷의 많은 기사에서 이에 대해 설명했지만 이 기사의 초점은 아닙니다.
(2) 데이터 저장 및 인덱스
인덱스는 가능한 한 데이터 초에 메모리에 저장해야 합니다.
꼭 필요한 인덱스만 보관하고 메모리를 최대한 활용하도록 주의하세요.
인덱스 메모리가 거의 가득 차면 디스크를 읽기 쉽고 속도가 느려집니다.
(3) 인덱스의 구현 및 저장 원리를 알고 나서 생각
삽입/업데이트/삭제하면 재조정 트리가 트리거됩니다. 따라서 데이터를 추가, 삭제 또는 수정하면 인덱스가 수정을 트리거하고 성능이 저하됩니다. 인덱스가 많을수록 좋습니다. 이 경우 어떤 필드를 인덱스로 선택해야 합니까? 쿼리에서 이러한 조건을 사용하는 경우 어떻게 해야 합니까?
가장 간단한 해시맵을 예로 들면 복잡성이 O(1)이 아니라 소위 O(1)에 가까운 이유는 무엇입니까? 키 충돌/중복이 있기 때문에 DB에서 찾을 때 키 충돌이 있는 데이터가 많으면 계속해서 찾아보아야 합니다. 키 선택을 보는 B-트리에서도 마찬가지입니다.
그래서 대부분의 개발자가 자주 저지르는 실수는 구별이 없는 키를 색인화하는 것입니다. 예를 들어, 많은 컬렉션에는 수십만 개 이상의 중앙 집중식 유형/상태 문서 범주만 있습니다. 일반적으로 이러한 종류의 색인은 도움이 되지 않습니다.
2. 복합 인덱스
(1) 복합 인덱스가 많을수록 좋습니다
중복된 인덱스를 더 많이 구축하고 싶지 않으면 개발 동료들이 복합 및 단일 필드 선택에 어려움을 겪는 경우가 있습니다.
일반적인 시나리오를 기반으로 몇 가지 실험을 해보겠습니다.
대출 컬렉션이 여기에서 생성됩니다. 100개의 데이터만 포함하도록 단순화되었습니다. 이 대출 테이블에는 _id, userId, 상태(대출 상태), 금액(금액)이 있습니다.
db.loans.count()100
db.loans.find({ "userId" : "59e022d33f239800129c61c7", "status" : "repayed", }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "status" : { "$eq" : "repayed" } }, { "userId" : { "$eq" : "59e022d33f239800129c61c7" } } ] }, "queryHash" : "15D5A9A1", "planCacheKey" : "15D5A9A1", "winningPlan" : { "stage" : "COLLSCAN", "filter" : { "$and" : [ { "status" : { "$eq" : "repayed" } }, { "userId" : { "$eq" : "59e022d33f239800129c61c7" } } ] }, "direction" : "forward" }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
위 COLLSCAN 테이블은 인덱스가 없기 때문에 스캔됩니다. 다음으로 각각 여러 개의 인덱스를 생성합니다.
1단계 먼저 {userId:1, status:1}
db.loans.createIndex({userId:1, status:1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
db.loans.find({ "userId" : "59e022d33f239800129c61c7", "status" : "repayed", }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "status" : { "$eq" : "repayed" } }, { "userId" : { "$eq" : "59e022d33f239800129c61c7" } } ] }, "queryHash" : "15D5A9A1", "planCacheKey" : "BB87F2BA", "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1, "status" : 1 }, "indexName" : "userId_1_status_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ], "status" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]" ], "status" : [ "["repayed", "repayed"]" ] } } }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
을 생성하세요. 결과: {userId:1, status:1}이(가) 승리 계획으로 적중됩니다.
2단계: 일반적인 인덱스 userId 만들기
db.loans.createIndex({userId:1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 }
db.loans.find({ "userId" : "59e022d33f239800129c61c7", "status" : "repayed", }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "status" : { "$eq" : "repayed" } }, { "userId" : { "$eq" : "59e022d33f239800129c61c7" } } ] }, "queryHash" : "15D5A9A1", "planCacheKey" : "1B1A4861", "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1, "status" : 1 }, "indexName" : "userId_1_status_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ], "status" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "[\"59e022d33f239800129c61c7\", \"59e022d33f239800129c61c7\"]" ], "status" : [ "[\"repayed\", \"repayed\"]" ] } } }, "rejectedPlans" : [ { "stage" : "FETCH", "filter" : { "status" : { "$eq" : "repayed" } }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1 }, "indexName" : "userId_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]" ] } } } ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
더 나은 실행을 위해 DB가 {userId:1, status:1}을 감지합니다.
db.loans.find({ "userId" : "59e022d33f239800129c61c7" }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "userId" : { "$eq" : "59e022d33f239800129c61c7" } }, "queryHash" : "B1777DBA", "planCacheKey" : "1F09D68E", "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1 }, "indexName" : "userId_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]" ] } } }, "rejectedPlans" : [ { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1, "status" : 1 }, "indexName" : "userId_1_status_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ], "status" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]" ], "status" : [ "[MinKey, MaxKey]" ] } } } ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
DB가 {userId:1}을 감지합니다. 더 나은 실행을 위한 계획, 음 ~, 예상대로.
db.loans.find({ "status" : "repayed" }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "status" : { "$eq" : "repayed" } }, "queryHash" : "E6304EB6", "planCacheKey" : "7A94191B", "winningPlan" : { "stage" : "COLLSCAN", "filter" : { "status" : { "$eq" : "repayed" } }, "direction" : "forward" }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
흥미로운 부분: 상태가 인덱스에 도달하지 않음, 전체 테이블 스캔
다음 단계, 정렬 추가:
db.loans.find({ "userId" : "59e022d33f239800129c61c7" }).sort({status:1}).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "userId" : { "$eq" : "59e022d33f239800129c61c7" } }, "queryHash" : "F5ABB1AA", "planCacheKey" : "764CBAA8", "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1, "status" : 1 }, "indexName" : "userId_1_status_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ], "status" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]" ], "status" : [ "[MinKey, MaxKey]" ] } } }, "rejectedPlans" : [ { "stage" : "SORT", "sortPattern" : { "status" : 1 }, "inputStage" : { "stage" : "SORT_KEY_GENERATOR", "inputStage" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1 }, "indexName" : "userId_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]" ] } } } } } ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
(2) 다른 시도
흥미로운 부분: 상태가 적중하지 않음 인덱스
db.loans.find({ "status" : "repayed","userId" : "59e022d33f239800129c61c7", }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "$and" : [ { "status" : { "$eq" : "repayed" } }, { "userId" : { "$eq" : "59e022d33f239800129c61c7" } } ] }, "queryHash" : "15D5A9A1", "planCacheKey" : "1B1A4861", "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1, "status" : 1 }, "indexName" : "userId_1_status_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ], "status" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "[\"59e022d33f239800129c61c7\", \"59e022d33f239800129c61c7\"]" ], "status" : [ "[\"repayed\", \"repayed\"]" ] } } }, "rejectedPlans" : [ { "stage" : "FETCH", "filter" : { "status" : { "$eq" : "repayed" } }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1 }, "indexName" : "userId_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]" ] } } } ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
는 인덱스에 도달하지만 우리가 추측한 것처럼 쿼리 필드의 순서와는 아무런 관련이 없습니다.
이제 흥미로운 부분이 나옵니다. 인덱스 {userId:1}을 삭제합니다.
db.loans.dropIndex({"userId":1}) { "nIndexesWas" : 3, "ok" : 1 } db.loans.find({"userId" : "59e022d33f239800129c61c7", }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "userId" : { "$eq" : "59e022d33f239800129c61c7" } }, "queryHash" : "B1777DBA", "planCacheKey" : "5776AB9C", "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1, "status" : 1 }, "indexName" : "userId_1_status_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ], "status" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "["59e022d33f239800129c61c7", "59e022d33f239800129c61c7"]" ], "status" : [ "[MinKey, MaxKey]" ] } } }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
DB 실행 분석기는 인덱스 {userId:1, status:1}이 더 나을 수 있다고 생각하지만 복합 인덱스에 도달하지 않습니다. 상태가 선두 필드가 아니기 때문입니다.
db.loans.find({ "status" : "repayed" }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "status" : { "$eq" : "repayed" } }, "queryHash" : "E6304EB6", "planCacheKey" : "7A94191B", "winningPlan" : { "stage" : "COLLSCAN", "filter" : { "status" : { "$eq" : "repayed" } }, "direction" : "forward" }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
정렬 각도를 다시 변경하고 이전 쿼리 및 정렬과 교환합니다.
db.loans.find({userId:1}).sort({ "status" : "repayed" })
차이점은 무엇인가요?
db.loans.find({ "status" : "repayed" }).sort({userId:1}).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "status" : { "$eq" : "repayed" } }, "queryHash" : "56EA6313", "planCacheKey" : "2CFCDA7F", "winningPlan" : { "stage" : "FETCH", "filter" : { "status" : { "$eq" : "repayed" } }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "userId" : 1, "status" : 1 }, "indexName" : "userId_1_status_1", "isMultiKey" : false, "multiKeyPaths" : { "userId" : [ ], "status" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "userId" : [ "[MinKey, MaxKey]" ], "status" : [ "[MinKey, MaxKey]" ] } } }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
짐작대로 지수를 쳐보세요.
다시 플레이하여 주요 제출 테스트를 확인하겠습니다.
db.loans.dropIndex("userId_1_status_1") { "nIndexesWas" : 2, "ok" : 1 }
db.loans.getIndexes() [ { "v" : 2, "key" : { "id" : 1 }, "name" : "id_", "ns" : "cashLoan.loans" } ]
db.loans.createIndex({status:1, userId:1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
db.loans.getIndexes() [ { "v" : 2, "key" : { "id" : 1 }, "name" : "id_", "ns" : "cashLoan.loans" }, { "v" : 2, "key" : { "status" : 1, "userId" : 1 }, "name" : "status_1_userId_1", "ns" : "cashLoan.loans" } ]
db.loans.find({ "status" : "repayed" }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "status" : { "$eq" : "repayed" } }, "queryHash" : "E6304EB6", "planCacheKey" : "7A94191B", "winningPlan" : { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "status" : 1, "userId" : 1 }, "indexName" : "status_1_userId_1", "isMultiKey" : false, "multiKeyPaths" : { "status" : [ ], "userId" : [ ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : { "status" : [ "["repayed", "repayed"]" ], "userId" : [ "[MinKey, MaxKey]" ] } } }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
db.loans.getIndexes() [ { "v" : 2, "key" : { "id" : 1 }, "name" : "id_", "ns" : "cashLoan.loans" }, { "v" : 2, "key" : { "status" : 1, "userId" : 1 }, "name" : "status_1_userId_1", "ns" : "cashLoan.loans" } ]
db.loans.find({"userId" : "59e022d33f239800129c61c7", }).explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "cashLoan.loans", "indexFilterSet" : false, "parsedQuery" : { "userId" : { "$eq" : "59e022d33f239800129c61c7" } }, "queryHash" : "B1777DBA", "planCacheKey" : "5776AB9C", "winningPlan" : { "stage" : "COLLSCAN", "filter" : { "userId" : { "$eq" : "59e022d33f239800129c61c7" } }, "direction" : "forward" }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "RMBAP", "port" : 27017, "version" : "4.1.11", "gitVersion" : "1b8a9f5dc5c3314042b55e7415a2a25045b32a94" }, "ok" : 1 }
看完这个试验,明白了 {userId:1, status:1} vs {status:1,userId:1} 的差别了吗?
PS:这个case 里面其实status 区分度不高,这里只是作为实例展示。
三、总结:
- 注意使用上、使用频率上、区分高的/常用的在前面;
- 如果需要减少索引以节省memory/提高修改数据的性能的话,可以保留区分度高,常用的,去除区分度不高,不常用的索引。
- 学会用explain()验证分析性能:
DB 一般都有执行器优化的分析,MySQL & MongoDB 都是 用explain 来做分析。
语法上MySQL :
explain your_sql
MongoDB:
yoursql.explain()
总结典型:理想的查询是结合explain 的指标,他们通常是多个的混合:
- IXSCAN : 索引命中;
- Limit : 带limit;
- Projection : 相当于非 select * ;
- Docs Size less is better ;
- Docs Examined less is better ;
- nReturned=totalDocsExamined=totalKeysExamined ;
- SORT in index :sort 也是命中索引,否则,需要拿到数据后,再执行一遍排序;
- Limit Array elements : 限定数组返回的条数,数组也不应该太多数据,否则schema 设计不合理。
彩蛋
文末,还有最开头1个问题没回答:如果我的索引改加的都加了,还不够快,怎么办?
留个悬念,之后再写一篇。
更多PHP相关技术文章,请访问PHP教程栏目进行学习!
위 내용은 MongoDB 인덱스 모범 사례의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











Navicat 만료 문제를 해결하는 방법은 다음과 같습니다: 라이센스 갱신, 자동 업데이트 비활성화, Navicat 고객 지원에 문의하세요.

프런트엔드 개발자의 경우 Node.js 학습의 어려움은 JavaScript 기초, 서버 측 프로그래밍 경험, 명령줄 익숙함, 학습 스타일에 따라 다릅니다. 학습 곡선에는 기본 개념, 서버 측 아키텍처, 데이터베이스 통합 및 비동기 프로그래밍에 중점을 둔 초급 수준 및 고급 수준 모듈이 포함됩니다. 전반적으로, JavaScript에 탄탄한 기초가 있고 시간과 노력을 투자할 의향이 있는 개발자에게는 Node.js를 배우는 것이 어렵지 않지만 관련 경험이 부족한 개발자에게는 극복해야 할 특정 과제가 있을 수 있습니다.

Navicat을 사용하여 MongoDB에 연결하려면 다음을 수행해야 합니다: Navicat 설치 MongoDB 연결 생성: a. 연결 이름, 호스트 주소 및 포트를 입력합니다. b. 인증 정보를 입력합니다(필요한 경우). SSL 인증서를 추가합니다(필요한 경우). 연결 저장

Node.js에서 가장 일반적으로 사용되는 모듈은 다음과 같습니다. 파일 작업을 위한 파일 시스템 모듈 네트워크 통신을 위한 네트워크 모듈 데이터 스트림 처리를 위한 스트림 모듈 데이터베이스와 상호 작용하기 위한 데이터베이스 모듈 암호화, 쿼리 문자열과 같은 기타 유틸리티 모듈 문자열 구문 분석 및 HTTP 프레임워크

Node.js 애플리케이션의 경우 데이터베이스 선택은 애플리케이션 요구 사항에 따라 다릅니다. NoSQL 데이터베이스 MongoDB는 유연성을 제공하고, Redis는 높은 동시성을 제공하며, Cassandra는 시계열 데이터를 처리하고, Elasticsearch는 검색 전용입니다. SQL 데이터베이스 MySQL은 뛰어난 성능을 갖고 있고, PostgreSQL은 기능이 풍부하며, SQLite는 가볍고, Oracle 데이터베이스는 포괄적입니다. 선택할 때 데이터 유형, 쿼리, 성능, 트랜잭션성, 가용성, 라이센스 및 비용을 고려하십시오.

Node.js에서 데이터베이스에 연결하려면 데이터베이스 시스템(관계형 또는 비관계형)을 선택한 다음 해당 유형에 특정한 모듈을 사용하여 연결을 설정해야 합니다. 일반적인 모듈에는 mysql(MySQL), pg(PostgreSQL), mongodb(MongoDB) 및 redis(Redis)가 포함됩니다. 연결이 설정된 후 쿼리 문을 사용하여 데이터를 검색하고 문을 업데이트하여 데이터를 수정할 수 있습니다. 마지막으로 리소스를 해제하려면 모든 작업이 완료되면 연결을 닫아야 합니다. 연결 풀링, 매개변수화된 쿼리 사용, 오류 처리 등 모범 사례를 따르면 성능과 보안이 향상됩니다.

Node.js에서 데이터베이스에 연결하는 단계: MySQL, MongoDB 또는 PostgreSQL 패키지를 설치합니다. 데이터베이스 연결 개체를 만듭니다. 데이터베이스 연결을 열고 연결 오류를 처리합니다.

.NET 4.0은 다양한 애플리케이션을 만드는 데 사용되며 객체 지향 프로그래밍, 유연성, 강력한 아키텍처, 클라우드 컴퓨팅 통합, 성능 최적화, 광범위한 라이브러리, 보안, 확장성, 데이터 액세스 및 모바일을 포함한 풍부한 기능을 애플리케이션 개발자에게 제공합니다. 개발 지원.
