Базы данных
MVCC и изоляция: тест с выбором ответа
Шесть вопросов поперёк всего юнита. Каждый отражает решение, которое ты принимаешь в реальном инциденте — какой isolation level, почему autovacuum застрял, что на самом деле означает 40001 — а не определение для заучивания.
Убедись, что связываешь видимость снимков, выбор isolation level, аномалии, которые каждый уровень допускает, пиннинг bloat и wraparound — тот синтез, к которому вели семь уроков.
Соединение B выполняет это под READ COMMITTED, пока соединение A коммитит UPDATE между двумя операторами: ```sql -- B: BEGIN; SELECT balance FROM accounts WHERE id = 42; -- вернул 100 -- (A коммитит: UPDATE accounts SET balance = 0 WHERE id = 42) SELECT balance FROM accounts WHERE id = 42; -- вернёт ? ``` Что вернёт второй SELECT и почему?
Приложение читает строку, вычисляет новое значение в коде и записывает обратно. Два запроса гонятся: ```sql -- оба соединения, конкурентно: BEGIN; SELECT stock FROM items WHERE id = 7; -- оба прочли 10 -- приложение считает 10 - 1 = 9 UPDATE items SET stock = 9 WHERE id = 7; COMMIT; ``` Под уровнем по умолчанию итоговый stock равен 9, а не 8. Что это и каков минимальный фикс?
У таблицы doctors есть инвариант «хотя бы один доктор on_call». Две транзакции под REPEATABLE READ: ```sql -- T1: -- T2 (конкурентно): SELECT count(*) FROM doctors SELECT count(*) FROM doctors WHERE on_call; -- 2 WHERE on_call; -- 2 UPDATE doctors SET on_call=false UPDATE doctors SET on_call=false WHERE name='alice'; WHERE name='bob'; COMMIT; COMMIT; ``` Обе коммитят; теперь ноль докторов на дежурстве. Что произошло и какой уровень это предотвращает?
Autovacuum проработал 30 минут на таблице orders 200 ГБ и логирует «287 million are dead but not yet removable, oldest xmin: 28391456». n_dead_tup не падает. Какова первопричина и что проверить первым?
Перевод между двумя счетами в банке читает оба баланса, дебетует один, кредитует другой. DBA предлагает READ COMMITTED с SELECT ... FOR UPDATE на обеих строках (с блокировкой в согласованном порядке) вместо SERIALIZABLE, который в 5 раз медленнее на пике. Это корректно?
Нагрузка делает много SELECT ... FOR KEY SHARE (неявная блокировка от проверок внешних ключей) и натыкается на предупреждение autovacuum о wraparound гораздо раньше, чем предсказывает её пропускная способность транзакций. Какой счётчик является ограничением?
Сквозная линия юнита — одно правило видимости и решения, надстроенные над ним. Снимки решают, что видит каждая транзакция (на каждый оператор под READ COMMITTED, на транзакцию под REPEATABLE READ). Isolation level решает, какими аномалиями владеешь ты: RC оставляет lost update приложению, REPEATABLE READ бросает 40001 на конкурентных обновлениях строки, но всё ещё допускает write skew, SERIALIZABLE добавляет SSI для ловли циклов write skew. Налог хранения — мёртвые tuples — autovacuum возвращает лишь до глобального oldest xmin, поэтому долгая транзакция или orphan slot — первый подозреваемый при разрастании bloat. А 32-битные счётчики XID и MultiXact оба требуют заморозки до wraparound. Выбирай самый слабый уровень, ещё защищающий твой инвариант, затем оборони границу.