|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
alekcvpВся операция, требующая блокировки, всё равно должна в одной транзакции выполняться Но сама блокировка может выполняться предварительно, в отдельной транзакции (если через флаги, в отдельной таблице или в отдельном столбце). alekcvpЕсли в этот момент какой-то пользователь изменит запись или добавит новую в таблицу RECORDS с этим же id_grp (читать можно), то процесс придётся начинать сначала. Вот это была мотивация или требование? Что должно произойти, если пока мы рассчитываем группу №1 другое соединение пытается удалить или изменить или добавить строку в этой же группе? Возможно, некоторые столбцы всё же можно изменять. Возможно, изменяющего клиента нужно сразу послать. Возможно, надо наоборот экстренно прервать процесс обновления. POST_EVENT и rollback, например. Возможно, надо учитывать как давно или скуолько раз обновление уже откладывалось. Типа до получаса разрешаем так прерывать обновление, а потом начинаем отказывать изменяющим клиентам и принудительно доводим перересчёт до конца. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 18:20 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
AriochalekcvpНу, в идеале, мне хочется чтобы это обновление блокировало только записи связанные с обновляемой группой, позволяя нормально работать со всеми остальными... Пройдет пару лет, система станет мощнее, и потребуется двумя "исполнителями" одновременно пересчитывать и апдейтить записи для двух разных групп. Не пройдёт, это "технический" процесс и он запускается не пользователями. Да и приложение не корпоративного масштаба, так - внутренний софт на один отдел. Завтра действительно попробую вариант с отдельной таблицей... ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 19:47 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
AriochalekcvpНу это понятно, идея мне нравится. Но заводить для этого отдельную таблицу... Лучше завести ОДНУ лишнюю табличку и для блокировки добавлять ОДНУ строку в неё, чем переписывать ВСЕ строки целевой группы в твоей больше таблицы, даже если из всей группы ты потом только изменишь 1% записей.Сейчас я завёл ОДНО ПОЛЕ в таблице групп и делаю ДВУКРАТНУЮ запись в него на каждый процесс. А в варианте с отдельной таблицей, в неё будут записи (попытки записей) при КАЖДОМ изменении данных в ТРЁХ таблицах. Плюс в моём варианте [если возникнет необходимость] можно проверить блокировку даже из read_commited транзакции и не дать прочитать данные из обновляемой группы. В случае отдельной таблицы с попыткой записи - так не получится. Ariochкто есть uid ?ID пользователя в таблице пользователей в базе. Т.е. поставить блокировку может кто угодно из тех кто имеет право писать в эти таблицы, а снять - только тот кто поставил. Ну и при дисконнекте по этому значению снимаются блокировки. AriochalekcvpFireBird 3 поддерживает временные таблицы (которые в базу не пишутся), доступные одновременно всем подключениям? а если бы поддерживал? ты не хочешь создавать ещё одну обычную таблицу (со списоком блокировок), но без хочешь создать ещё одну необычную (временную) таблицу? а в чем разница?В том, что при [аварийном] выключении сервера и при backup/restore не надо париться об этой таблице. AriochalekcvpЕсли во время обновления данных произойдёт какой-либо exception, то она вызывается ещё раз с locked = 0, после чего транзакции делается rollback. зачем? Анекдот про два стакана на ночь у постели программиста?Я же показал - там внутри автономная транзакция, которая в любом случае снимает блокировку, а rollback основной транзакции отменяет изменения, которые уже успел сделать "вывалившийся" процесс. AriochalekcvpВся операция, требующая блокировки, всё равно должна в одной транзакции выполняться Но сама блокировка может выполняться предварительно, в отдельной транзакции (если через флаги, в отдельной таблице или в отдельном столбце). Может и так. Но мне проще всё "завернуть" в одну транзакцию. AriochalekcvpЕсли в этот момент какой-то пользователь изменит запись или добавит новую в таблицу RECORDS с этим же id_grp (читать можно), то процесс придётся начинать сначала. Вот это была мотивация или требование? Что должно произойти, если пока мы рассчитываем группу №1 другое соединение пытается удалить или изменить или добавить строку в этой же группе?Ну представьте что там в записях электронные подписи, а в мемберсах - ключи для проверки. Что будет, если при смене этих подписей, кто-то добавит ещё одну запись, подписанную старым ключём (который считывается непосредственно перед подписью)? Если же обновлять сначала ключ, а потом подписи (в разных транзакциях), то кто-то в этот момент может попытаться проверить старую подпись новым ключём и будет облом. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:04 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
alekcvp, а что, старый и новый ключ не могут перекрываться по датам? Так ведь и происходит. я получаю новый ключ заранее, до окончания старого. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:14 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
alekcvpСейчас я завёл ОДНО ПОЛЕ в таблице групп и делаю ДВУКРАТНУЮ запись в него на каждый процесс. А в варианте с отдельной таблицей, в неё будут записи (попытки записей) при КАЖДОМ изменении данных в ТРЁХ таблицах.Ну и зря. Так тебе нужно обновлять миллион записей (реальных данных, с возможно еще какими-то триггерами, индексами) два раза по неизвестно какому условию. А в случае таблицы локов - один раз, и еще миллион раз - обновлять одну запись с одним полем по ключу в короткой таблице. Что намного быстрее. К тому же, твой вариант поможет только при обновлении данных, а таблицу локов можно использовать, если нужно, и для insert/delete. alekcvpПлюс в моём варианте [если возникнет необходимость] можно проверить блокировку даже из read_commited транзакции и не дать прочитать данные из обновляемой группы. В случае отдельной таблицы с попыткой записи - так не получится.Транзакция любого типа может прочитать только закоммиченные данные. Так у тебя одна транзакция, или две? Или ты после первого update делаешь CommitRetaining? ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:21 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
alekcvpID пользователя в таблице пользователей в базе. Т.е. поставить блокировку может кто угодно из тех кто имеет право писать в эти таблицы, а снять - только тот кто поставил. Ну и при дисконнекте по этому значению снимаются блокировки. а если клиент отвалился без отрабатывания триггера? как, например, любому другому клиенту в триггере on connect определить, что пользователя Х остались бесхозные блокировки и снять их ? Also, если расчёты идут так долго, то как часто клиент общается с сервером? 1. рассчитывает строку, записывает её в сервер (update), рассчитывает следующую, записывает, в конце - commit 2. долго рассчитывает все строки, потом все сразу сливает на сервер и commit, во время самих рассчетов с сервером не общается alekcvpВ том, что при [аварийном] выключении сервера и при backup/restore не надо париться об этой таблице. On connect триггер, зачищающий блокировки, IMHO все равно желателен. alekcvpМожет и так. Но мне проще всё "завернуть" в одну транзакцию. у тебя автономные транзакции летают - это уже едва ли "в одну" alekcvpавтономная транзакция, которая в любом случае снимает блокировку > Because the autonomous transaction is completely independent of its parent, care must be taken to avoid deadlocks. В официальных примерах они используются только, чтобы добавлять записи в лог-таблицы. У меня сильные сомнения, что есть какие-то гарантии по синхронизации. Т.е. я не вижу принципиальной невозможности такой последовательности: 1. запускается транзакция на установку блокировки, но почему-либо притормаживается. Sweep пошёл тяжелый, или встала ждёт строку, модифицированную незакомиченной другой транзакцией 2. выполняются изменение данных и запускается автономка для снятия лока 3. 2-я автономка завершается, лок снят 4. 1-я автономка "отмерзает" завершается, лок выставлен Я бы очень подумал, прежде чем с блокировками работать асинхронными средствами. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:33 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
alekcvpМожет и так. Но мне проще всё "завернуть" в одну транзакцию. дело не в том, что проще, а в том какого конкретно детерминированного поведения ты хочешь джобиться, в случае конфликта - 21365634 !!! Вот тебе ещё на вскидку асинхронный сценарий. 1. ты из соединения 1 начал редактировать записи по группе 1, 2. начинает создаваться автономка №1 для выставления лока 3. пересчитывается и изменяется одна строка данных по группе 1 4. ты из соединения 2 редактируешь САМУ ГРУППУ 1, меняешь ей название например, но еще не коммитишь 5. автономка №1 начинает исполняться, обнаруживает, что запись группы была изменена из соединения 2 и по конфликту умирает с роллбаком. 6. соединение 2 коммитит переименование группы 7. пересчитывается и изменяется 2-я строка данных по группе 1 8. пересчитывается и изменяется 3-я строка данных по группе 1 ....и т.д., изменения данных пошли, а лок не выставлен. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:38 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
alekcvpНу представьте что там в записях электронные подписи, а в мемберсах - ключи для проверки. я не хочу представлять и гадать, я хочу чтобы вы чётко сформулировали как в такой ситуации должна правильно вести себя ваша система. Какие варианты ее поведения желательны, какие нежелательны, но допустимы, и какие недопустимы категорически. после того, как вы сформулируете четко поведение, "дизайн", можно обсуждать как такую "архитектуру" лучше закодировать. но кодировать неизвестное несформулированное поведение - это с ненулевой вероятностью получить программу качества GIGO ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:42 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
YuRockТак тебе нужно обновлять миллион записей нет, не так. он добавил столбец - НЕ В ТАБЛИЦУ ДАННЫХ, а в отдельные "справочник групп" "миллионы записей" он меняет в таблицах MEMBERS и RECORDS а "выставляет флаг" в таблице GROUPS во всяком случае я так его понял PS. "обновлять два раза" возможно не так уж и плохо, если обновляются ВСЕ строки конкретной группы. Во всяком случае это не хуже, чем SELECT fOR UPDATE Вот если бы он обновлял для блокировки миллион строк ,а потом реально вносил изменения данных в тысячу из них - тогда да, жуткий перерасход записей. Но если обновление тотальное - то двукратное или даже трекратное умножение записей вполне могло бы быть терпимым ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:49 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
YuRockalekcvpСейчас я завёл ОДНО ПОЛЕ в таблице групп и делаю ДВУКРАТНУЮ запись в него на каждый процесс. А в варианте с отдельной таблицей, в неё будут записи (попытки записей) при КАЖДОМ изменении данных в ТРЁХ таблицах.Ну и зря. Так тебе нужно обновлять миллион записей (реальных данных, с возможно еще какими-то триггерами, индексами) два разаО, я недочитал. Раз ты только "таблицу групп" дважды обновляешь - значит, это изменение "на клиенте" в логике. Значит, раз этого достаточно, можно и запись в таблицу локов один раз делать, а не в триггерах. Впрочем, это не важно, раз достаточно. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:50 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
Ariochнет, не так. он добавил столбец - НЕ В ТАБЛИЦУ ДАННЫХ, а в отдельные "справочник групп"Да, я уже сам заметил. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:51 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
YuRock, ну он просто объединил таблицу локов и таблицу-справочник групп воедино если она не слишком широкая и сама по себе не часто меняется, то и нехай себе ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:51 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
.....только нахрена же автономки тут. Блокировки всегда делаются, чтобы принудительно и гарантировано сериализовать(синхронизировать) принципиально асинхронные (параллельные, хаотические по времени начала) операции. А он пытается одни асинхронные операции сериализовать другими асинхронными. Ну в большинстве случаев это сработает, конечно, не вопрос. Всё таки БД на диске - не многоядерный процессор. Но изредка гейзенбаги скорее всего будут случаться. ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:53 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
AriochВот тебе ещё на вскидку асинхронный сценарий.Я правильно тебя понял, что в твоём "асинхронном сценарии" основная и автономные тр-ции выполняются параллельно ? ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 20:57 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
kdvalekcvp, а что, старый и новый ключ не могут перекрываться по датам? Так ведь и происходит. я получаю новый ключ заранее, до окончания старого.Это был пример для понимания, а не реальная ситуация. По факту там цифровые конверты - ключи в members и сами данные в records. Процесс обновления вызывается когда у кого-то отзывается доступ к группе, для смены ключей. YuRockalekcvpПлюс в моём варианте [если возникнет необходимость] можно проверить блокировку даже из read_commited транзакции и не дать прочитать данные из обновляемой группы. В случае отдельной таблицы с попыткой записи - так не получится.Транзакция любого типа может прочитать только закоммиченные данные. Так у тебя одна транзакция, или две? Или ты после первого update делаешь CommitRetaining? У меня две транзакции. И проверить значение lock-поля в таблице групп я могу и из читающей, а чтобы попробовать что-то записать в отдельную таблицу и узнать получится или нет - мне придётся обязательно стартовать и пишущую. AriochalekcvpID пользователя в таблице пользователей в базе. Т.е. поставить блокировку может кто угодно из тех кто имеет право писать в эти таблицы, а снять - только тот кто поставил. Ну и при дисконнекте по этому значению снимаются блокировки.а если клиент отвалился без отрабатывания триггера?Пока думаю над этим, но возможно заставлю ту процедуру, которая проверяет его наличие, проверять (опять же при состоянии locked = 1) так же наличие активной сессии заблокированного пользователя. Ну или перейду на вариант с отдельной таблицей - у обоих вариантов есть свои плюсы и минусы. AriochAlso, если расчёты идут так долго, то как часто клиент общается с сервером?Хм, мне казалось что я написал: считывает строку, обрабатывает (~10 секунд), запихивает назад. AriochalekcvpМожет и так. Но мне проще всё "завернуть" в одну транзакцию. у тебя автономные транзакции летают - это уже едва ли "в одну" В одну с точки зрения приложения, что там в базе происходит - приложению знать не обязательно. AriochЯ бы очень подумал, прежде чем с блокировками работать асинхронными средствами.Там же написано что автономная транзакция наследует параметры "родительской", родительская - nowait, т.е. либо обновила сразу, либо вывалилась с ошибкой. Или я как-то не так понимаю механизм nowait-транзакций (тоже вариант)? ... |
|||
:
Нравится:
Не нравится:
|
|||
24.04.2018, 21:34 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
hvladЯ правильно тебя понял, что в твоём "асинхронном сценарии" основная и автономные тр-ции выполняются параллельно ? могут выполняться параллельно. в доках не указаны никакие гарантии синхронизации, стало быть это деталь реализации и сервер может делать так, как ему будет удобнее. В зависимости от содержимого базы ,настроек, версии сервера и фазы луны. закладываться на то, что не имеет никаких гарантий и может при любом изменении данных или версий рассыпаться, да еще "в многопотоке" с его трудностями воспроизводимой диагностики - я бы не стал. ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2018, 12:14 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
alekcvpТам же написано что автономная транзакция наследует параметры "родительской" там во-первых НЕ написано, что родительская транзакция встанет торчком и будет ждать окончания автономки. Она запустит процесс создания и выполнения автономки, но когда та закончится и закоммититься - детали реализации. Для ведения логов это подходит, если у тебя запись в лог внесется на несколько секунд позже, чем данные, не важно, лишь бы внеслась. Для синхронизации многопотока - на мой взгляд это раскладывание граблей. И во вторых там НЕ написано, что родительская транзакция проверит результат автономки. Я в твоем описании не заметил, что если 1-я автономка отвалится и не сможет выставить лок - то дальше процесс остановится. Поэтому на мой вкус - лучше честно-примитивно делать две транзакции. а) выставление лока, commit. Теперь приложение точно знает, что лок выставлен. Или что была ошибка. б) изменение данных и снятие лока, commit; в) и вообще, тогда ЛЮБЫЕ изменения данных, разумеется, должны делаться через локи. Не только большой-перерасчёт, но и любые. Чтобы не было картинки 1) соединение 1 хочет внести небольшие изменения в несколько строк данных по группе 1. Оно открывает транзакцию (RC? snapshot?), проверяет что лока нет (массовый перерасчет не идёт) и начинает изменять свои пару сотен строчек. 2) соединение 2 хочет начать массовый перерасчет и выставляет лок 3) соединение 1 продолжает изменять данные, потому что лок проверялся раньше и тогда его не было 4) соединение 2 тоже изменяет данные, но поскольку рассчёты большие и массивные - делает это медлено. .....а дальше кто первый закоммитится, скорее всего 1-е. ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2018, 12:23 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
AriochhvladЯ правильно тебя понял, что в твоём "асинхронном сценарии" основная и автономные тр-ции выполняются параллельно ? могут выполняться параллельно. в доках не указаны никакие гарантии синхронизацииВ документации не обязаны описывать чьи-то фантазии. Раз и навсегда - автономные тр-ции выполняются в том же логическом потоке управления, что и весь остальной код пользователя. Если вдруг когда то появится *внутренняя* параллельность при выполнения запросов, то код пользователя никак об этом не узнает. Разве что косвенно, по статистике\плану. Но с точки зрения пользователя - это один логический поток управления. Без спецэффектов. Ещё раз: тр-ция не является самостоятельным элементом выполнения кода. Это просто характеристика текущего окружения, она не может "выполнять" запросы. Это скорее контекст выполнения, а не само выполнение. PS Выдыхай уже ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2018, 13:42 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
AriochalekcvpТам же написано что автономная транзакция наследует параметры "родительской" там во-первых НЕ написано, что родительская транзакция встанет торчком и будет ждать окончания автономки. Это логически ожидаемо. Иначе можно сказать что нельзя писать хранимки с таким кодом: Код: plsql 1. 2.
Т.к. никто не гарантирует, что на момент insert'a select уже выполнится. Что звучит как полный бред. ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2018, 13:59 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
alekcvpнельзя писать хранимки, Т.к. никто не гарантирует FB documentationThe body of a PSQL module is a block of statements that run in a logical sequence, like a program. Это во первых. А во вторых, твой стейтмент выполнился - автономная транзакция запущена. Остальное - детали реализации. hvladВ документации не обязаны описывать чьи-то фантазии. да вообще никто никому ничего не обязан. и завязывать критический код на никому не обещанные детали реализации конечно же можно. а если потом реализация будет оптимизирована - "так это, пойми, потом". В общем, просто делаем на этом форуме поиск фразы "не обещал", читаем, потом думаем кто будет искать гейзенбаги в многопоточном коде лет через несколько, и выбираем. ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2018, 15:35 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
Arioch, у тебя явная неспособность оценить уровень собственных фантазмов :) ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2018, 15:44 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
Ariochalekcvpнельзя писать хранимки, Т.к. никто не гарантирует FB documentationThe body of a PSQL module is a block of statements that run in a logical sequence, like a program. Это во первых. А во вторых, твой стейтмент выполнился - автономная транзакция запущена. Остальное - детали реализации. Именно. "block of statements that run in a logical sequence". Т.е. в любой точке хранимки я могу быть уверен, что весь предыдущий код (с учётом ветвлений) уже выполнился, вне зависимости от того в автономной он транзакции или нет. Или с циклами for такая же фигня - он запустился на исполнение, а когда закончится - никто не знает, хранимка дальше пошла?.. :) ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2018, 16:02 |
|
Массовое обновление данных в базе
|
|||
---|---|---|---|
#18+
Это уже не трава пошла... ... |
|||
:
Нравится:
Не нравится:
|
|||
25.04.2018, 17:58 |
|
|
start [/forum/topic.php?fid=40&gotonew=1&tid=1561139]: |
0ms |
get settings: |
10ms |
get forum list: |
14ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
38ms |
get topic data: |
9ms |
get first new msg: |
7ms |
get forum data: |
2ms |
get page messages: |
68ms |
get tp. blocked users: |
1ms |
others: | 330ms |
total: | 487ms |
0 / 0 |