Индексирование при поиске в MongoDB не так быстро, как ожидалось?

В моей базе данных есть уникальная коллекция твиттер-слов. Документ внутри него имеет следующую форму:

{
    _id: <some-object-id>,
    initial: "t",
    word: "the",
    count: 986,
    tweets: <position information for the given word>
}

Я пробовал следующие коды для создания индексов:

db.tweet_words.create_index([("word", pymongo.ASCENDING), ("initial", pymongo.ASCENDING)], background=True)

и

db.tweet_words.create_index("word", background=True)

и

db.tweet_words.create_index([("word", pymongo.HASHED)], background=True)

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

Вот мои команды обновления:

from pymongo import UpdateOne
# connect to db stuff
# create indexing using one of the approaches above
commands = []
for word in words: # this is actually not the real loop I've used but it fits for this example
    # assume tweet_id's and position is calculated here
    initial = word[0]
    ret = {"tweet_id": tweet_id, "pos": (beg, end)} # additional information about word
    command = UpdateOne({"word": word, "initial": initial}, # use query {"word": word} only if one of bottom two indexing strategy is chosen
        {
            "$setOnInsert": {"initial": initial},
            "$inc": {"count": 1},
            "$push": {"tweets": ret},
        },
    commands.append(command)
    if len(commands) % 1000 == 0:
        db.tweet_words.bulk_write(commands, ordered=False)
        commands = []

Вы можете найти настоящий код здесь.

Вот результат работы pprint(db.tweet_words.find({"word": word}).explain()) (не удалось придумать, как использовать метод объяснения на UpdateOne или bulk_write), когда в коллекции присутствует ~ 130 тыс. Документов:

{'executionStats': {'allPlansExecution': [],
                    'executionStages': {'advanced': 1,
                                        'alreadyHasObj': 0,
                                        'docsExamined': 1,
                                        'executionTimeMillisEstimate': 0,
                                        'inputStage': {'advanced': 1,
                                                       'direction': 'forward',
                                                       'dupsDropped': 0,
                                                       'dupsTested': 0,
                                                       'executionTimeMillisEstimate': 0,
                                                       'indexBounds': {'initial': ['[MinKey, '
                                                                                   'MaxKey]'],
                                                                       'word': ['["seval", '
                                                                                '"seval"]']},
                                                       'indexName': 'word_1_initial_1',
                                                       'indexVersion': 2,
                                                       'invalidates': 0,
                                                       'isEOF': 1,
                                                       'isMultiKey': False,
                                                       'isPartial': False,
                                                       'isSparse': False,
                                                       'isUnique': False,
                                                       'keyPattern': {'initial': 1,
                                                                      'word': 1},
                                                       'keysExamined': 1,
                                                       'multiKeyPaths': {'initial': [],
                                                                         'word': []},
                                                       'nReturned': 1,
                                                       'needTime': 0,
                                                       'needYield': 0,
                                                       'restoreState': 0,
                                                       'saveState': 0,
                                                       'seeks': 1,
                                                       'seenInvalidated': 0,
                                                       'stage': 'IXSCAN',
                                                       'works': 2},
                                        'invalidates': 0,
                                        'isEOF': 1,
                                        'nReturned': 1,
                                        'needTime': 0,
                                        'needYield': 0,
                                        'restoreState': 0,
                                        'saveState': 0,
                                        'stage': 'FETCH',
                                        'works': 2},
                    'executionSuccess': True,
                    'executionTimeMillis': 0,
                    'nReturned': 1,
                    'totalDocsExamined': 1,
                    'totalKeysExamined': 1},
 'ok': 1.0,
 'queryPlanner': {'indexFilterSet': False,
                  'namespace': 'twitter.tweet_words',
                  'parsedQuery': {'word': {'$eq': 'seval'}},
                  'plannerVersion': 1,
                  'rejectedPlans': [],
                  'winningPlan': {'inputStage': {'direction': 'forward',
                                                 'indexBounds': {'initial': ['[MinKey, '
                                                                             'MaxKey]'],
                                                                 'word': ['["seval", '
                                                                          '"seval"]']},
                                                 'indexName': 'word_1_initial_1',
                                                 'indexVersion': 2,
                                                 'isMultiKey': False,
                                                 'isPartial': False,
                                                 'isSparse': False,
                                                 'isUnique': False,
                                                 'keyPattern': {'initial': 1,
                                                                'word': 1},
                                                 'multiKeyPaths': {'initial': [],
                                                                   'word': []},
                                                 'stage': 'IXSCAN'},
                                  'stage': 'FETCH'}},
 'serverInfo': {'gitVersion': 'f288a3bdf201007f3693c58e140056adf8b04839',
                'host': 'MostWanted',
                'port': 27017,
                'version': '4.0.4'}}

Есть ли в моем коде разрешимое узкое место? Или это так хорошо, как получается?

Где вы сначала назначаете commands чему-то в своем коде?

Jon Clements 24.12.2018 16:02

Обновил код. Это не настоящий код, который я использовал в своем проекте. Вот почему я по ошибке его пропускаю.

Talha Çolakoğlu 24.12.2018 16:04

Да ... Я не вижу ничего очевидного, поэтому немного обеспокоен тем, что это не настоящий код, вы могли его упростить - особенно если вы только что пропустили нужную строку :)

Jon Clements 24.12.2018 16:05

В любом случае - заставьте его объяснить выполняемый запрос и убедитесь, что он решает использовать индексы ... нажатие на список, хотя, когда он становится большим, может включать переписывание страниц, хотя ... так что может быть это - трудно сказать. Я предлагаю начать с объяснения плана запроса и посмотреть, указывает ли это на что-нибудь очевидное.

Jon Clements 24.12.2018 16:07

Не знаю, как использовать метод explain для работы с bulk_write. Вместо этого я запросил слово и получил объяснение. Вы можете проверить результат выше.

Talha Çolakoğlu 24.12.2018 17:21

Спасибо ... Я давно не использовал монго - так что не эксперт - просто советовал эту информацию. должен быть в самом Q, потому что он, надеюсь, поможет кому-то указать на что-то, если возможно ... удачи, тем не менее, с решением.

Jon Clements 24.12.2018 17:41

Текущий вывод объяснения запроса, который вы включили, указывает 'executionTimeMillis': 0` только с одним поиском по индексу и документу. Любые возможности для улучшения заключаются в сокращении ненужных накладных расходов (например, удалении неиспользуемых индексов) или поиске узких мест в коде или развертывании. Не могли бы вы предоставить более подробную информацию о том, что вы считаете медленным - пора ли выполнять операцию массовой записи? Если да: сколько документов вы вставляете / обновляете, какие ресурсы сервера у вас есть (в частности, RAM и диск) и каков общий dataSize для вашего развертывания?

Stennie 24.12.2018 23:17

Я пытаюсь найти уникальные слова примерно в 13 млн твитов. Если предположить, что в среднем твит содержит 10 слов, это означает, что будет выполнено около 130 миллионов команд вставки / обновления. В моей последней попытке я запускал скрипт почти 16 часов подряд, затем мне нужно его убить. Потому что он обработал около 1 миллиона твитов. У моего компьютера 28 ГБ оперативной памяти, а на жестком диске - Hitachi HDS721010CLA332. dataSize всей базы данных twitter - это {dataSize : 202887823327}, и вы можете найти настоящий код, который я запускал в этом посте.

Talha Çolakoğlu 25.12.2018 00:04
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
8
52
0

Другие вопросы по теме