![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Фундаментальный вопрос о правильном использовании транзакций. [D6, Правильная БД]
Курдль © (2004-05-13 10:47:00)После плодотворного общения на этом уважаемом форуме у меня сложилась стойкая ассоциация, что транзакция, это такая большая ручка от чемодана, которую прилепляют к багажнику "Жигулей", чтобы они ездили, как "Феррари" (еще ее называют спойлером).
Есть предложение раз и навсегда выработать общие рекомендации по работе с транзакциями и вынести результат куда-нить в ФАК.
У меня есть свой стиль, который я ни в коем случае никому не навязываю. Но очень хотел бы почитать всеобщую критическую оценку.
1. Я работаю только с кэшированными изменениями.
2. После работы с набором данных фиксирую обновления.
3. В 90% случаев не пользуюсь явными транзакциями, а просто делаю ApplyUpdates.
4. Пользуюсь явными транзакциями лишь в тех случаях, когда требуется откат изменений, сделанных во множественных таблицах со сложными связями.
5. Как правило код фиксации при такой работе занимает 5-10 строк.
У кого есть другие мнения? Может напишем общий "манифест о транзакциях"? Было бы здорово...
Курдль (2004-05-13 15:16:00) [21]
> kaif © (13.05.04 14:00) [33]
> Курдль © (13.05.04 13:45) [28]
> Как Ваш пример среагирует на дубликат?
Дубликат чего? Уникального поля? Получит отлуп от СУБД и вернет все взад. Какая разница, на каком уровне активируются constraint-ы или триггеры БД, от одиночного поста, или пакета изменений?
> Если я где нибудь в коде сделаю детайл-датасету Close;Open,
> то это приведет к завершению транзакции.
При кэшированных изменениях это приведет всего лишь к refresh-у датасэта. А без них... Я не уверен, что к закрытию транзакции. Транзакция только в IB может открываться множественно, в остальных СУБД транзакция жестко привязана к сессии, а не к датасэту.
kaif (2004-05-13 14:05:00) [22]Прошу прощения за дубликат. Вот - легок на помине. :)))) Клянусь, я тут не причем. Сервер долго не отвечал, а кнопка продолжала нажиматься... Вот Вам пример! Обе записи имеют свой отдельный ID, как можно заметить. Однако уникальность ничем не гарантирована.
Vlad (2004-05-13 14:03:00) [23]
> Курдль © (13.05.04 13:57) [32]
> Что значит "закрытие"?
Закрытие - это то и значит. Close тоесть.
Если я где нибудь в коде сделаю детайл-датасету Close;Open, то это приведет к завершению транзакции.
kaif (2004-05-13 14:01:00) [24]Курдль © (13.05.04 13:45) [28]
Как Ваш пример среагирует на дубликат?
Или в Вашей базе допускается создавать энное количество одного и того же контрагента?
Вы получили ID и думаете, что этого достаточно?
А завтра запросят "долг по этому контрагенту" и получат цифру, не соответствующую реальному долгу. После "расследования" (не без помощи программиста) выяснится, что имеется еще один "точно такой же", но с другим ID.
Как тогда Вы будете объяснять заказчику преимущества метода кешированных изменений на клиенте перед уникальньным индексом на ИНН и Rollback при попытке создания дубликата?
kaif (2004-05-13 14:00:00) [25]Курдль © (13.05.04 13:45) [28]
Как Ваш пример среагирует на дубликат?
Или в Вашей базе допускается создавать энное количество одного и того же контрагента?
Вы получили ID и думаете, что этого достаточно?
А завтра запросят "долг по этому контрагенту" и получат цифру, не соответствующую реальному долгу. После "расследования" (не без помощи программиста) выяснится, что имеется еще один "точно такой же", но с другим ID.
Как тогда Вы будете объяснять заказчику преимущества метода кешированных изменений на клиенте перед уникальньным индексом на ИНН и Rollback при попытке создания дубликата?
Курдль (2004-05-13 13:57:00) [26]
> Тут закрытие Detail датасета приводит к неявному подтверждению
> всей транзакции. И что, это нормально ?
Что значит "закрытие"? Если пройдет успешно 1-я фаза (запись данных) из обоих датасэтов, то ApplyUpdates неявно (т.е. не Вами инициированно) совершит фиксацию обновлений в БД, а если не успешно - откатит изменения в обоих датасетов и закроет транзакцию, неявно открытую им же перед началом всей цепочки изменений.
kaif (2004-05-13 13:51:00) [27]> 3. Правильная организация транзакций обычно прежде всего
> преследует достижение правильной координации совместной
> работы множества юзеров с базой данных и поэтому сильно
> зависит от задачи.
Совершенно не согласен! Эта задача СУБД на 99%! Правильная организация транзакций преследует достижение правильного управления данными в рамках одной сессии (от одного юзера). Например, если юзер начал правку нескольких взаимосвязанных таблиц, изменения в которых допускаются только солидарные.
Мне кажется, это заблуждение.
Зачем тогда полученный такой кровью режим ReadCommitted? Или режимы wait? Тогда достаточно иметь во всех случаях Snapshot. При таком подходе действительно между кешированием на клиенте и контекстом транзакции, возможно, и нет никакой разницы...
ИМХО, главное в нормальной СУБД - уникальность. Уникальность может быть проверена только при посылке данныхна сервер. И больше никак. И уникальные индексы работают за пределами транзакцийвообще. Таким образом, нельзя считать, что правильная организация транзакций преследует достижение правильного управления данными в рамках одной сессии. Так как уникальность в эти рамки никак не лезет. И то, подвердилдругой юзер ту запись, которая в этой сессииприведет к нарушению уникальности или не подвердил, имеет первостепенное значение для того, чей именноапдейт рухнет. Я уже не говорю о тех случаях, когда возможный конфликт двух юзеров ставится целью(пессимистическая модель). Когда нельзя никак допуститьпродажу одного железнодорожного билета из двух касс. И тогда выбор способа изоляции транзакций полностью определяется самой задачей. Хотя, согласен, это редкий случай.
Vlad (2004-05-13 13:50:00) [28]
> Вот приведенный в хэлпе способ обхода проблем с внешними
> ключами:
Тут закрытие Detail датасета приводит к неявному подтверждению всей транзакции. И что, это нормально ?
bushmen (2004-05-13 13:49:00) [29]>первичного ключа из заранее взятого значения типа GetIdentity
Вот Вы уже переползли на конкретику. Что есть в одной СУБД, не обязательно есть в другой
Курдль (2004-05-13 13:45:00) [30]
> Я бы не стал закладываться на то, что в базе данных как
> правило:
> 1. Не используются широко "справочники".
> 2. Не используются уникальные ключи.
Я только этим и занимаюсь :)
Например, при создании нового лица (физ., юр.) в одной форме создаются до 5 записей в раздичных таблицах, типа адреса, телефоны, счета, паспорта и собснно лица. Ну и что? Все зависимые таблицы получают значение внешнего ключа, а главная - первичного ключа из заранее взятого значения типа GetIdentity.
Потом отрабатываетсяDataBase.ApplyUpdates([лица, адреса, телефоны, счета, паспорта]);
Вот приведенный в хэлпе способ обхода проблем с внешними ключами:
procedure TForm1.DetailBeforeClose(DataSet: TDataSet);
begin
if Master.UpdatesPending or Detail.UpdatesPending then
if Master.UpdateStatus = usInserted then
Database1.ApplyUpdates([Master, Detail])
else
Database1.ApplyUpdates([Detail, Master]);
end;
Vlad (2004-05-13 13:42:00) [31]
> kaif © (13.05.04 13:32) [26]
> Люди! Не пренебрегайте CachedUpdates!
Это правильно. Пренебрегать конечно не стоит, тем более что в некоторых задачах это полезная вещь. А в некоторых - бессмысленная, и м.б. даже вредная.
Что касается применения ApplyUpdates(<array of TDataSet>), так это тоже не всегда хорошо. Зачастую необходимо сбросить из кэша данные только одного-двух датасетов из десяти(к примеру), не подтверждая транзакцию. Этот метод тут уже не подходит.
Поэтому если уж пользоваться им, то с головой. Т.е. надо понимать к чему это приведет.
И не надо говорить "всегда и везде пользуйтесь только этим". Оно неправильно.
kaif (2004-05-13 13:32:00) [32]2 Курдль © (13.05.04 13:04) [24]
А как такой пример?
1. Добавляем в справочник новый элемент (дубликат), который долженнарушить альтернативную (предметную) уникальность, например "наименование товара". Получаем для него ID (из генератора). В общем, все, как полагается...
2. Добавляем полученную ссылку на этот элемент в документ, например, в накладную. Все по-прежнему OK, так как на сервер ничего кроме запроса на новый ID от генератора не посылалось.
3. Применяем DataBase.ApplyUpdates([DataSet1, ... DataSetN]);
Получаем сообщение о нарушении уникального ключа в форме:
Violation of primary or unique key in table ...
4. Что теперь?
Я бы не стал закладываться на то, что в базе данных как правило:
1. Не используются широко "справочники".
2. Не используются уникальные ключи.
Неужели это и есть случай "сложных связей множества таблиц"? Или же это достаточно типичный случай "простых связей"?
Таким образом фраза "сложные связи" становится очень субъективно воспринимаемой и от рекомендаций ничего не остается, кроме самого исходного базового мотива "пореже осуществлять, если это возможно, команды update, insert, delete, чаще пользуясь механизмами кеширования на клиенте".
В общем, я бы сократил все вышесказанное до одной простой рекомендации:
Люди! Не пренебрегайте CachedUpdates!
А явный старт или не явный, много кода или мало... Это никакого отношения к делу в данном случае, ИМХО, не имеет...
Курдль (2004-05-13 13:26:00) [33]
> kaif © (13.05.04 12:57) [22]
> ИМХО, политика транзакций для разных СУБД и разных задач
> - разная.
> 1. Не все СУБД поддерживают полный набор режимов изоляции
> транзакций (DirtyRead, RedCommitted, RepeatebleRead и т.д.)
> 2. Некоторые локальные СУБД, например, связка Paradox+BDE
> вообще имеют ограничение на количество обновлений в рамках
> одной транзакции и к ним не применимы обычные рекомендации,
> кажущиеся оптимальными в отношении серверных СУБД.
В заголовке топика значится [D6, Правильная БД], вряд ли следует относить к последним "Paradox+BDE"
> 3. Правильная организация транзакций обычно прежде всего
> преследует достижение правильной координации совместной
> работы множества юзеров с базой данных и поэтому сильно
> зависит от задачи.
Совершенно не согласен! Эта задача СУБД на 99%! Правильная организация транзакций преследует достижение правильного управления данными в рамках одной сессии (от одного юзера). Например, если юзер начал правку нескольких взаимосвязанных таблиц, изменения в которых допускаются только солидарные.
> 5. Применение кешированных изменений не всегда оправдано
> (например, если редактируется результат объединения кучи
> таблиц и нужен Refresh для полного отображения результата
> редактирования текущей строки).
Это совсем не понятно. Мне кажется, что сначала select-ом получают данные, потом их изменяют и возвращают в БД
> 6. Как уже было замечено, и по-моему так оно и есть, абстракция
> кешированных изменений не регламентирует еще конкретной
> реализации подтверждения транзакции, так что с какими-то
> компонентами, возможно, ApplyUpdates не вызовет ни старта,
> ни подтверждения транзакции вообще, ни "явной", ни "неявной".
> Различение транзакций на "явные" и "неявные" может не иметь
> никакого отношения к сущности самих этих транзакций и вызывать
> дополнительные недоразумения.
Я привел список компонентов, которые полностью поддерживают метод ApplyUpdates и исполняют его так, как описано в документации (методом двухфазного подтверждения).
Курдль (2004-05-13 13:04:00) [34]
> И еще мне кажется, что увязывание вопросов кешированных
> изменений с вопросами транзакций говорит о том, что автор
> предлагает видимо вместо RollBack чаще использовать CancelUpdates,
> так как не находит (в большинстве случаев) между ними разницы.
Да я просто предлагаю не разорять улей лишний раз! Потому и рекомендуюDataBase.ApplyUpdates([DataSet1, ... DataSetN]);, чтобы не нагружать ни свой мозг, ни компьютерный лишними проблемами - указанная конструкция сама все сделает!
Vlad (2004-05-13 13:00:00) [35]Кстати, по поводу БДЕ, для меня пока так и не ясен ответ на вопрос [14]
автору:
Ну ты бы ветку с этого и начал: "Мужики, я хочу заявить, что считаю кэшированные изменения оптимальным методом работы с БД"
Ну и считай наздоровье.
"Если ты считаешь, что жена тебе не изменяет, значит она тебе действительно не изменяет" (с)Vlad
А начал-то с транзакций ?
kaif (2004-05-13 12:57:00) [36]ИМХО, политика транзакций для разных СУБД и разных задач - разная.
1. Не все СУБД поддерживают полный набор режимов изоляции транзакций (DirtyRead, RedCommitted, RepeatebleRead и т.д.)
2. Некоторые локальные СУБД, например, связка Paradox+BDE вообще имеют ограничение на количество обновлений в рамках одной транзакции и к ним не применимы обычные рекомендации, кажущиеся оптимальными в отношении серверных СУБД.
3. Правильная организация транзакций обычно прежде всего преследует достижение правильной координации совместной работымножества юзеров с базой данных и поэтому сильно зависит от задачи.
4. Сокращение программного кода "любой ценой" не может вообще служить критерием "правильного программирования".
5. Применение кешированных изменений не всегда оправдано (например, если редактируется результат объединения кучи таблиц и нужен Refresh для полного отображения результата редактирования текущей строки).
6. Как уже было замечено, и по-моему так оно и есть, абстракция кешированных изменений не регламентируетеще конкретной реализации подтверждения транзакции, так что с какими-то компонентами, возможно, ApplyUpdates не вызовет ни старта, ни подтверждения транзакции вообще, ни "явной", ни "неявной". Различение транзакций на "явные" и "неявные" может не иметь никакого отношения к сущности самих этих транзакций и вызывать дополнительные недоразумения. Например, транзакция стартовала неявно. Можно ли ее откатить? В старых IBX в документации было сказано, что нельзя. А я посмотрел в новых IBX реализацию этого "неявного" старта и не нашел никаких отличий от старта "явного". Таким образом, в новых IBX различия между "неявным" и "явным" стартом нет. То есть эти различия зависят от реализациии не носят принципиального характера, а для некоторых СУБД вообще бессмысленны. И это может сбить разработчиков с толку, если такую терминологию применять "в рекомендациях".
Вообще, ИМХО, любые рекомендации должны прояснять вопросы, а не только давать советы.
Так что, ИМХО, любой "набор рекомендаций вообще" был бы вредным для начинающих разработчиков, а приведенный набор рекомендаций - просто ошибочным. Может быть имеет смысл создать набор рекомендаций для "работающих с BDE", да и то для каждой СУБД - в отдельности. Примерно так пишутся хорошие книги.
И еще мне кажется, что увязывание вопросов кешированных изменений с вопросами транзакций говорит о том, что автор предлагает видимо вместо RollBack чаще использовать CancelUpdates, так как не находит (в большинстве случаев) между ними разницы. Может об этом стоит поговорить? Так есть ли разница и когда именно она проявляется? И тогда легче будет выработать более точные рекомендации. По крайней мере, станет понятнее пункт 4. Может быть следует сформулировать все сказанное так:
"Везде, где это возможно, применяйте на клиенте режимы CachedUpdates и метод CancelUpdates вместо посылки данных на сервер и отката транзакции".
Или так:
"Старайтесь вообще как можно меньше взаимодействовать с СУБД, и, если это возможно, всегда применяйте режим CachedUpdates".
Интересно, а такая рекомендация хороша или нет? Я лично затрудняюсь с лету ответить...
Курдль (2004-05-13 12:49:00) [37]
> 1. Если ты указываешь постоянно в своих постах некий ApplyUpdates,
> то указывай и класс, для которого этот метод ты подразумеваешь.
Я имею в виду TDataBase и его наследников или аналогов(TIBDatabase, TSDDatabase, TOracleSession и т.п.).
Johnmen (2004-05-13 12:39:00) [38]>Курдль © (13.05.04 12:17) [15]
>Давай конструктивнее!
С удовольствием. На взаимной основе...
>Что именно не соответствует моим постулатам?
и далее...
>Лейтмотив моего "выступления" - это моё личное убеждение, что
>связка "кэшированные изменения + неявная транзакция по
>ApplyUpdates" - самый оптимальный вариант организации обмена
>данными с БД.
1. Если ты указываешь постоянно в своих постах некий ApplyUpdates, то указывай и класс, для которого этот метод ты подразумеваешь. На это я уже неоднократно намекал. Т.к. данный метод совершенно различен по реализации для разных классов.
2. Надежда на функционирование неявной транзакции может привести к неявным/непредсказуемым/неожиданным последствиям :)
3. По поводу оптимальности Vlad © уже сказал. Я с ним согласен.
Курдль (2004-05-13 12:29:00) [39]
> Ты определись, о чем ты говоришь - об оптимальном обмене
> данными с БД или все-таки конкретно о работе с транзакциями
> ?
Я говорю о том, что для оптимальной работы с БД вовсе незачем попусту размахивать транзакциями.
> Кстати, если уж на то пошло, то связка "кэшированные изменения
> + неявная транзакция по ApplyUpdates" - далеко не всегда
> оптимальный вариант обмена данными
"Оптимальный" - не значит "абсолютный". А я настаиваю, что оптимальный!
Цитата: "К преимуществам способа кэшированных изменений перед транзакционным способом можно отнести:
- минимизацию сетевого трафика;
- отсутствие блокировок на изменяемые записи."
Vlad (2004-05-13 12:27:00) [40]
> Как раз в [*] говорится про то, Commit автоматом вызывается
> лишь при SHARED AUTOCOMMIT.
Что такое [*] ?
Ты исходники почитай. ApplyUpdates вызывает безусловный коммит, независимо не от каких настроек.