버전 3.2부터 MongoDB는 모든 인덱스에 대한 사용 통계를 추적합니다. 이러한 통계에 액세스하기 위해 MongoDB는 $indexStats 집계 파이프라인 단계를 제공합니다. 다음은 MongoDB에서 사용되지 않는 인덱스를 찾을 때 고려해야 할 6가지 사항입니다.
예를 들어 다음 명령은 "test.foo" 컬렉션에 대한 인덱스 통계를 제공합니다.
db.foo.aggregate( [ { $indexStats:{ } } ] )
https://bit.ly/2seXnzo
이 주제를 다루는 문서와 좋은 기사가 많기 때문에 $indexStats 출력에 대해서는 설명하지 않겠습니다. 대신 $indexStats 연산자를 사용할 때 서로 다른 영역에 대해 6가지 고려 사항을 제공합니다.
고려 사항 1:서비스를 다시 시작할 때마다 통계가 재설정됩니다.
$indexStats 연산자를 사용할 때는 항상 "accesses.since" 필드에 특별한 주의를 기울이십시오. 일과 종료 일괄 처리 또는 주간 보고서와 같은 일부 쿼리 패턴은 빈도가 낮을 수 있으므로 평가하려는 통계 기간이 요구 사항을 충족하는지 확인하십시오. 다음 스크립트를 사용하면 임계값(시간 단위)을 설정할 수 있으며 사용하지 않은 인덱스가 준수하지 않을 경우 경고를 출력합니다.
임계값_시간=24; db.foo.aggregate( [ { $indexStats:{ } } ] ).forEach(function(f){if (f.accesses.ops==0) {if (ISODate()-f.accesses.since당신에게 적합한 임계 값은 무엇입니까? 간단한 답은 없습니다. 모든 명령문 패턴이 동일한 빈도로 실행되는 것은 아니므로 올바른 임계값은 애플리케이션마다 다르며 동일한 데이터베이스 내의 컬렉션마다 다를 수 있습니다.
고려 2:2차 읽기
기본적으로 $indexStats는 기본에서 읽습니다. 애플리케이션이 보조에서 읽기만 하는 경우 기본에 대해 $indexStats를 실행하면 잘못된 결론이 나옵니다. Secondary에서 결과를 읽으려면 위의 첫 번째 고려 사항에서 스크립트를 사용할 수 있지만 db.getMongo().setReadPref('secondary')를 실행합니다. 실행하기 전에 명령이 보조에서 읽도록 합니다.
이제 질문은 "컬렉션이 1차 및 2차 읽기를 모두 수신하면 어떻게 될까요?"입니다. 다음 스크립트를 사용하여 1차 및 2차 모두에서 사용되지 않는 인덱스로 idx 배열을 채울 수 있습니다.
idx=[];db.foo.getIndexes().forEach(function(f){idx.push(f.name)})db.getMongo().setReadPref('기본');db.foo. 집계( [ { $indexStats:{ } } ] ).forEach(function(f){if (f.accesses.ops>0) { var index =idx.indexOf(f.name); if (index> -1) {idx.splice(index, 1);};}})db.getMongo().setReadPref('secondary');db.foo.aggregate( [ { $indexStats:{ } } ] ).forEach(function(f ){if(f.accesses.ops>0) { var index =idx.indexOf(f.name); if(index> -1) {idx.splice(index, 1);};}})idx 배열의 내용에는 기본 및 보조 모두에서 사용되지 않은 인덱스가 포함됩니다. 규정 준수 기간(threshold_hours)은 어떻습니까? 고려 사항 1(이전 하위 섹션)에서 스크립트를 약간 수정하면 시간 준수를 적용할 수 있습니다. 집계 부분을 아래 제공된 부분으로 교체하고 setReadPref를 사용하여 기본 및 보조에 대해 스크립트를 실행하기만 하면 됩니다.
db.foo.aggregate( [ { $indexStats:{ } } ,{$match:{"이름" :{$in:idx}}}] ).기본 및 보조에서 서로 다른 규정 준수 결과를 반환하는 경우 가장 좋은 방법은 두 계층이 규정을 준수할 때까지 기다리는 것입니다.
Secondary 읽기 기본 설정을 사용하면 Secondary가 최근에 다시 시작되고 스크립트가 통계를 가져오기 위해 Secondary를 선택한 경우 "잘못된 결과"가 발생할 수 있습니다. 이 경우 db.serverStatus().uptime을 확인하는 것이 좋습니다. 가동 시간이 더 높은 보조를 선택하십시오. 가동 시간 방법을 사용하려면 이 블로그 게시물의 향후 개정판에서 구현될 스크립트에 대한 다른 접근 방식이 필요합니다.
고려 3:복제 세트 태그
복제본 세트 태그는 많은 사용 사례에 적용할 수 있지만 주로 지역 복제 시나리오에서 읽기 지역성과 전용 노드에 대한 특정 워크로드(예:무거운 분석 작업)를 대상으로 하는 데 사용됩니다. 워크로드 대상 지정과 관련하여 태그가 지정된 노드는 다른 노드가 사용하지 않는 인덱스를 사용할 수 있으므로 별도로 검사해야 합니다. 지역 복제의 경우도 마찬가지이지만 더 드물긴 합니다. 고려 사항 1의 스크립트는 태그가 지정된 구성원을 확인하는 데 사용할 수 있으며 스크립트 맨 처음에 readPreference를 추가합니다. 다음은 읽기 기본 설정을 분석용으로 표시된 보조로 설정하는 예입니다.
db.getMongo().setReadPref('secondary', [ { "workload":"analytics" } ] ) .위의 두 번째 고려 사항의 마지막 단락에서 설명한 향후 접근 방식은 태그가 지정된 보조 항목도 확인하도록 확장할 수 있습니다.
고려 사항 4:TTL 색인
TTL 인덱스는 작업을 제공하지만 주요 용도는 데이터 정리입니다. TTL 모니터는 색인 작업으로 계산되지 않기 때문에 $indexStats에서 이러한 색인을 사용되지 않은 것으로 보고할 가능성이 매우 높습니다. $indexStats 보고서 결과를 기반으로 TTL 인덱스를 삭제하고 싶지 않으므로 스크립트에서 이러한 유형의 인덱스를 제외해야 합니다.
두 번째 섹션에서 populate idx 배열 스크립트를 약간 수정하면 작업이 수행됩니다. 두 번째 줄을 다음 줄로 바꾸면 idx 배열에 TTL 인덱스가 포함되지 않습니다.
db.foo.getIndexes().forEach(function(f){if (f.expireAfterSeconds==undefined) {idx.push(f.name)}})제외된 인덱스에 대한 논의에서 한 단계 더 나아가 _id도 이 범주에 속한다고 주장할 수 있습니다. 분명히 _id 인덱스가 사용되지 않더라도 삭제할 수 없습니다. _id 인덱스를 건드리지 않는 컬렉션은 재설계가 필요할 수 있습니다.
고려 사항 5:샤딩된 클러스터
샤딩된 클러스터와 관련하여 $indexStats의 출력을 평가하기 전에 고려해야 할 두 가지 항목이 있습니다. 첫째, 샤딩된 컬렉션에 대해 $indexStats를 사용할 때 샤드 키 인덱스가 미사용으로 분류될 수 있습니다. 예를 들어 {_id:”hashed”}에서 샤딩된 쓰기 작업이 많은 컬렉션 (고른 쓰기 배포를 위해) 그러나 _id 인덱스를 활용할 수 있는 읽기/업데이트/삭제 작업이 없습니다. 이 경우 {_id:"hashed"} 미사용으로 보고됩니다. 인덱스를 삭제하면 샤딩된 클러스터가 손상될 수 있으므로 삭제하는 것은 좋지 않습니다. 'stats.foo' 컬렉션에 대한 shard key를 제외하고 싶다면 2번(2차 읽기)에서 설명한 메소드에 다음 스크립트를 첨부하면 shard key는 idx 배열에서 제외됩니다.
shardkey=db.getSiblingDB('config').collections.findOne({_id:'stats.foo'},{_id:0,key:1});db.foo.getIndexes().forEach(함수 (f){if (JSON.stringify(f.key)!=JSON.stringify(shardkey.key)) {printjson(shardkey.key);printjson(f.key);idx.push(f.name)}} )또 다른 고려 사항은 $indexStats의 출력과 관련이 있습니다. 이제 컬렉션이 존재하는 모든 샤드의 통계를 반환하기 때문입니다. 흔하지는 않지만 인덱스가 일부 샤드에서 사용되지 않은 것으로 보고하는 반면 다른 샤드에서는 사용하는 경우가 있습니다. 불량한 샤드 키가 원인일 수 있지만 가장 일반적인 시나리오는 "인덱스 커버"입니다.
다음은 커버링 인덱스를 더 잘 이해하기 위한 예입니다. 인덱스 {a:1} 및 {a:1,b:1} 모두 'a' 필드에서 동등 일치를 제공할 수 있습니다. shardA의 옵티마이저가 {a:1}을 선택하고 shardB가 {a:1,b:1}을 선택하면 두 인덱스 모두 최소 하나의 샤드에 대해 사용되지 않은 것으로 보고합니다.
문제는 전 세계적으로 사용되지 않는 인덱스를 찾는 것입니다. 고려 사항 2 스크립트(2차 읽기)의 집계 부분(forEach 루프 뒤)을 다음 스크립트로 교체하면 작업이 수행됩니다.
db.foo.aggregate( [ { $indexStats:{ } } , {$group:{_id:"$name",number :{$sum:"$accesses.ops"}}}] ).forEach( function(f){if (f.number>0) { var index =idx.indexOf(f._id); if (index> -1) {idx.splice(index, 1);};}})사전>또 다른 과제는 부분적으로 사용 중인 인덱스를 찾는 것입니다. 이 정보는 중복 인덱스 또는 인덱스 식별 오작동과 관련하여 도움이 될 수 있습니다. idx 배열을 사용하여 다음 집계/스크립트가 전역적으로 사용되지 않는 인덱스로 채워지고 부분적으로 사용되는 인덱스를 보고합니다.
db.foo.aggregate( [ { $indexStats:{ } },{$match:{name:{$nin:idx},"accesses.ops":0}}]).forEach(함수(f) {print("인덱스 "+f.name+"는 샤드/호스트 " +f.host)에서 부분적으로 사용되지 않은 것으로 보고합니다.})
고려 사항 6:가장 적게 사용되는 인덱스
사용되지 않는 인덱스를 발견하고 삭제하는 것도 중요하지만 가장 적게 사용된 인덱스를 평가하는 것도 중요합니다. 인덱스가 일주일 또는 몇 달에 한 번 또는 두 번만 액세스되는 경우 작업 부하에 필요하지 않거나 유익하지 않다는 의미일 수 있습니다. 다음 집계를 사용하여 가장 적게 사용된 인덱스를 주기적으로 확인하는 것은 항상 좋은 방법입니다.
db.foo.aggregate( [ { $indexStats:{ } },{$match:{"accesses.ops":{$gt:0}}},{$group:{_id:"$name", 번호 :{$sum:"$accesses.ops"}}},{$sort:{number:1}}] )그런 다음 인덱스를 삭제하거나 인덱스 정의를 변경하거나 스키마/애플리케이션 논리를 변경하여 인덱스를 중복으로 만드는 등 적절한 조치를 취하십시오.
우리는 당신을 위해 여기 있습니다
MongoDB 인스턴스에 대한 인덱스 정리를 도와드리겠습니다. [email protected]에 대한 티켓을 만들고 색인을 개선하는 데 도움을 드리기만 하면 됩니다!