У меня есть коллекция MongoDB с примерно 20M объектами. Ночью я удаляю данные за несколько финансовых месяцев и заменяю их импортом из CSV, который использует PyMongo 3.0, что-то через Django. Этот импорт занимает все больше времени по мере роста базы данных, а сервер и процесс начинают время от времени зависать. Одно только удаление (6M строк) на наших серверах AWS занимает ~ 14 минут. Я проиндексировал удаление по дате, которое является соответствующим полем.
Запрос, который я использую для удаления:
db.getCollection("x").remove({date: {$gte: ISODate('2018-04-01')}})
Код верхнего уровня, интегрированный с Pymongo, в котором может быть небольшая утечка памяти:
while keep_fetch:
raw_data = input_adapter.get_bulk() # in practice pulling 100,000 lines.
if raw_data is None:
raise Exception("bulk of data didn't return due to error,remove all last inserted docs")
if raw_data == {} or raw_data == [] :
log.debug("no more data to fetch")
keep_fetch = False
break
#check for maintenance before insert first bulk
if first_bulk == True: # first run-through, execute a delete if there is data.
first_bulk = False
#clean data in db to avoid duplicates
if 'maintain_before_insert' in dir(output_adapter):
# Call function that cleans a time period's data from the database, and does other maintenance.
if output_adapter.maintain_before_insert() == False:
raise Exception("collection maintenance failed")
#Run an input adapter that formats the data into a list for insertion into Mongo.
list_docs=input_adapter.format_data( raw_data )
#get last date for next task to start from
last_date = max(list_docs, key=lambda x:x['date'])['date']
output_adapter.store_bulk( list_docs ) # Mongo insert; it uses unmodified insert_many on the collection
gc.collect()
Мне любопытно, как я могу ускорить процесс. Я попытался выполнить массовое удаление без каких-либо улучшений. Мне любопытно, что индексы с истекшим сроком годности, но я не знаю, могут ли индексы с истекшим сроком действия остановиться, когда наш импорт устареет (после 3 полных месяцев просрочки он снова уменьшится до 2 и увеличится в размере на один день ночью)
Увеличение размера сервера AWS не влияет на скорость процесса. Мы используем сервер Django / Python для отправки запросов на удаление и вставку, и процессы вставки и удаления, похоже, занимают примерно одинаковое количество времени.
На сервере нет файла подкачки; Возможно, это проблема с настройкой сервера, и поэтому она больше подходит для ServerFault, но, поскольку это возможно, возникла ошибка кода, которую я задал здесь.
Я провел значительное количество исследований и попробовал несколько возможностей. Поможет ли обновление нашей версии PyMongo?
Данные процесса, Процесс 1
Показатели производительности при вставке одного (~ 675 МиБ) файла CSV:
Загрузка файла из S3 bucket: .45 секунд
Удаление записей (апрель-незаконченный июнь): 6 200 000 записей удалено в 14:18
Хранение записей: 100 000 записей вставляются каждые 11-12 секунд между [примечание: первые 100 000 записей загружаются в память Python перед удалением, поэтому цифры не являются точными] 20:15:21 и 20:32:11, всего Добавлено 16:50 и 6798155 записей
Размер таблицы после обработки: Количество: 19 367 660; Размер: 7,5 ГиБ, Размер хранилища: 3,6 ГиБ, Индексы: 2, Общий размер индекса: 486,3 МБ
Данные процесса, Процесс 2
Показатели производительности при вставке одного (~ 225 МиБ) файла CSV:
Загрузка файла из S3 bucket: .5 секунд
Удаление записей (апрель-незаконченный июнь): 2 399 827 записей удалено в 2:51
Хранение записей: 100 000 записей вставляются каждые 15 минут между [примечание: первые 100 000 записей загружаются в память Python перед удалением, поэтому цифры не являются точными] 20:37:10 и 20:43:37, всего Добавлено 6:27 и 2 417 669 записей
Размер таблицы после обработки: Количество: 6,332,293; Размер: 2,5 ГиБ, Размер хранилища: 1,1 ГиБ, Индексы: 3, Общий размер индекса: 264,3 МБ
Данные процесса, Процесс 3
Показатели производительности при вставке одного (~ 20 МиБ) файла CSV:
Загрузка файла из S3 bucket: .03 секунд
Удаление записей (апрель-незаконченный июнь): 341 317 записей удалено за 22 секунды
Хранение записей: 100 000 записей вставляются каждые 21 секунду между [примечание: первые 100 000 записей загружаются в память Python перед удалением, поэтому цифры не являются точными] 20:44:46 и 20:45:17, всего Добавлено 0:31 и 349 100 записей
Размер таблицы после обработки: Количество: 811,200; Размер: 145,7 МБ, Размер хранилища: 75,6 МБ, Индексы: 3, Общий размер индекса: 23,4 МБ
Данные процесса, Процесс 4
Показатели производительности при вставке одного (~ 20 МиБ) файла CSV:
Загрузка файла из S3 bucket: .5 секунд
Удаление записей (апрель-незаконченный июнь): 352 010 записей удалено в: 09
Хранение записей: 100 000 записей вставляются каждые 8 секунд между [примечание: первые 100 000 записей загружаются в память Python перед удалением, поэтому цифры не являются точными] 20:46:05 и 20:46:38, всего Добавлено 0:33 и 357 040 записей
Размер таблицы после обработки: Количество: 821,924; Размер: 217,7 МБ, Размер хранилища: 95,3 МБ, Индексы: 3, Общий размер индекса: 22,9 МБ
Примечание
Производительность кажется крайне нелинейной при удалении. Это выглядит очень похоже на то, что я должен как-то пакетировать удаления перед повторной индексацией, а я нет; могу ли я как-то «заблокировать» эту коллекцию для удаления и добавления (она не используется кроме этого процесса) или создать одну транзакцию, которая не позволяет индексу пересчитать промежуточный процесс, чтобы впоследствии он мог пересчитать его?
Запрошенная информация:
1) Наши серверы - это экземпляры EC2, работающие 18.04 на стороне Django / Python и 14.04 на стороне Mongo. Версия Mongo - 3.4.3, двигатель Wired Tiger.
2) Я проверил вывод объяснения (), и вывод JSON выглядит следующим образом:
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"date" : -1.0
},
"indexName" : "date_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"date" : [
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"date" : [
"[new Date(9223372036854775807), new Date(1522540800000)]"
]
}
}
Мой вывод таков: индекс работает.
3) Поле 'date' проиндексировано, как и поле 'id'
4) Текущее оборудование на стороне Mongo - сервер Mongo t2.small или t2.medium. Обновление экземпляра с t1 до t2, по-видимому, мало повлияло на скорость удаления, хотя серверы среднего уровня не зависают, и это хорошо.
5) Том для каждого диска является облачным экземпляром gp2 AWS. Это теоретический предел в 160 МБ / с, строки, которые я добавляю, составляют 7,5 ГБ, а размер индекса составляет 486,3 МБ.