Можно ли улучшить скорость Монгоэкспорта? - PullRequest
0 голосов
/ 03 мая 2018

У меня есть коллекция MongoDB 3.6.2.0 на 130 миллионов строк. Он имеет несколько простых полей и 2 поля с вложенными документами JSON. Данные хранятся в сжатом формате (zlib).

Мне нужно как можно быстрее экспортировать одно из встроенных полей в формат JSON. Тем не менее, Монгоэкспорт берет навсегда. После 12 часов работы он обработал только 5,5% данных, что для меня слишком медленно.

Процессор не занят. Монгоэкспорт кажется однопоточным.

Команда экспорта, которую я использую:

mongoexport -c places --fields API \
    --uri mongodb://user:pass@hostIP:hostPort/maps?authSource=admin \
    -o D:\APIRecords.json

Это на самом деле команда getMore, которая неоправданно медленная:

2018-05-02T17:59:35.605-0700 I COMMAND  [conn289] command maps.places command: getMore { getMore: 14338659261, collection: "places", $db: "maps" } originatingCommand: { find: "places", filter: {}, sort: {}, projection: { _id: 1, API: 1 }, skip: 0, snapshot: true, $readPreference: { mode: "secondaryPreferred" }, $db: "maps" } planSummary: COLLSCAN cursorid:14338659261 keysExamined:0 docsExamined:5369 numYields:1337 nreturned:5369 reslen:16773797 locks:{ Global: { acquireCount: { r: 2676 } }, Database: { acquireCount: { r: 1338 } }, Collection: { acquireCount: { r: 1338 } } } protocol:op_query 22796ms

Я попытался запустить несколько команд с опциями --SKIP и --LIMIT в отдельных процессах, подобных этому

mongoexport -c places --SKIP 10000000 --LIMIT 10000000 --fields API \
    --uri mongodb://user:pass@hostIP:hostPort/maps?authSource=admin \
    -o D:\APIRecords.json
mongoexport -c places --SKIP 20000000 --LIMIT 10000000 --fields API \
    --uri mongodb://user:pass@hostIP:hostPort/maps?authSource=admin \
    -o D:\APIRecords.json

и т.д.. Но я не смог закончить ждать, пока команда с первым ненулевым SKIP даже не запустится!

Я также пробовал с опцией --forceTableScan, которая не имела никакого значения.

У меня нет индексов на таблице мест.

Моя конфигурация хранилища:

journal.enabled: false
wiredTiger.collectionConfig.blockCompressor: zlib

Статистика сбора:

'ns': 'maps.places',
'size': 2360965435671,
'count': 130084054,
'avgObjSize': 18149,
'storageSize': 585095348224.0

Характеристики моего сервера:

Windows Server 2012 R2 x64
10Gb RAM 4TB HDD 6 cores Xeon 2.2Ghz

Я провел тест, и с SSD у него такая же ужасная пропускная способность, как и с HDD.

Мой вопрос:

Почему чтение так медленно? Кто-нибудь еще испытывал такую ​​же проблему? Можете ли вы дать мне какие-либо советы о том, как ускорить дамп данных?

Обновление

Я переместил БД на быстрые SSM-накопители NVME, и теперь я думаю, что могу более четко изложить свои опасения по поводу производительности чтения MongoDB.

Зачем нужна эта команда, которая ищет блок документов, не имеющих определенного поля:

2018-05-05T07:20:46.215+0000 I COMMAND  [conn704] command maps.places command: find { find: "places", filter: { HTML: { $exists: false }, API.url: { $exists: true } }, skip: 9990, limit: 1600, lsid: { id: UUID("ddb8b02c-6481-45b9-9f84-cbafa586dbbf") }, $readPreference: { mode: "secondaryPreferred" }, $db: "maps" } planSummary: COLLSCAN cursorid:15881327065 keysExamined:0 docsExamined:482851 numYields:10857 nreturned:101 reslen:322532 locks:{ Global: { acquireCount: { r: 21716 } }, Database: { acquireCount: { r: 10858 } }, Collection: { acquireCount: { r: 10858 } } } protocol:op_query 177040ms

дает только скорость чтения 50 Мб / с на флешку? Это явно производительность однопоточного случайного (рассеянного) чтения. Принимая во внимание, что я только что доказал, что этот накопитель обеспечивает скорость чтения / записи 1 Гбит / с.

Если говорить о внутренностях Монго, не было бы разумнее читать файл BSON в последовательном порядке и получить 20-кратное улучшение скорости сканирования? (И, поскольку мои блоки сжаты zlib, а сервер имеет 16 ядер, лучше декодировать извлеченные фрагменты в одном или нескольких вспомогательных потоках?) Вместо итерации документа BSON за документом.

Я также могу подтвердить, что даже если я не указываю никаких фильтров запросов и явно хочу повторить ВСЮ коллекцию, быстрого последовательного чтения файла BSON не происходит.

Ответы [ 4 ]

