MongoDB оптимальный индекс |Поведение планировщика запросов - PullRequest
1 голос
/ 08 марта 2019

У меня есть защищенный кластер MongoDB, на котором размещено 250 с лишним миллионов документов.

Структура документа выглядит следующим образом:

{
    "app_id": "whatever", 
    "created": ISODate("2018-05-06T12:13:45.000Z"),
    "latest_transaction": ISODate("2019-03-06T11:11:40.000Z"),
    "anotherField1": "Str", "anotherField2": "Str", ...otherfields
}
{
    "app_id": "whatever", 
    "created": ISODate("2018-04-06T12:13:45.000Z"),
    "latest_transaction": ISODate("2019-03-06T11:11:40.000Z"),
    "uninstalled": ISODate("2019-03-07T11:11:40.000Z"),
    "anotherField1": "Str", "anotherField2": "Str", ...otherfields
}

Поэтому в некоторых документах поле удалено .а некоторые нет.

Ниже приведен запрос к коллекции (это объяснение Pymongo, извините за datetime.datetime s):

{
    '$and': [
        {'app_id': {'$eq': 'whatever'}},
        {'created': {'$lt': datetime.datetime(2019, 3, 7, 0, 0)}},
        {'latest_transaction': {'$gt': datetime.datetime(2019, 2, 5, 0, 0)}},
        {'$nor': [{'uninstalled': {'$lt': datetime.datetime(2019, 3, 7, 0, 0)}}]}
    ]
}

Здесьэто два соответствующих индекса, которые у меня есть в коллекции:

Index1: {"created": 1, "latest_transaction": -1, "uninstalled": -1, "app_id": 1}
Index2: {'app_id': 1, 'anotherField1': 1, 'anotherField2': 1}

Теперь проблема в том, что планировщик запросов MongoDb никогда не выбирает Index1 , который у меня есть в коллекции для этой же цели!

Мое первоначальное впечатление было, что запрос будет использовать закрытый индекс с тем, как я структурировал индексы [следовательно, невероятно быстро], , но, как ни странно, mongodb использует Index2 и все слишком медленно , иногда занимает 10 минут + иногда и обычно около 6 минут для результирующего набора из 1,5 миллиона документов [т.е. соответствующий app_id имеет около 1,5 миллиона документов].

Вот вывод команды объясненияв запросе показано отклонено плана с использованием «Index1»

{
    'inputStage': {
        'inputStage': {
            'direction': 'forward',
            'indexBounds': {
                'app_id': ['["whatever", "whatever"]'],
                'created': ['(true, new Date(1551916800000))'],
                'latest_transaction': ['[new Date(9223372036854775807), new Date(1549324800000))'],
                'uninstalled': ['[MaxKey, new Date(1551916800000)]', '[true, MinKey]']
            },
            'indexName': 'created_1_latest_transaction_-1_uninstalled_-1_app_id_1',
            'indexVersion': 2,
            'isMultiKey': False,
            'isPartial': False,
            'isSparse': False,
            'isUnique': False,
            'keyPattern': {
                'app_id': 1.0,
                'created': 1.0,
                'latest_transaction': -1.0,
                'uninstalled': -1.0
            },
            'multiKeyPaths': {'app_id': [], 'created': [], 'latest_transaction': [], 'uninstalled': []},
            'stage': 'IXSCAN'},
        'stage': 'FETCH'},
    'stage': 'SHARDING_FILTER'
}

И далее победа планировать с использованием нерелевантно, непокрыто, Index2 :

{'inputStage': {
    'inputStage': {'direction': 'forward',
                   'indexBounds': {
                       'app_id': ['["whatever", "whatever"]'],
                       'anotherField1': ['[MinKey, MaxKey]'],
                       'anotherField2': ['[MinKey, MaxKey]']},
                   'indexName': 'app_id_1_anotherField2_1_anotherField1_1',
                   'indexVersion': 2,
                   'isMultiKey': False,
                   'isPartial': False,
                   'isSparse': False,
                   'isUnique': False,
                   'keyPattern': {'app_id': 1, 'anotherField1': 1, 'anotherField2': 1},
                   'multiKeyPaths': {'app_id': [], 'anotherField1': [], 'anotherField2': []},
                   'stage': 'IXSCAN'},
    'stage': 'FETCH'},
    'stage': 'SHARDING_FILTER'
}
  • Есть идеи, почему mongodb не будет правильно использовать мой индекс?
  • Это потому, что удалено может отсутствовать в некоторых документах?
  • Некоторые пояснения относительно направления индексов при выполнении запросов составных дат также будут весьма полезны, может быть, причина в направлениях индекса?(1, -1, -1, 1)

