2017-01-19 4 views
1

Я бегу MongoDB 3.0 и у меня есть коллекция, которая имеет (среди прочих) следующие показатели:

{ 
    "v" : 1, 
    "key" : { 
     "run_state" : 1 
    }, 
    "name" : "run_state_1", 
    "ns" : "xxx.yyyy" 
}, 
{ 
    "v" : 1, 
    "key" : { 
     "visibility_state" : 1 
    }, 
    "name" : "visibility_state", 
    "ns" : "xxx.yyyy" 
}, 
{ 
    "v" : 1, 
    "key" : { 
     "affiliate_id" : "hashed" 
    }, 
    "name" : "affiliate_id_hashed", 
    "ns" : "xxx.yyyy" 
} 

Мне нужно получить все документы, соответствующие некоторым условиям.
Одним из них является наличие поля affiliate_id.

Если у меня есть запрос, который включает в себя $exists оператора и другие условия, например:

db.getCollection('campaigns') 
    .find({ 
    affiliate_id : { $exists : true }, 
    visibility_state : 'showing', 
    run_state : 'running' 
    }) 
    .explain() 

я получаю следующий план выполнения:

{ 
    "winningPlan" : { 
    "stage" : "KEEP_MUTATIONS", 
     "inputStage" : { 
     "stage" : "FETCH", 
      "filter" : { 
      "$and" : [ 
       { 
       "visibility_state" : { 
        "$eq" : "showing" 
       } 
       }, 
       { 
       "affiliate_id" : { 
        "$exists" : true 
       } 
       } 
      ] 
      }, 
      "inputStage" : { 
      "stage" : "IXSCAN", // GOOD, it uses the index (: 
       "keyPattern" : { 
       "run_state" : 1 
       }, 
       "indexName" : "run_state_1", 
       "isMultiKey" : false, 
       "direction" : "forward", 
       "indexBounds" : { 
       "run_state" : [ 
        "[\"running\", \"running\"]" 
       ] 
       } 
      } 
     } 
    } 
} 

Таким образом, запрос будет использовать индекс и будет выполнять очень быстро.

Однако, если запрос содержит только оператор $exists, я получаю:

{ 
    "winningPlan" : { 
    "stage" : "COLLSCAN", // Full collection scan D: 
     "filter" : { 
     "affiliate_id" : { 
      "$exists" : true 
     } 
     }, 
     "direction" : "forward" 
    }, 
} 

Так этот запрос будет игнорировать индекс и выполнить полный сбор и сканирование будет выполнять очень плохо.

Если вместо этого я использую $ne : null, результаты будут такими же, как указано выше.


Я понимаю, что в первом случае индекс поиск будет происходить только в run_state поле и на следующем этапе будет просто отфильтровать неправдоподобные данные.

Мне просто интересно, почему Mongo не использует индекс для $exists или $ne : null.


Редактировать

Как @lascort предложил в комментариях, я изменил тип индекса быть регулярным отсортированный один. И теперь используется индекс, но он все еще имеет этот этап фильтра.

{ 
    "winningPlan" : { 
    "stage" : "KEEP_MUTATIONS", 
     "inputStage" : { 
     "stage" : "FETCH", 
      "filter" : { 
      "affiliate_id" : { 
       "$exists" : true 
      } 
      }, 
      "inputStage" : { 
      "stage" : "IXSCAN", 
       "keyPattern" : { 
       "affiliate_id" : 1.0 
       }, 
       "indexName" : "affiliate_id_1", 
       "isMultiKey" : false, 
       "direction" : "forward", 
       "indexBounds" : { 
       "affiliate_id" : [ 
        "[MinKey, MaxKey]" 
       ] 
       } 
      } 
     } 
    }, 
} 

Я также создал индекс соединения с run_state, visibility_state и affiliate_id клавиш, сохраняя при этом отдельные из них. Если я выполнить запрос, содержащий 3 поля, я получаю то же самое, прежде чем создавать составной индекс, с FETCH стадии, содержащей операцию ФИЛЬТР:

{ 
    "winningPlan" : { 
    "stage" : "KEEP_MUTATIONS", 
     "inputStage" : { 
     "stage" : "FETCH", 
      "filter" : { 
      "$and" : [ 
       { 
       "visibility_state" : { 
        "$eq" : "showing" 
       } 
       }, 
       { 
       "affiliate_id" : { 
        "$exists" : true 
       } 
       } 
      ] 
      }, 
      "inputStage" : { 
      "stage" : "IXSCAN", 
       "keyPattern" : { 
       "run_state" : 1.0 
       }, 
       "indexName" : "run_state_1", 
       "isMultiKey" : false, 
       "direction" : "forward", 
       "indexBounds" : { 
       "run_state" : [ 
        "[\"running\", \"running\"]" 
       ] 
       } 
      } 
     } 
    } 
}, 

