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

Можно ли выполнить запрос без транзакции?

1.0 Junior🔥 171 комментариев
#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Выполнение запросов без явной транзакции

Да, можно выполнить запрос без явной транзакции, но важно понимать, что даже в этом случае транзакция существует неявно. Каждый запрос к базе данных выполняется внутри транзакции по умолчанию.

1. Неявная транзакция (autocommit)

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

import psycopg2

# Подключение с autocommit=True (неявная транзакция)
conn = psycopg2.connect(
    dbname='mydb',
    user='postgres',
    password='password',
    host='localhost'
)
conn.autocommit = True  # ВАЖНО!

cursor = conn.cursor()

# Каждый запрос коммитится автоматически
cursor.execute("INSERT INTO users (name, email) VALUES (%s, %s)", ('Alice', 'alice@example.com'))
# Данные сразу видны в БД, даже без явного commit()

cursor.execute("SELECT * FROM users")
print(cursor.fetchall())

cursor.close()
conn.close()

2. Явная транзакция (manual transaction)

Если autocommit=False (по умолчанию), нужна явная BEGIN и COMMIT:

import psycopg2

# Подключение с явными транзакциями (autocommit=False по умолчанию)
conn = psycopg2.connect('dbname=mydb user=postgres')
cursor = conn.cursor()

# Начало транзакции (неявно через BEGIN)
cursor.execute("INSERT INTO users (name, email) VALUES (%s, %s)", ('Bob', 'bob@example.com'))
# Данные ещё НЕ видны другим подключениям!

# Коммит
conn.commit()  # Теперь видны для всех

# Откат
try:
    cursor.execute("UPDATE users SET name = %s WHERE id = %s", ('Charlie', 1))
    # Что-то пошло не так...
    raise Exception("Ошибка!")
except Exception as e:
    conn.rollback()  # Откат всех изменений
    print(f"Откатили транзакцию: {e}")

cursor.close()
conn.close()

3. SQLAlchemy: явное управление транзакциями

SQLAlchemy предоставляет удобный API для работы с транзакциями:

from sqlalchemy import create_engine, text
from sqlalchemy.orm import Session

engine = create_engine('postgresql://user:password@localhost/mydb')

# Без явной транзакции — используется autocommit
with engine.connect() as conn:
    result = conn.execute(text("SELECT * FROM users"))
    print(result.fetchall())
    # После выхода из контекста — автоматический commit

# С явной транзакцией
with engine.begin() as conn:
    conn.execute(
        text("INSERT INTO users (name, email) VALUES (:name, :email)"),
        {"name": "Alice", "email": "alice@example.com"}
    )
    # Коммит автоматически при выходе

# Низкоуровневое управление
conn = engine.raw_connection()
try:
    cursor = conn.cursor()
    cursor.execute("INSERT INTO users (name, email) VALUES (%s, %s)", ('Bob', 'bob@example.com'))
    conn.commit()
except Exception:
    conn.rollback()
finally:
    cursor.close()
    conn.close()

4. SQLAlchemy ORM с сессиями

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)

engine = create_engine('postgresql://user:password@localhost/mydb')
SessionLocal = sessionmaker(bind=engine)

# Без явной транзакции (единственный запрос)
session = SessionLocal()
user = session.query(User).filter(User.id == 1).first()
print(user.name)
session.close()  # Autocommit

# С явной транзакцией (несколько операций)
session = SessionLocal()
try:
    # Начало транзакции
    new_user = User(name='Charlie', email='charlie@example.com')
    session.add(new_user)
    
    # Ещё операции
    existing_user = session.query(User).filter(User.id == 2).first()
    existing_user.name = 'Updated'
    
    # Коммит
    session.commit()
except Exception as e:
    session.rollback()  # Откат всех операций
    print(f"Ошибка: {e}")
finally:
    session.close()

5. Asyncio и асинхронные запросы

import asyncpg
import asyncio