Спасибо!:)

------------ РЕДАКТИРОВАТЬ --------------

Полный результатОбъяснение немного длинное, поэтому я вставил его сюда , оно объясняет выбор indexPlanner индекса (Index2).

Также и о shard_key, он полностью отличается от того, что запрашивается здесьВот почему я определяю отдельный конкретный индекс только для этого запроса.(ключ шарда является составным индексом для (app_id, android_id, some_other_field_not_in_query).

Ответы [ 2 ]

1 голос
/ 10 марта 2019

Отвечая на мой собственный вопрос здесь,

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

Так что в основномПотребовалось несколько часов, чтобы понять, что Index1: {"created": 1, "latest_transaction": -1, "uninstalled": -1, "app_id": 1} должен иметь более высокий балл, чем другой индекс, в то время как я ожидал, что изменение в поведении будет мгновенным.

Присвоенный балл и текущие оценки планировщика также можно получить в Mongodb , следующие команды помогли мне выяснить оценки и как они прогрессируют во времени.

var queryShape = db.installation.getPlanCache().listQueryShapes()[IDX]
db.installation.getPlanCache().getPlansByQuery(queryShape)
1 голос
/ 08 марта 2019

Покрываемые запросы требуют правильной проекции - убедитесь, что вы просите вернуть только те поля, которые есть в индексе В частности, для сегментированных коллекций индекс также должен содержать ключ сегмента: https://docs.mongodb.com/manual/core/query-optimization/#restrictions-on-sharded-collection.

Более подробную информацию можно получить из explain, используя параметр allPlansExecution . Он покажет вам, как планировщик запускает выборки и почему index2 выигрывает.

https://github.com/mongodb/mongo/blob/master/src/mongo/db/query/plan_ranker.cpp#L191 - это способ подсчета очков:

baseScore = 1
productivity = advanced / works // the main one 

tieBreak = very_small_number
   + noFetchBonus // 0 for not covered queries
   + noSortBonus // 0 for no sort
   + noIxisectBonus // 0 for index intersection

score = baseScore + productivity + tieBreakers

Он выбирает план с более высоким баллом по первым 100 возвращенным документам (расширенный), который обычно дает хорошее представление о том, как он будет работать для всего запроса. Если вы сомневаетесь в этом, попробуйте подсказку другого индекса и проверьте, работает ли он быстрее.

UPDATE

ключ шарда является составным индексом (app_id, android_id, some_other_field_not_in_query

Кинда объясняет это. app_id - это общий префикс в ключе шардинга и Index2. Это означает, что с помощью этого индекса Монго может мгновенно решить, какие шарды запрашивать. Измените порядок полей в Index1 в соответствии с префиксом ключа шардинга:

Index1: {"app_id": 1, "created": 1, "latest_transaction": -1, "uninstalled": -1}

Существенные числа из объяснения:

   u'inputStage': {u'advanced': 0,
     u'indexName': u'created_1_latest_transaction_-1_uninstalled_-1_app_id_1',       


   u'inputStage': {u'advanced': 88,
     u'indexName': u'app_id_1_is_enabled_1_another_id_1',

   u'inputStage': {u'advanced': 12,
     u'indexName': u'app_id_1_uninstalled_1_is_enabled_1',

   u'inputStage': {u'advanced': 101,
     u'indexName': u'app_id_1_is_enabled_1_gaid_1',

Победитель - app_id_1_is_enabled_1_gaid_1, поскольку ему удалось вернуть 101 документ во время оценки. Тот, у которого нет префикса created_1_latest_transaction_-1_uninstalled_-1_app_id_1, как минимум, в 100 раз медленнее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...