Обходной путь MongoDB для документа размером более 16 МБ?

#mongodb

#mongodb

Вопрос:

Коллекция MongoDB, над которой я работаю, принимает данные датчика с мобильного телефона, и они передаются на сервер примерно каждые 2-6 секунд.

Данные огромны, и ограничение в 16 МБ преодолевается через 4-5 часов, похоже, для этого нет никаких обходных путей?

Я попытался найти его в Stack Overflow и ответил на различные вопросы, но никто на самом деле не поделился своим взломом.

Есть ли какой-нибудь способ … возможно, на стороне базы данных, который будет распределять фрагмент, как это делается для больших файлов через GridFS?

Комментарии:

1. Документы с неограниченным ростом являются антишаблоном; вероятно, вам следует пересмотреть свою модель данных, чтобы лучше поддерживать ваш вариант использования. Подход GridFS подходит только в том случае, если вы храните большие двоичные двоичные объекты; это бесполезно для данных с полями, которые вы планируете запрашивать (если запросы не ограничены метаданными о двоичном файле в GridFS). Для получения рекомендаций по схеме вам нужно опубликовать пример документа и описать ваши общие обновления и запросы. Ваша версия сервера MongoDB и настроенный механизм хранения также будут иметь значение.

Ответ №1:

Чтобы устранить эту проблему, вам нужно будет внести некоторые небольшие изменения в вашу структуру данных. Судя по всему, чтобы ваши документы превышали лимит в 16 МБ, вы должны встраивать данные вашего датчика в массив в одном документе.

Я бы не советовал использовать GridFS здесь, я не считаю, что это лучшее решение, и вот почему.

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

Способ, которым это работает, заключается в следующем:

Допустим, у меня есть документ с некоторыми встроенными показаниями для определенного датчика, который выглядит следующим образом:

 {
    _id : ObjectId("xxx"),
    sensor : "SensorName1",
    readings : [
        { date : ISODate("..."), reading : "xxx" },
        { date : ISODate("..."), reading : "xxx" },
        { date : ISODate("..."), reading : "xxx" }
    ]
}
  

В приведенной выше структуре уже есть серьезный недостаток: массив readings может расти экспоненциально и превышать ограничение для документа размером 16 МБ.

Итак, что мы можем сделать, это немного изменить структуру, чтобы она выглядела примерно так, чтобы включить свойство count:

 {
    _id : ObjectId("xxx"),
    sensor : "SensorName1",
    readings : [
        { date : ISODate("..."), reading : "xxx" },
        { date : ISODate("..."), reading : "xxx" },
        { date : ISODate("..."), reading : "xxx" }
    ],
    count : 3
}
  

Идея, лежащая в основе этого, заключается в том, что когда вы $ помещаете свое чтение во встроенный массив, вы увеличиваете ($ inc) переменную count для каждого выполняемого нажатия. И когда вы выполняете эту операцию обновления (push), вы должны включить фильтр для этого свойства «count», которое может выглядеть примерно так:

 { count : { $lt : 500} }
  

Затем настройте параметры обновления таким образом, чтобы вы могли установить для «upsert» значение «true»:

 db.sensorReadings.update(
    { name: "SensorName1", count { $lt : 500} },
    {
        //Your update. $push your reading and $inc your count
        $push: { readings: [ReadingDocumentToPush] }, 
        $inc: { count: 1 }
    },
    { upsert: true }
)
  

смотрите здесь для получения дополнительной информации об обновлении MongoDB и опции Upsert:

Документация по обновлению MongoDB

Что произойдет, так это то, что когда условие фильтрации не выполняется (т. Е. Когда либо нет существующего документа для этого датчика, либо количество больше или равно 500 — потому что вы увеличиваете его каждый раз при нажатии элемента), будет создан новый документ, и показания теперь будут встроены в этот новый документ. Таким образом, вы никогда не превысите лимит в 16 МБ, если сделаете это правильно.

Теперь, запрашивая у базы данных показания определенного датчика, вы можете получить обратно несколько документов для этого датчика (вместо одного со всеми показаниями в нем), например, если у вас есть 10 000 показаний, вы получите обратно 20 документов, в каждом из которых по 500 показаний.

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

Дополнительную информацию о unwind смотрите здесь, это очень полезно

MongoDB разматывается

Я надеюсь, что это поможет.

Комментарии:

1. Это лучший способ для решения этой проблемы. Чтобы прочитать больше о bucketing, который касается именно этого варианта использования, вы можете посетить здесь : mongodb.com/blog/post/building-with-patterns-the-bucket-pattern

2. Спасибо! На самом деле это блестящее решение, которое я искал.

3. @pieperu Можете ли вы также привести пример извлечения данных путем агрегирования или другими методами? Применяется ли ограничение в 16 МБ также для результата агрегирования?

4. Потребует ли эта стратегия указания индекса в поле count? (или составное имя индекса количество?)

5. ДА. Каждый раз, когда поле считывается, оно должно покрываться индексом. В этом случае он считывается как часть условия обновления.

Ответ №2:

Вы можете справиться с такого рода ситуациями, используя GridFS в MongoDB.

Вместо того, чтобы хранить файл в одном документе, GridFS делит файл на части, или блоки 1, и сохраняет каждый фрагмент как отдельный документ. По умолчанию GridFS использует фрагмент размером 255 Кб; то есть GridFS делит файл на фрагменты по 255 Кб, за исключением последнего фрагмента. Последний фрагмент имеет необходимый размер. Аналогично, файлы, размер которых не превышает размер фрагмента, имеют только конечный фрагмент, используя ровно столько места, сколько необходимо, плюс некоторые дополнительные метаданные.

Документация по GriFS содержит почти все, что вам нужно для реализации GridFS. Вы можете следовать ему.

Поскольку ваши данные являются потоковыми, вы можете попробовать следующее…

 gs.write(data, callback)
  

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

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

Комментарии:

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

2. ваши данные поступают в виде потока?

3. Да, через сокеты.