Авто сжимать удаленное пространство в mongodb?

В документе Mongodb говорится, что

Чтобы сжать это пространство, запустите db.repairDatabase() из оболочки mongo (обратите внимание, что эта операция будет блокироваться и будет медленной).

в http://www.mongodb.org/display/DOCS/Excessive+Disk+Space

Интересно, как сделать mongodb свободным удаленным дисковым пространством автоматически?

ps Мы сохранили много задач загрузки в mongodb, до 20 ГБ, и закончили их за полчаса.

4 ответа

Решение

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

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

Однако, если вам нужно уменьшить базу данных, вы должны помнить о двух вещах.

  1. MongoDB увеличивает свои файлы данных путем удвоения, поэтому файлы данных могут иметь размер 64 МБ, затем 128 МБ и т. Д. До 2 ГБ (в этот момент он прекращает дублирование, чтобы сохранить файлы до 2 ГБ.)

  2. Как и в большинстве других баз данных... для выполнения таких операций, как сжатие, для этого необходимо запланировать отдельную работу, в MongoDB нет "автоусадки". На самом деле из основных баз данных noSQL (ненавижу это имя) только Riak будет автоматически сжиматься. Итак, вам нужно будет создать задание, используя планировщик вашей ОС для запуска сжатия. Вы можете использовать bash-скрипт или запустить php-скрипт и т. Д.

Серверный Javascript

Вы можете использовать Javascript на стороне сервера, чтобы сжимать и запускать этот JS через оболочку mongo на регулярной основе через задание (например, cron или службу планирования Windows) ...

Предполагая, что коллекция называется foo, вы должны сохранить приведенный ниже javascript в файле bar.js и запустить...

$ mongo foo bar.js

Файл javascript будет выглядеть примерно так...

// Get a the current collection size.
var storage = db.foo.storageSize();
var total = db.foo.totalSize();

print('Storage Size: ' + tojson(storage));

print('TotalSize: ' + tojson(total));

print('-----------------------');
print('Running db.repairDatabase()');
print('-----------------------');

// Run repair
db.repairDatabase()

// Get new collection sizes.
var storage_a = db.foo.storageSize();
var total_a = db.foo.totalSize();

print('Storage Size: ' + tojson(storage_a));
print('TotalSize: ' + tojson(total_a));

Это запустится и вернет что-то вроде...

MongoDB shell version: 1.6.4
connecting to: foo
Storage Size: 51351
TotalSize: 79152
-----------------------
Running db.repairDatabase()
-----------------------
Storage Size: 40960
TotalSize: 65153

Запустите это по расписанию (не в часы пик), и все готово.

Закрытые Коллекции

Однако есть еще один вариант - закрытые коллекции.

Закрытые коллекции - это коллекции фиксированного размера, которые имеют очень высокую производительность для автоматического истечения срока действия FIFO (срок действия зависит от порядка вставки) . Они немного похожи на концепцию "RRD", если вы знакомы с этим.

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

По сути, вы можете ограничить размер (или количество документов) в коллекции, скажем.. 20 ГБ, и как только этот предел будет достигнут, MongoDB начнет выбрасывать самые старые записи и заменять их новыми записями по мере их поступления.

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

У меня есть другое решение, которое может работать лучше, чем db.repairDatabase(), если вы не можете позволить себе заблокировать систему или не имеете двойного хранилища.

Вы должны использовать набор реплик.

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

Процесс занимает много времени, но он должен стоить всего несколько секунд простоя, когда вы выполняете rs.stepDown().

Также это не может быть автоматизировано. Ну, может, но я не думаю, что хочу попробовать.

Для запуска db.repairDatabase() вам потребуется пространство, равное текущему размеру базы данных, доступной в файловой системе. Это может быть неприятно, когда вы знаете, что оставленные коллекции или данные, которые необходимо сохранить в базе данных, в настоящее время занимают гораздо меньше места, чем выделено, и у вас недостаточно места для восстановления.

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

use cleanup_database
db.dropDatabase();

use oversize_database

db.collection.find({},{}).forEach(function(doc){
    db = db.getSiblingDB("cleanup_database");
    db.collection_subset.insert(doc);
});

use oversize_database
db.dropDatabase();

use cleanup_database

db.collection_subset.find({},{}).forEach(function(doc){
    db = db.getSiblingDB("oversize_database");
    db.collection.insert(doc);
});

use oversize_database

<add indexes>
db.collection.ensureIndex({field:1});

use cleanup_database
db.dropDatabase();

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

Также в качестве политики вы можете хранить постоянные коллекции в отдельной базе данных от ваших данных переходных процессов / обработки и просто удалять базу данных обработки после завершения ваших заданий. Поскольку MongoDB не содержит схем, ничего, кроме индексов, не будет потеряно, и ваша БД и коллекции будут воссозданы, когда вставки для процессов будут запущены в следующий раз. Просто убедитесь, что ваши задания включают создание любых необходимых индексов в соответствующее время.

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

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

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

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

ПРИМЕЧАНИЕ: ЭТО ОСНОВНОЙ PSEUDO-КОД - ТОЛЬКО ДЛЯ ИЛЛЮСТРАТИВНЫХ ЦЕЛЕЙ - НЕ ИСПОЛЬЗУЙТЕ ДЛЯ ПРОИЗВОДСТВЕННЫХ СИСТЕМ БЕЗ ЗНАЧИТЕЛЬНЫХ ИЗМЕНЕНИЙ

#!/bin/bash 

# First arg is host MongoDB is running on, second arg is the MongoDB port

MONGO=/path/to/mongo
MONGOHOST=$1
MONGOPORT=$2
DBPATH = /path/to/dbpath

# make sure the node we are connecting to is not the primary
while (`$MONGO --quiet --host $MONGOHOST --port $MONGOPORT --eval 'db.isMaster().ismaster'`)
do
    `$MONGO --quiet --host $MONGOHOST --port $MONGOPORT --eval 'rs.stepDown()'`
    sleep 2
done    
echo "Node is no longer primary!\n"

# Now shut down that server 
# something like (assuming user is set up for key based auth and has password-less sudo access a la ec2-user in EC2)
ssh -t user@$MONGOHOST sudo service mongodb stop

# Wipe the data files for that server

ssh -t user@$MONGOHOST sudo rm -rf $DBPATH
ssh -t user@$MONGOHOST sudo mkdir $DBPATH
ssh -t user@$MONGOHOST sudo chown mongodb:mongodb $DBPATH

# Start up server again
# similar to shutdown something like 
ssh -t user@$MONGOHOST sudo service mongodb start 
Другие вопросы по тегам