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