Когда использовать 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), aDMLquery создает копию записи (тем или иным способом), и обычно читатели не блокируют писателей и наоборот. Для этих баз данных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 заблокированы, никакая другая транзакция не может их каким-либо образом изменить, и, следовательно, уровень изоляции транзакций здесь не имеет значения.
обратите внимание также, что уровень изоляции транзакций не зависит от блокировки: установка другого уровня изоляции не позволяет обойти блокировку и обновление строк в другая транзакция, заблокированная вашей транзакцией.
какие уровни изоляции транзакций гарантируют (на разных уровнях) согласованность данных во время транзакций.