Как стать автором
Обновить

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

Да, но их multi-document transacations пока не поддерживают Sharded Clusters, только Replica Sets. И (по слухам) заметно снижают производительность. Они появились совсем недавно, должно пройти время пока им начнут доверять. Слишком много у народа было проблем из-за их отсутствия:

news.ycombinator.com/item?id=18366385

Или вот чувак описывает, что MongoDB в силу специфического дизайна операции update (она неатомарна и выполняется как delete и insert) иногда не возвращает соответствующие запросу документы, хотя в базе они есть:

blog.meteor.com/mongodb-queries-dont-always-return-all-matching-documents-654b6594a827

А так — MongoDB не стоит на месте, развивается.
Все ваши ссылки относятся напрямую к deprecated MMAPv1 storage engine
Увы, нет. Автор поста по второй ссылке тоже так думал и перепроверил у знакомого разработчика MongoDB. А тот ответил, что с WiredTiger поведение не изменилось:

Reads may miss matching documents that are updated during the course of the read operation.
When I originally looked at the concurrency documentation, this sentence said it only applied to the older MMAPv1 storage engine. We started looking into upgrading our databases to use the newer WiredTiger engine, and pinged a friendly MongoDB engineer to double-check that we understood the issue and that upgrading would fix it. Sadly, his response was to fix the docs to say that this behavior is expected with WiredTiger as well. Ah well.
В WiredTiger операция Update не выполняется как Delete и Insert. MongoDB может перемещать документ, если вам не хватило его capacity.
В статье идет речь о том, что если во время выполнения длительного запроса документы уже отфильтрованные по индексу меняют свои значения, то они могут попасть в конечную выборку с измененными значениями.
Странно ожидать чего-то другого…
Вам стоит читать не только набросы PG-сообщества, которое очень любят критиковать монгу, везде заявляя, что она не нужна и для всего есть JSONB.
Не совсем так. В статье идет речь о том, что если во время выполнения длительного запроса документы уже отфильтрованные по индексу меняют свои значения, то они могут не попасть в конечную выборку, хотя формально все время удовлетворяли условиям отбора (и до, и после изменений).

А именно, допустим у вас есть коллекция документов с атрибутами (Name, Country, City) и индекс по полям (Country, City). Вы выполняете запрос people.find({country: «France»}), который использует индекс и выдает 1000 документов. Если вы в параллельной транзакции меняете одну запись таким образом, что она и до, и после изменений подпадает под условия поиска (поле Country равно ‘France’ и не изменяется), но ее позиция в индексе сдвигается (поле City изменяется):

(Anna, France, Paris) —> (Anna, France, Bordeaux)

то возможна ситуация (race condition), когда в результатах запроса вы эту запись не увидите. То есть вы получите 999 документов вместо 1000.

Обратите внимание, и старый, и новый документ удовлетворяли условиям поиска, но запрос не вернул ни один из них!

Это классический пример phantom read, и совсем неинтуитивное поведение. В других БД при высоком уровне изоляции (Serializable) или в системах MVCC такое поведение невозможно.

Вот человек пишет, что воспроизвел это на Wired Tiger 3.2:

medium.com/@alex_65399/ah-we-were-able-to-replicate-it-on-wired-tiger-3-2-5fb18cc5bc45

Таким образом, операции JOIN масштабируются принципиально плохо и это фундаментальная проблема реляционного подхода.

