← Назад к вопросам

Какие типы транзакций реализованы в 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 транзакции на уровне, который раньше был доступен только в традиционных реляционных БД.

Какие типы транзакций реализованы в MongoDB? | PrepBro