Когда использовать SELECT ... для обновления?

пожалуйста, помогите мне понять прецедент позади SELECT ... FOR UPDATE.

Вопрос 1: является ли следующий хороший пример того, когда SELECT ... FOR UPDATE следует использовать?

дано:

  • номера[id]
  • теги[id, name]
  • room_tags[room_id, в tag_id]
    • room_id и tag_id являются внешними ключами

приложение хочет перечислить все комнаты и их теги, но необходимо различать номера без тегов и номера, которые были удалены. Если выбрать ... Для обновления не используется, что может произойти:

  • изначально:
    • помещения, содержит [id = 1]
    • содержит теги [id = 1, name = 'cats']
    • room_tags содержит [room_id = 1, tag_id = 1]
  • резьбы 1: SELECT id FROM rooms;
    • returns [id = 1]
  • резьба 2: DELETE FROM room_tags WHERE room_id = 1;
  • поток 2: DELETE FROM rooms WHERE id = 1;
  • поток 2: [совершает транзакцию]
  • резьбы 1: SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
    • возвращает пустой список

теперь поток 1 думает, что комната 1 не имеет тегов, но на самом деле комната была удалена. Чтобы решить эту проблему, поток 1 должен SELECT id FROM rooms FOR UPDATE, тем самым предотвращая удаление потока 2 из rooms пока поток 1 не будет сделан. Это верно?

Вопрос 2: когда следует использовать SERIALIZABLE изоляции транзакций против READ_COMMITTED С SELECT ... FOR UPDATE?

ответы должны быть портативной (не базы данных). Если это невозможно, объясните почему.

2 ответов


единственный портативный способ достичь согласованности между номерами и тегами и убедиться, что номера никогда не возвращаются после того, как они были удалены, блокирует их с SELECT FOR UPDATE.

однако в некоторых системах блокировка является побочным эффектом управления параллелизмом, и вы достигаете тех же результатов без указания FOR UPDATE явно.


чтобы решить эту проблему, поток 1 должен SELECT id FROM rooms FOR UPDATE, тем самым предотвращая удаление потока 2 из rooms пока Поток 1 выполнен. Это верно?

это зависит от управления параллелизмом, используемого системой базы данных.

  • MyISAM на MySQL (и несколько других старых систем) блокирует всю таблицу на время запроса.

  • на SQL Server, SELECT запросы размещают общие блокировки на записях / страницах / таблицах, которые они рассмотрели, в то время как DML запросы помещают блокировки обновления (которые позже повышаются до эксклюзивных или понижен до общих блокировок). Эксклюзивные блокировки несовместимы с общими блокировками, поэтому либо SELECT или DELETE запрос будет заблокирован до фиксации другого сеанса.

  • в базах данных, использовать MVCC (типа Oracle, PostgreSQL, MySQL С InnoDB), a DML query создает копию записи (тем или иным способом), и обычно читатели не блокируют писателей и наоборот. Для этих баз данных SELECT FOR UPDATE пригодится: он будет блокировать либо SELECT или DELETE запрос до фиксации другого сеанса, так же как SQL Server делает.

когда следует использовать REPEATABLE_READ изоляции транзакций против READ_COMMITTED С SELECT ... FOR UPDATE?

как правило, REPEATABLE READ не запрещает фантомные строки (строки, которые появились или исчезли в другой транзакции, а не модифицируется)

  • на Oracle и ранее PostgreSQL версии REPEATABLE READ на самом деле синоним SERIALIZABLE. В принципе, это означает, что транзакция не видит изменений, сделанных после ее запуска. Итак, в этой настройке последний Thread 1 запрос вернет комнату, как будто она никогда не была удалена (что может быть или не быть тем, что вы хотели). Если вы не хотите показывать комнаты после их удаления, вы должны заблокировать строки с помощью SELECT FOR UPDATE

  • на InnoDB, REPEATABLE READ и SERIALIZABLE разные вещи: читатели в SERIALIZABLE режим set next-key блокировки на записи они оценивают, эффективно предотвращая параллельное DML на них. Так что вам не нужно SELECT FOR UPDATE в сериализуемом режиме, но они нужны в REPEATABLE READ или READ COMMITED.

обратите внимание, что стандарт на режимах изоляции предписывает, что вы не видите определенных причуд в своих запросах, но не определяете, как (с блокировкой или с MVCC или иначе).

когда я говорю: "вам не нужно SELECT FOR UPDATE " Я действительно должен был добавить "из-за побочных эффектов определенной реализации компонента database engine".


короткий ответ:

Вопрос 1: Да.

Q2: не имеет значения, какой вы используете.

ответ:

A select ... for update будет (как следует из этого) выбирать определенные строки, но также блокировать их, как если бы они уже были обновлены текущей транзакцией (или как если бы обновление идентификатора было выполнено). Это позволяет вам обновить их снова в текущей транзакции, а затем зафиксировать, без другой транзакции, способной изменять эти строки в любом путь.

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

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

Так как строки, затронутые my_condition заблокированы, никакая другая транзакция не может их каким-либо образом изменить, и, следовательно, уровень изоляции транзакций здесь не имеет значения.

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

какие уровни изоляции транзакций гарантируют (на разных уровнях) согласованность данных во время транзакций.