Можно ли выполнить SELECT запрос без открытия транзакции в базе данных
Что проверяют
Знаете ли вы, что в PostgreSQL любой запрос выполняется внутри транзакции, и понимаете ли, как этим управляет драйвер Python. Следом обычно спрашивают, что такое «idle in transaction» и чем опасно соединение, которое после SELECT не закрыло транзакцию.
Как отвечать
Со стороны базы ответ «нет»: без явного BEGIN PostgreSQL оборачивает оператор в неявную транзакцию и сразу её фиксирует. Со стороны Python всё решает режим драйвера:
- DB-API 2.0 (PEP 249): по стандарту соединение не в автокоммите. psycopg 2 и psycopg 3 при первом же
execute(), даже для SELECT, отправляютBEGIN, и транзакция висит доcommit()илиrollback(); - автокоммит:
conn.autocommit = Trueв psycopg, тогда каждый запрос живёт своей короткой транзакцией. В psycopg 3 явные блоки пишут черезwith conn.transaction():; - SQLAlchemy 2.0: и
Connection, иSessionначинают транзакцию автоматически (autobegin), поэтому соединение возвращают в пул черезwith-блок или явныйcommit()либоrollback(); - Django: по умолчанию работает в автокоммите, транзакцию открывает
transaction.atomic().
Явная транзакция для чтения нужна, когда несколько SELECT должны видеть одно согласованное состояние, например в отчёте. На уровне READ COMMITTED каждый оператор берёт свой снимок, для общего снимка нужен REPEATABLE READ, можно вместе с READ ONLY.
Где спотыкаются
Говорят, что SELECT транзакции не требует, и не знают про autobegin. В проде это соединения в состоянии «idle in transaction»: они держат блокировку AccessShareLock и не дают выполнить ALTER TABLE, а на REPEATABLE READ ещё и удерживают снимок, из-за которого VACUUM не может вычистить мёртвые строки. Страховка на стороне базы: idle_in_transaction_session_timeout.
Считают, что чтение без транзакции видит «грязные» данные. В PostgreSQL их нет вовсе: READ UNCOMMITTED там работает как READ COMMITTED.
Путают with conn: в двух версиях psycopg. В psycopg 2 блок фиксирует транзакцию, но соединение не закрывает, а в psycopg 3 фиксирует и закрывает.