async def async_query():
    # Подключение
    conn = await asyncpg.connect(
        user='postgres',
        password='password',
        database='mydb',
        host='127.0.0.1',
    )
    
    # Без явной транзакции (autocommit)
    result = await conn.fetch('SELECT * FROM users')
    print(result)
    
    # С явной транзакцией
    async with conn.transaction():
        await conn.execute(
            'INSERT INTO users (name, email) VALUES ($1, $2)',
            'Alice', 'alice@example.com'
        )
        # При выходе из контекста — автоматический commit
        # Если исключение — автоматический rollback
    
    await conn.close()

asyncio.run(async_query())

6. Levels of Isolation (уровни изоляции транзакций)

Если работаешь без явной транзакции, важно понимать уровень изоляции:

import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE

conn = psycopg2.connect('dbname=mydb user=postgres')

# Установка уровня изоляции
conn.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)

# Теперь все запросы выполняются в транзакциях с более строгой изоляцией
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = 1")
result = cursor.fetchone()

# Сразу коммитим
conn.commit()

Уровни изоляции (от слабого к строгому):

УровеньГрязное чтениеНеповторяемое чтениеФантомное чтениеSpeed
READ UNCOMMITTEDДаДаДаБыстро
READ COMMITTEDНетДаДа✅ Баланс
REPEATABLE READНетНетДаМедленнее
SERIALIZABLEНетНетНетМедленно

7. Практические примеры

Запрос БЕЗ явной транзакции (SELECT)

# Это безопасно — просто читаем данные
conn = psycopg2.connect('dbname=mydb')
conn.autocommit = True
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
print(cursor.fetchall())
cursor.close()
conn.close()

Запросы МЕНЯЮЩИЕ ДАННЫЕ БЕЗ транзакции (опасно!)

# ❌ ПЛОХО: Каждый запрос коммитится отдельно
conn = psycopg2.connect('dbname=mydb')
conn.autocommit = True
cursor = conn.cursor()

cursor.execute("INSERT INTO accounts (user_id, balance) VALUES (%s, %s)", (1, 1000))
# Коммит!
cursor.execute("INSERT INTO audit_log (action) VALUES (%s)", ('Account created',))
# Коммит!
# Если второй запрос падает — первый уже в БД!

✅ ХОРОШО: Используй явную транзакцию для атомарности

conn = psycopg2.connect('dbname=mydb')
conn.autocommit = False  # Явная транзакция
cursor = conn.cursor()

try:
    cursor.execute("INSERT INTO accounts (user_id, balance) VALUES (%s, %s)", (1, 1000))
    cursor.execute("INSERT INTO audit_log (action) VALUES (%s)", ('Account created',))
    conn.commit()  # Обе операции — атомарно
except Exception as e:
    conn.rollback()  # Откатываем обе
    print(f"Ошибка: {e}")
finally:
    cursor.close()
    conn.close()

8. Правила ACID

# Транзакция без явного управления может нарушить ACID

# ❌ Без транзакции (ACID нарушена)
for user_id, amount in transfers:
    cursor.execute("UPDATE accounts SET balance = balance - %s WHERE id = %s",
                   (amount, user_id[0]))
    # Если сервер упадёт здесь — деньги пропадают!
    cursor.execute("UPDATE accounts SET balance = balance + %s WHERE id = %s",
                   (amount, user_id[1]))

# ✅ С явной транзакцией (ACID гарантирована)
try:
    for user_id, amount in transfers:
        cursor.execute("UPDATE accounts SET balance = balance - %s WHERE id = %s",
                       (amount, user_id[0]))
        cursor.execute("UPDATE accounts SET balance = balance + %s WHERE id = %s",
                       (amount, user_id[1]))
    conn.commit()  # Либо все, либо ничего
except Exception:
    conn.rollback()

Рекомендации

Можно выполнить запрос без явной транзакции:

  • Для SELECT — всегда безопасно
  • Для одного INSERT/UPDATE/DELETE — если критична скорость
  • Когда операция полностью независима от других

НЕ используй без явной транзакции:

  • Когда несколько операций должны быть атомарными
  • При работе с финансовыми данными
  • Когда нужен откат при ошибке
  • При необходимости изоляции от других запросов

Вывод: Да, можно выполнить запрос без явной транзакции благодаря autocommit режиму, но для операций, меняющих данные, всегда используй явные транзакции для безопасности и гарантии ACID свойств.

Можно ли выполнить запрос без транзакции? | PrepBro