Если я падение 3 односпальных индексов, то я получаю это:

{ 
    "winningPlan" : { 
    "stage" : "KEEP_MUTATIONS", 
     "inputStage" : { 
     "stage" : "FETCH", 
      "filter" : { 
      "affiliate_id" : { 
       "$exists" : true 
      } 
      }, 
      "inputStage" : { 
      "stage" : "IXSCAN", 
       "keyPattern" : { 
       "visibility_state" : 1.0, 
        "run_state" : 1.0, 
        "affiliate_id" : 1.0 
       }, 
       "indexName" : "visibility_state_1_run_state_1_affiliate_id_1", 
       "isMultiKey" : false, 
       "direction" : "forward", 
       "indexBounds" : { 
       "visibility_state" : [ 
        "[\"showing\", \"showing\"]" 
       ], 
        "run_state" : [ 
        "[\"running\", \"running\"]" 
        ], 
        "affiliate_id" : [ 
        "[MinKey, MaxKey]" 
        ] 
       } 
      } 
     } 
    } 
} 

Который кажется более эффективным.

+0

Я не совсем уверен, что происходит, как и почему он не будет использовать индекс, но это, кажется, проблема с индекс 'affiliate_id'« hashed ». Если вы создадите обычный индекс, он будет работать нормально. По другому вопросу .. для вашего первого запроса вы должны рассмотреть использование составного индекса, он должен работать лучше, чем только с использованием индекса run_state. А для вашего второго запроса (единственного с условием '$ exists: true') разреженный индекс звучит хорошо. – lascort

+0

Спасибо @ lascort, регулярный индекс сделал это!: D О составных индексах, должен ли я создать новый или рассмотреть вопрос о слиянии этого 3 в 1? Каковы предельные накладные расходы на добавление нового индекса в монго? –

ответ

1

Я отвечу на ваш комментарий здесь, так как это будет легче читать, и оно, вероятно, займет более 600 символов.

Что касается того, следует ли создавать новый составной индекс и отбрасывать другие или сохранять их, действительно зависит от множества переменных, например, сколько других индексов у вас есть, насколько велики ваши коллекции, какова ваша аппаратная конфигурация, и т.п.

Принимая это во внимание, если ваш план заключается в создании единого составного индекса для всех ваших запросов, тогда вам действительно нужно подумать заранее и иметь в виду, что может сделать комплексный индекс. Например ..

db.collection.createIndex({field1: 1, field2: 1}) не даст таких же результатов, как db.collection.createIndex({field2: 1, field1: 1}). Если вы создадите более позднюю, фильтрация ваших запросов только на field1 не будет использовать индекс.

Но сосредоточитесь больше на своем сценарии .. если вы создадите индекс с полями run_state, affiliate_id, visibility_state в этом порядке. Ваши запросы с JUST условием {$exists: false} также не будут использовать индекс. Я приведу документацию о манго, чтобы объяснить это дальше.

Приставки

Index префиксы начальные подмножества индексированных полей. Для примера рассмотрим следующий составной индекс:

{ "item": 1, "location": 1, "stock": 1 } 

Индекс имеет следующие индексные префиксы:

{ item: 1 } 
{ item: 1, location: 1 } 

Для индекса соединения, MongoDB может использовать индекс для поддержки запросов на указательном префиксов , Таким образом, MongoDB может использовать индекс для запросов по следующим направлениям:

  • поле элемента
  • поле элемента и поле местоположения
  • поле элемента и поле местоположения и поле запаса

MongoDB также может использовать индекс для поддержки запроса на пункт и фондовых полей, поскольку поле элемента соответствует префиксу. Тем не менее, индекс будет не так эффективен в поддержке запроса, как индекс только для позиции и запаса.

Однако, MongoDB не может использовать индекс для поддержки запросов, которые включают следующие поля, так как без поля элемента, ни один из перечисленных полей не соответствуют индексу приставкой:

  • поле Расположение
  • фондового поля, или
  • расположение и фондовых полей.

Вот ссылка на эту часть документации https://docs.mongodb.com/v3.0/core/index-compound/