0 голосов
/ 17 мая 2018

Вы можете попробовать использовать библиотеку pandas и joblib для экспорта в файл JSON по частям. Вы можете обратиться к этой сути для обработки данных в MongoDB.

from pandas import DataFrame
from joblib import Parallel,delayed

def process(idx,cursor):
    file_name = "fileName"+str(idx)+".json"
    df = DataFrame(list(cursor))
    df.to_json(file_name, orient='records')

#make a list of cursors.. you can read the parallel_scan api of pymongo

cursors = mongo_collection.parallel_scan(no_of_parts_of_collection)
Parallel(n_jobs=4)(delayed(process)(idx,cursor) for idx,cursor in enumerate(cursors)

Параметр n_jobs должен вызывать процессы, равные указанному числу. Каждый процесс должен содержать одно ядро. Я использовал 4, так как на вашем сервере доступно 6 ядер. parallel_scan() API принимает число и делит коллекцию на части, равные предоставленному числу. Вы можете попробовать большее число, чтобы разбить коллекцию на равномерно разделенные курсоры.

Я пробовал аналогичный подход, но подпись и определение моей process функции были другими. Я смог обработать 2,5 млн записей менее чем за 20 минут. Вы можете прочитать мой ответ , чтобы получить представление о том, чего именно я пытался достичь.

0 голосов
/ 16 мая 2018

mongoexport - это клиентская библиотека, которая использует публичный API и сокетное соединение с самой mongodb.

Так что у него нет доступа к документу BSON на диске

Это похоже на то, что вы упомянули?

https://docs.mongodb.com/manual/core/backups/#back-up-by-copying-underlying-data-files

mongodump также может быть вариантом для вас

0 голосов
/ 17 мая 2018

Есть много факторов, которые ограничивают эффективность экспорта.

  • Размер данных относительно велик по сравнению с доступной памятью: ~ 2 ТБ против ~ 5 ГБ кеша WiredTiger (если установлено по умолчанию). То есть:
    • Весь кеш WiredTiger может содержать только в лучшем случае ~ 0,22% коллекции, на самом деле, скорее всего, намного меньше, чем этот, поскольку кэш будет содержать данные из других коллекций и индексов.
    • Это означает, что WiredTiger нужно очень часто извлекать данные с диска, исключая при этом текущее содержимое кэша. Если набор реплик активно используется, это будет означать удаление «грязных» данных из кэша и сохранение их на диске, что займет время.
    • Обратите внимание, что документы внутри кэша WiredTiger не сжимаются.
  • Коллекция содержит большие документы, из которых вам нужна только одна часть. Это означает, что для обработки документов требуется дополнительное время.
  • Коллекция сжата с помощью zlib, а это означает, что для распаковки документов необходимо использовать дополнительное время.
  • Значение readPreference равно secondaryPreferred, что означает, что он будет пытаться читать с дополнительного устройства. Если набор реплик активно записывается, операции применения оплога к вторичному устройству блокируют читателей. Это добавит дополнительную задержку.

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

Редактировать: параллельная работа mongoexport может быть полезна в этом случае:

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

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

Для этого разделите пространство имен _id, соответствующее количеству mongoexport процесса, который вы планируете запустить.

Например, если у меня есть 200 000 документов, начиная с _id:0 до _id:199,999 и используя 2 mongoexport процессов:

mongoexport -q '{"_id":{"$gte":0, "$lt":100000}}' -d test -c test > out1.json &
mongoexport -q '{"_id":{"$gte":100000, "$lt":200000}}' -d test -c test > out2.json &

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

Тестируя этот рабочий процесс с 1 процессом, 2 процессами, 4 процессами и 8 процессами, я пришел к следующим временным интервалам:

Использование 1 процесса:

real    0m32.720s
user    0m33.900s
sys 0m0.540s

2 процесса:

real    0m16.528s
user    0m17.068s
sys 0m0.300s

4 процесса:

real    0m8.441s
user    0m8.644s
sys 0m0.140s

8 процессов:

real    0m5.069s
user    0m4.520s
sys 0m0.364s

В зависимости от доступных ресурсов выполнение 8 mongoexport параллельных процессов, по-видимому, ускоряет процесс в ~ 6 раз. Это было проверено на машине с 8 ядрами.

Примечание : ответ Хэлфера схож с идеей, хотя в этом ответе в основном делается попытка выяснить, есть ли какая-то польза от параллельного вызова mongoexport.

0 голосов
/ 15 мая 2018

Я не работаю с Mongo, но можно воспользоваться общим приемом: создать простое приложение, которое эффективно последовательно запрашивает все данные, фильтрует их и сохраняет в нужном формате.

Если вам нужно сохранить в сложном формате и нет библиотек для работы с ним (я действительно сомневаюсь в этом), все равно может быть эффективно прочитать все, отфильтровать, поместить обратно во временную коллекцию, экспортировать эту коллекцию полностью, отбросьте временную коллекцию.

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