← Назад к вопросам
Какие типы транзакций реализованы в MongoDB?
1.7 Middle🔥 81 комментариев
#Базы данных (NoSQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Типы транзакций в MongoDB
MongoDB эволюционировала в поддержке транзакций, начиная с версии 4.0. Сейчас она предоставляет полноценную поддержку ACID транзакций на разных уровнях.
1. Одиночные документы (Single Document Transactions)
Имплицитные транзакции - все операции над одним документом атомарны с 4.0:
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
db = client['mydb']
users = db['users']
# Одиночный документ - всегда атомарна операция
result = users.update_one(
{'_id': ObjectId('...')},
{'$set': {'status': 'active', 'updated_at': datetime.now()}}
)
# Гарантированно атомарна - либо обновились оба поля, либо ничего
Когда полезно:
- Изменение одного документа
- Нет необходимости в координации нескольких документов
- Высокая производительность
2. Multi-Document Transactions (Версия 4.0+)
Транзакции, охватывающие несколько документов в одной коллекции:
from pymongo import MongoClient
from pymongo.errors import OperationFailure
client = MongoClient('mongodb://localhost:27017/')
session = client.start_session()
try:
session.start_transaction()
# Вся логика в одной транзакции
accounts = db['accounts']
# Шаг 1: Снять деньги с одного аккаунта
accounts.update_one(
{'account_id': 'ACC001'},
{'$inc': {'balance': -100}},
session=session
)
# Шаг 2: Зачислить на другой аккаунт
accounts.update_one(
{'account_id': 'ACC002'},
{'$inc': {'balance': 100}},
session=session
)
# Шаг 3: Записать логу
logs = db['transaction_logs']
logs.insert_one(
{'from': 'ACC001', 'to': 'ACC002', 'amount': 100},
session=session
)
# Commit все вместе
session.commit_transaction()
print('Transfer successful')
except OperationFailure as e:
session.abort_transaction()
print(f'Transaction failed: {e}')
finally:
session.end_session()
Гарантии ACID:
- A (Atomicity): Либо все операции выполнятся, либо ничего
- C (Consistency): БД переходит из одного консистентного состояния в другое
- I (Isolation): На уровне Snapshot Isolation
- D (Durability): После commit - данные сохранены на диск
3. Cross-Document Transactions
Транзакции, охватывающие документы в разных коллекциях (версия 4.0+):
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
session = client.start_session()
try:
session.start_transaction()
db = client['ecommerce']
orders = db['orders']
products = db['products']
inventory = db['inventory']
# Оформить заказ (несколько коллекций)
order_id = ObjectId()
# 1. Создать заказ
orders.insert_one(
{
'_id': order_id,
'user_id': 'USER123',
'items': [{'product_id': 'PROD1', 'qty': 2}],
'status': 'pending'
},
session=session
)
# 2. Обновить инвентарь
inventory.update_one(
{'product_id': 'PROD1'},
{'$inc': {'stock': -2}},
session=session
)
# 3. Обновить статистику продукта
products.update_one(
{'_id': 'PROD1'},
{'$inc': {'sold_count': 2}},
session=session
)
session.commit_transaction()
print(f'Order {order_id} created successfully')
except Exception as e:
session.abort_transaction()
print(f'Failed to create order: {e}')
finally:
session.end_session()
4. Sharded Transactions (Версия 4.2+)
Транзакции на шардированных кластерах:
# MongoDB автоматически управляет распределенными транзакциями
# когда используется sharded cluster
client = MongoClient('mongodb://localhost:27017/')
session = client.start_session()
try:
session.start_transaction(readPreference='primary')
# Операции могут затрагивать разные шарды
db = client['mydb']
users = db['users']
# Автоматически координируется между шардами
users.update_many(
{'status': 'inactive'},
{'$set': {'status': 'archived'}},
session=session
)
session.commit_transaction()
except Exception as e:
session.abort_transaction()
raise
finally:
session.end_session()
5. Транзакции с Causal Consistency
Учет причинно-следственных связей между операциями:
from pymongo import MongoClient, ReadPreference, ReadConcern
client = MongoClient('mongodb://localhost:27017/')
# Включить causal consistency для сессии
session = client.start_session(causal_consistency=True)
try:
# Запись
db = client['mydb']
db['data'].insert_one({'key': 'value'}, session=session)
# При чтении гарантируется, что видим эту запись
# даже при репликации
result = db['data'].find_one({'key': 'value'}, session=session)
finally:
session.end_session()
6. Read Transactions
Транзакции только для чтения:
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
session = client.start_session()
try:
session.start_transaction()
db = client['reporting']
# Читаем консистентный снимок данных
total_users = db['users'].count_documents({}, session=session)
total_orders = db['orders'].count_documents({}, session=session)
total_revenue = sum(
doc['total'] for doc in
db['orders'].find({}, session=session)
)
# Гарантировано видим консистентный снимок
report = {
'users': total_users,
'orders': total_orders,
'revenue': total_revenue
}
session.commit_transaction()
finally:
session.end_session()
7. Write Transactions
Транзакции с записью:
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
session = client.start_session()
try:
session.start_transaction()
db = client['mydb']
# Атомарное выполнение всех операций
db['users'].insert_one(
{'_id': 'user1', 'name': 'Alice'},
session=session
)
db['profiles'].insert_one(
{'user_id': 'user1', 'bio': 'Developer'},
session=session
)
db['audit_logs'].insert_one(
{'action': 'user_created', 'user_id': 'user1'},
session=session
)
session.commit_transaction()
except Exception as e:
session.abort_transaction()
raise
finally:
session.end_session()
8. Snapshot Isolation
Уровень изоляции, используемый MongoDB:
# MongoDB использует Snapshot Isolation
# Это означает:
# - Читаем снимок, который был до начала транзакции
# - Не видим изменения других транзакций
# - Невозможна dirty read
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
session1 = client.start_session()
session2 = client.start_session()
db = client['mydb']
accounts = db['accounts']
try:
# Transaction 1: Передача денег
session1.start_transaction()
accounts.update_one(
{'_id': 'acc1'},
{'$inc': {'balance': -100}},
session=session1
)
# Еще не коммитим
# Transaction 2: Видим старое значение balance
session2.start_transaction()
balance = accounts.find_one({'_id': 'acc1'}, session=session2)['balance']
# balance имеет старое значение (до уменьшения на 100)
# Transaction 1: Коммит
session1.commit_transaction()
# Transaction 2: Все еще видит старое значение (Snapshot Isolation)
balance = accounts.find_one({'_id': 'acc1'}, session=session2)['balance']
session2.commit_transaction()
finally:
session1.end_session()
session2.end_session()
9. Параметры транзакции
from pymongo import MongoClient, ReadPreference
from pymongo.read_concern import ReadConcern
from pymongo.write_concern import WriteConcern
client = MongoClient('mongodb://localhost:27017/')
# Кастомная транзакция с параметрами
session = client.start_session(
causal_consistency=True,
default_transaction_options={
'read_preference': ReadPreference.PRIMARY,
'read_concern': ReadConcern('majority'),
'write_concern': WriteConcern(j=True) # journal=True
}
)
try:
session.start_transaction(
read_preference=ReadPreference.PRIMARY,
write_concern=WriteConcern(w='majority', j=True),
read_concern=ReadConcern('majority')
)
# Операции с гарантиями
db = client['mydb']
db['data'].insert_one({'key': 'value'}, session=session)
session.commit_transaction()
finally:
session.end_session()
10. Пример реального use case
from pymongo import MongoClient
from pymongo.errors import OperationFailure
import logging
logger = logging.getLogger(__name__)
def transfer_funds(from_user_id: str, to_user_id: str, amount: float) -> bool:
"""Безопасная передача денег между пользователями."""
client = MongoClient('mongodb://localhost:27017/')
session = client.start_session()
try:
session.start_transaction()
db = client['bank']
users = db['users']
transactions = db['transaction_history']
# Проверить баланс
sender = users.find_one(
{'_id': from_user_id},
session=session
)
if not sender or sender['balance'] < amount:
raise ValueError('Insufficient funds')
# Выполнить транзакцию
users.update_one(
{'_id': from_user_id},
{'$inc': {'balance': -amount}},
session=session
)
users.update_one(
{'_id': to_user_id},
{'$inc': {'balance': amount}},
session=session
)
# Логировать
transactions.insert_one(
{
'from': from_user_id,
'to': to_user_id,
'amount': amount,
'status': 'completed'
},
session=session
)
session.commit_transaction()
logger.info(f'Transfer {amount} from {from_user_id} to {to_user_id}')
return True
except OperationFailure as e:
session.abort_transaction()
logger.error(f'Transaction failed: {e}')
return False
finally:
session.end_session()
client.close()
# Использование
success = transfer_funds('user1', 'user2', 100)
if success:
print('Transfer completed')
else:
print('Transfer failed')
Ограничения транзакций MongoDB
# 1. Таймауты
# По умолчанию 30 секунд на выполнение
transactionLifetimeLimitSeconds = 30
# 2. Размер операций
# Максимум 16MB на транзакцию (как один документ)
# 3. Тип хранилища
# Требует WiredTiger, не работает с MMAPv1
# 4. Шардирование
# На версиях < 4.2 только на отдельных коллекциях
# На версиях >= 4.2 поддерживаются cross-shard транзакции
MongoDB обеспечивает надежные ACID транзакции на уровне, который раньше был доступен только в традиционных реляционных БД.