Я знаю, что здесь есть похожие вопросы, но они говорят мне, чтобы переключиться на обычные системы РСУБД, если мне нужны транзакции или используйте атомные операции или двухфазное принятие. Второй вариант кажется лучшим выбором. Третий я не хочу следовать, потому что кажется, что многие вещи могут пойти не так, и я не могу проверить это во всех аспектах. Мне сложно реорганизовать мой проект для выполнения атомных операций. Я не знаю, связано ли это с моей ограниченной точки зрения (я только работал с базами данных SQL до сих пор), или это действительно невозможно сделать.
Мы хотели бы пилотировать тест MongoDB в нашей компании. Мы выбрали относительно простой проект - шлюз SMS. Это позволяет нашему программному обеспечению отправлять SMS-сообщения в сотовую сеть, а шлюз выполняет грязную работу: фактически общается с поставщиками через различные протоколы связи. Шлюз также управляет выставлением счетов. Каждый клиент, который подает заявку на услугу, должен купить несколько кредитов. Система автоматически уменьшает баланс пользователя при отправке сообщения и отказывает в доступе, если баланс недостаточен. Кроме того, поскольку мы являемся клиентами сторонних провайдеров SMS, у нас также могут быть свои собственные балансы. Мы также должны отслеживать их.
Я начал думать о том, как я могу хранить требуемые данные с помощью MongoDB, если я сократил некоторую сложность (внешняя биллинг, отправка посылок в очереди). Исходя из мира SQL, я бы создал отдельную таблицу для пользователей, другую для SMS-сообщений и одну для хранения транзакций относительно баланса пользователей. Скажем, я создаю отдельные коллекции для всех из них в MongoDB.
Представьте задачу отправки SMS со следующими шагами в этой упрощенной системе:
проверьте, имеет ли пользователь достаточный баланс; запретить доступ, если недостаточно кредитов
отправьте и сохраните сообщение в сборнике SMS с подробной информацией и стоимостью (в живой системе сообщение будет иметь атрибут status
, и задача подберет его для доставки и установит цену SMS в соответствии с его текущим состоянием)
уменьшить баланс пользователей за счет стоимости отправленного сообщения
зарегистрировать транзакцию в коллекции транзакций
Теперь, что проблема с этим? MongoDB может выполнять атомарные обновления только на одном документе. В предыдущем потоке может случиться, что в базу данных заходит некоторая ошибка, и сообщение сохраняется в базе данных, но баланс пользователя не обновляется и/или транзакция не регистрируется.
Я придумал две идеи:
Создайте единую коллекцию для пользователей и сохраните баланс как поле, связанные с пользователем транзакции и сообщения в качестве поддокументов в пользовательском документе. Поскольку мы можем обновлять документы атомарно, это фактически решает проблему транзакции. Недостатки: если пользователь отправляет много SMS-сообщений, размер документа может стать большим и может быть достигнут предел документа 4 МБ. Возможно, я могу создавать документы истории в таких сценариях, но я не думаю, что это была бы хорошая идея. Кроме того, я не знаю, насколько бы быстрой была система, если я нажимаю все больше и больше данных на один и тот же большой документ.
Создайте одну коллекцию для пользователей и одну для транзакций. Могут быть два вида сделок: покупка кредита с положительным изменением баланса и сообщения, отправленные с отрицательным сальдо. Транзакция может иметь поддокумент; например, в отправляемых сообщениях детали SMS могут быть встроены в транзакцию. Недостатки: я не сохраняю текущий баланс пользователя, поэтому я должен рассчитать его каждый раз, когда пользователь пытается отправить сообщение, чтобы сообщить, может ли сообщение пройти или нет. Я боюсь, что этот расчет может стать медленным, поскольку количество хранимых транзакций растет.
Я немного смущен тем, какой метод выбрать. Существуют ли другие решения? Я не мог найти ни одного передового опыта в Интернете о том, как обойти эти проблемы. Я думаю, многие программисты, которые пытаются познакомиться с миром NoSQL, сталкиваются с аналогичными проблемами в начале.
Отметьте этот из Tokutek. Они разрабатывают плагин для Mongo, что promises не только транзакции, но и повышение производительности.
Поддерживает транзакции ACID, но, хотя транзакций в MongoDB
нет, у нас есть атомарные операции. Ну, атомные операции означают, что когда вы работаете над одним документом, эта работа будет завершена до того, как кто-либо еще увидит документ. Они увидят все сделанные нами изменения или ни одно из них. И используя атомные операции, вы часто можете выполнить ту же самую вещь, которую мы выполнили бы с помощью транзакций в реляционной базе данных. И причина в том, что в реляционной базе данных нам нужно внести изменения в несколько таблиц. Обычно таблицы должны быть объединены, и поэтому мы хотим сделать это все сразу. И для этого, поскольку существует несколько таблиц, нам нужно будет начать транзакцию и выполнить все эти обновления, а затем завершить транзакцию. Но с MongoDB
мы собираемся внедрить данные, так как мы собираемся предварительно присоединить к ним в документах, и это эти богатые документы, которые имеют иерархию. Мы можем часто выполнять одно и то же. Например, в примере блога, если мы хотим убедиться, что мы обновили сообщение в блоге атомарно, мы можем это сделать, потому что мы можем сразу обновить весь блог. Если бы это была куча реляционных таблиц, нам, вероятно, пришлось бы открыть транзакцию, чтобы мы могли обновлять коллекцию записей и комментариев.
Итак, каковы наши подходы, которые мы можем предпринять в MongoDB
, чтобы преодолеть недостаток транзакций?
Update
, findAndModify
, $addToSet
(в рамках обновления) и $push
(в рамках обновления) операции работают атомарно внутри одного документа.
Довести это до такой степени: если целостность транзакций является обязательной, тогда не используйте MongoDB, а используйте только компоненты в системе, поддерживающей транзакции. Чрезвычайно сложно создать что-то поверх компонента, чтобы обеспечить ACID-подобную функциональность для компонентов, не совместимых с ACID. В зависимости от индивидуальных условий может иметь смысл отделить действия к транзакционным и не транзакционным действиям каким-либо образом...
Теперь, что проблема с этим? MongoDB может выполнять атомарные обновления только на одном документе. В предыдущем потоке может случиться, что в базе данных происходит некоторая ошибка, и сообщение сохраняется в базе данных, но баланс пользователя не уменьшается и/или транзакция не регистрируется.
Это не проблема. Вы упомянули ошибку либо логическую ошибку (ошибка), либо ошибку ввода-вывода (сеть, сбой диска). Такая ошибка может оставлять как транзакционные, так и транзакционные хранилища в несогласованном состоянии. Например, если он уже отправил SMS, но при сохранении ошибки сообщения произошел - он не может отменить отправку SMS, что означает, что он не будет зарегистрирован, баланс пользователя не будет уменьшен и т.д.
Настоящая проблема заключается в том, что пользователь может использовать условия гонки и отправлять больше сообщений, чем позволяет его баланс. Это также относится к РСУБД, если только вы не отправляете SMS внутри транзакции с блокировкой поля баланса (что было бы большим препятствием). В качестве возможного решения для MongoDB сначала следует использовать findAndModify
, чтобы уменьшить баланс и проверить его, если он отрицательно отклоняет отправку и возвращает сумму (атомный приращение). Если положительный, продолжите отправку и в случае, если он не сможет вернуть сумму. Также можно сохранить коллекцию истории баланса, чтобы помочь исправить/проверить поле баланса.
Транзакции отсутствуют в MongoDB по уважительным причинам. Это одна из тех вещей, которые делают MongoDB быстрее.
В вашем случае, если транзакция является обязательной, mongo кажется не очень подходящей.
Может быть RDMBS + MongoDB, но это добавит сложности и затруднит управление и поддержку приложения.
Проект прост, но вы должны поддерживать транзакции для оплаты, что затрудняет выполнение всего этого. Так, например, сложная система портала с сотнями коллекций (форум, чат, реклама и т.д.) В некотором отношении проще, потому что, если вы теряете форум или запись в чате, никто действительно не заботится. Если вы, с другой стороны, потеряете транзакцию платежа, которая является серьезной проблемой.
Итак, если вам действительно нужен пилотный проект для MongoDB, выберите тот, который прост в этом отношении.