Так-то оно так, join не масштабируется горизонтально.
Но часто в предметной области нет необходимости связывать любой объект с любым объектом.
Часто есть условно Контрагент, и все его Сделки.
Соответственно храним одного Контрагента и его Сделки на одной ноде, другого Контрагента и его Сделки на другой. Успешно обслуживать Контрагентов это не мешает.
Глобального join не нужен, а локальный join успешно работает.
Хм, а если есть сделка, в которой участвуют контрагенты из обеих нод?
То записывается на нод1 как внешняя и так же на нод2 как внешняя. Как сделка с контрагентом, который не имеет логина.
Считайте, что у вас две независимые базы, разных организаций, просто с одним фронтендом.
Получается, что такая информация рассматривается как некая мета-информация, которая в идеале не будет меняться? Например, если в эту сделку будет включено допсоглашение, по которому добавится третий контрагент, который уже есть на второй ноде, но нет на первой, синхронизация может стать головной болью. Впрочем, если мы всегда пляшем от контрагента, то и информация об изменении будет поступать от каждого из них, и неконсистентное состояние одной сделки на разных шардах является своего рода даже фичей, насколько я понимаю?
Нет, вы делаете пометку на записи «внешняя, кластер ХХ», делаете пометку «изменен YYY». Дальше вы раз в Х минут пробегаете по индексу по полю «изменен» и переносите информацию в другие ДБ. Реально связанность будет малая и изменения тоже малые. Перенос выполняется в сторону «внешней» базы всегда, что упрощает процесс. Обычно такой перенос выполняется для данных типа «смена номера счета» и вообще некритичный по времени.
Если вам надо хранить информацию о контрагенте, которая записывается другим контрагентом, а не им самим, то вы создаете отдельную таблицу для таких полей и делаете джойн. Эта информация не требует синхронизации.
Если у вас есть контракт, вы его дублируете в обе базы, под разными номерами естественно.
Естественно, при внесении третьего контрагента вам надо проверить, есть ли он и внести его на вторую базу. Но, однако, то же происходит и в случае разных баз и программных комплексов, нет?
Вы же не обязаны внести всю информацию, вы просто переносите основные данные и отметку «контрагент из базы ZZZ». Дальше при запросе данных по контрагенту по необходимости читаете из RO реплики ZZZ
Да, это усложняет бакенд, необходимо понимать, что бывают external entity, доступ к которым медленее. Как обращение к другой ноде numa.
Вы какбы в каждой базе делаете «кеш» с именами контрагентов, что необходимо для частых операций типа сделать список и так далее, а по запросу детальной информации получается miss и идет запрос на внешний кластер.
Обслуживаться это может логикой бекенда либо решениями типа Percona Orchestrator(но у любом случае вы должны исключать данные внешних нод из изменения в транзакции, иначе не-ACID).
И да, если сделка неконсистентна, вы показываете ее пользователю с отметкой кто ее поменял, дальше пользователь должен решить, согласен он с изменениями или нет и договориться с контрагентом. Или вы вносите доп соглашения без согласия второй стороны? Тут есть два варианта. Либо вы ставите «владельца» сделки, либо храните две сделки в вариантах каждой из сторон и разрешаете им сделать сверку.
У меня не контрагенты, но, я например, выполняю сверку раз в сутки ночью и по записям в Aerospike(nosql, low latency) раз в 15 секунд. Aerospike может терять записи, обычно одну на 500тысяч запись counter теряет(телефония)
Для таких моментов есть разные варианты решений, каждый из них имеет как преимущества, так и недостатки. Всё нужно оценивать и выбирать исходя из полных требований.
Примеры решения:
  1. Введение reference сущности которая является immutable и реплицируется на все ноды. Т.к. в реальности immutablе редко достижим, то вводятся объекты с указанным временем жизни (active_from / active_to например). Соответственно join операции проводятся локально по данным одной ноды.
  2. Вводится дополнительная агрегирующая сущность в которую реплицируется полный необходимый набор данных с других нод. И далее логика работы идет над этим срезом данных. Здесь имеется большая избыточность, но и плюсы есть (например жесткая фиксация контракта, цен и прочее. Такой вариант версионирования когда мастер данные можно менять не боясь сломать активный текущий процесс.
  3. Организуется MapReduce подход или его аналог
  4. В зависимости от требований и технических особенностей хранилища данных возможны и другие варианты

Что то вы забыли вынос READ-ONLY на отдельный сет серверов в описании реляционных БД. ACID не обеспечивает, но для большинства приложений достаточная консистентность+все фишки реляционной ДБ.
Спасибо за замечание. В одном посте невозможно описать все возможные use cases.
Ну вроде как RO реплика это самый простой из вариантов для данных некритичных к transaction delay. В mariadb после parallel replication вообще шикарно работает. Всякие телекомы, статистика для клиентов(кроме последних минут) и так далее.
У нас все операции критичны к transaction delay. Среднее время ответа application server — 250мс. И мы планомерно снижаем его.
1) Apache Ignite есть возможность «родственные» данные хранить на одной ноде и делать локальный join
2) В последней версии реализовали препродуктовый вариант транзакций в SQL.
Транзакции в key/value хранилище существуют изначально.
3) Можно посмотреть RavenDB, как альтернатива Монге — обещан полный ACID.
Тут в коментах мелькает — обещан ACID.
Но CAP-теорема как бы намекает, что ACID достигается расплатой либо A, либо P.
А раз так, то зачем продукт который cAP, вдруг начинает работать как CaP, или CAp?
Неясно, о каком именно продукте вы говорите.

На практике большую часть времени сеть надежна (нет Partition), и тогда практически любая система согласована и доступна (Consistent + Available).

CAP-теорема говорит только о ситуации сетевых сбоев. В присутствии Partition любая БД, которая заявляет ACID-свойства, должна выбирать Consistency (иными словами быть СP, жертвуя A). Однако, в CAP-теореме A понимается слишком жестко — а именно «все ноды доступны 100% времени». В реальной жизни такое требование избыточно. Ноды могут быть недоступны какое-то минимальное время, если пользователи этого не успевают заметить. Это формализуется термином HA (High Availability) и обычно измеряется числом девяток, например «4 девятки» означает «все ноды доступны 99,99% времени».

Короче говоря, HA!=A и CAP-теорема не запрещает CP-систем, которые не A, но HA.

Вот, собственно, рассуждения по теме автора оригинальной CAP-гипотезы (доказали его как теорему другие):

static.googleusercontent.com/media/research.google.com/en//pubs/archive/45855.pdf
Однако, в CAP-теореме A понимается слишком жестко — а именно «все ноды доступны 100% времени»

«А» в CAP означает доступность любой из нод с соблюдением SLA, и ее способность успешно обработать запрос. Т.е. если какая то из нод дала ответ, то А выполнен.

Неясно, о каком именно продукте вы говорите.

В целом это не очень важно, но пусть будет монга.
Монга родилась как cAP. Тут «неожиданно» выясняется — оказывается acid ( C) в монге не совсем acid. В монге не совсем acid не потому что разработчики монги не знают что такое acid, или не могут его правильно реализовать, а потому что за acid ( C) нужно платить.
Монга родилась как cAP. Тут «неожиданно» выясняется — оказывается acid ( C) в монге не совсем acid. В монге не совсем acid не потому что разработчики монги не знают что такое acid, или не могут его правильно реализовать, а потому что за acid ( C) нужно платить.

Понятно, спасибо.

А раз так, то зачем продукт который cAP, вдруг начинает работать как CaP, или CAp?

Они раскрутили cAP-продукт, но рынок показал, что «С», оказывается, не менее важно. Предполагаю, что руководство Монги говорит разработчикам: «клиенты жалуются на потери данных, надо что-то делать». Разработчики пытаются сделать, чтобы данные терялись чуть реже, не переписывая БД с нуля. Платить за полноценный «С» (разработчики — временем, бизнес — деньгами) никто не хочет.
То что хочется свой продукт продать понятно.
Вызывает вопрос, зачем пользователи монги ожидают от монги (вместо монги подставить любое другое cAP решение) честного acid?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории