Когда использовать 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(и несколько других старых систем) блокирует всю таблицу на время запроса.In
SQL Server,SELECTзапросы помещают общие блокировки на записи / страницы / таблицы, которые они рассмотрели, в то время какDMLзапросы размещают блокировки обновления (которые позже повышаются до эксклюзивных или понижен до общих блокировок). Эксклюзивные блокировки несовместимы с общими блокировками, поэтому либоSELECTилиDELETEзапрос будет заблокирован до фиксации другого сеанса.в базах данных, использовать
MVCC(типаOracle,PostgreSQL,MySQLСInnoDB), aDMLзапрос создает копию записи (тем или иным способом) и обычно читатели не блокируют писателей и наоборот. Для этих баз данныхSELECT FOR UPDATEпригодится: он будет блокировать либоSELECTилиDELETEзапрос до фиксации другого сеанса, так же какSQL Serverделает.когда следует использовать
REPEATABLE_READизоляция транзакций по сравнению сREAD_COMMITTEDСSELECT ... FOR UPDATE?как правило,
REPEATABLE READне запрещает фантомные строки (строки, которые появились или исчезли в другой транзакции, а не модифицируется)
In
Oracleи ранееPostgreSQLверсииREPEATABLE READна самом деле синонимSERIALIZABLE. В основном это означает, что транзакция не видит изменений, внесенных после ее запуска. Так что в этой настройке последнийThread 1запрос вернет номер, как будто он никогда не был удален (что может быть или не быть тем, что вы хотели). Если вы не хотите показывать номера после их удаления, вы должны заблокировать строки с помощьюSELECT FOR UPDATEIn
InnoDB,REPEATABLE READиSERIALIZABLEразные вещи: читатели вSERIALIZABLEрежим установлен далее-ключевые замки на записи они оценивают, эффективно предотвращая одновременноеDMLна них. Так что вам не нужноSELECT FOR UPDATEв сериализуемом режиме, но они нужны вREPEATABLE READилиREAD COMMITED.обратите внимание, что стандарт режимов изоляции предписывает, что вы не видите определенных причуд в своих запросах, но не определяете, как (с блокировкой или с
MVCCили иначе).когда я говорю " вам не нужно
SELECT FOR UPDATE" Я действительно должен был добавить "из-за побочных эффектов определенной реализации ядра СУБД".
короткий ответ:
Q1: Да.
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заблокированы, никакая другая транзакция не может изменить их каким-либо образом, и, следовательно, уровень изоляции транзакций здесь не имеет значения.обратите внимание также, что уровень изоляции транзакций не зависит от блокировки: установка другого уровня изоляции не позволяет обойти блокировку и обновить строки в другая транзакция, которая заблокирована вашей транзакцией.
какие уровни изоляции транзакций гарантируют (на разных уровнях) согласованность данных во время выполнения транзакций.
Comments