Модераторы: Akella
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как избавиться от взаимной блокировки? вроде бы сложный вопрос 
:(
    Опции темы
ksili
Дата 11.5.2007, 09:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2069
Регистрация: 3.11.2005
Где: Красноярск

Репутация: нет
Всего: 17



Использую Firebird 2-й версии, Builder 6 и компоненты FIBPlus.
Описание проблемы:
Есть таблица T_BUFFER, используемая как FIFO-буфер заявок. В неё помещаются записи (заявки). Параллельно работает несколько потоков, каждый из которых периодически смотрит в эту таблицу и, если там есть записи, выбирает самую старую (то есть вырезает - select, потом delete), и обрабатывает её. Проблема в том, что иногда (по-моему, примерно в половине случаев) заявка читается не одним, а скажем двумя потоками. 
То есть первый делает select, и до того как сделает delete, второй поток успевает сделать select той же записи. 
Ладно бы они ещё её оба обработали и всё, но при этом эта запись не удаляется ни одним их них. Я предполагаю, что это происходит из-за взаимной блокировки. Короче они друг другу мешают её удалить. В результате все потоки читают эту запись постоянно (до бесконечности), так как она остаётся самой старой и не удаляется.

Надо сделать так, чтобы одна запись из T_BUFFER читалась только одним потоком.

Сейчас вырезание из буфера реализовано так: есть компонент TpFIBQuery в котором выполняется delete. У этого компонента есть свойство BeforeExecute, в котором выбирается та же запись, что и удаляется.
Удаляем:
Код

 BufferQuery->Transaction->StartTransaction();
 BufferQuery->SQL->Clear();
 BufferQuery->SQL->Add("delete from T_BUFFER where ID_CALL in ");
 BufferQuery->SQL->Add("(select first 1 ID_CALL from T_BUFFER");
 BufferQuery->ExecQuery();
 BufferQuery->Transaction->Commit();

Перед этим читаем в BeforeExecute():
Код

 GetOldestCall->Transaction->StartTransaction();
 GetOldestCall->SQL->Clear();
 GetOldestCall->SQL->Add("select first 1 NOMER,TIME_IN from T_BUFFER order by ID_CALL");
 GetOldestCall->ExecQuery();
 GetOldestCall->Transaction->Commit();

Я вижу два пути решения проблемы:
1) Снизить вероятность одновременного чтения сократив время выполнения связки select-delete. Вот здесь хотелось бы услышать ваши советы. 
а) Значительно ли сократится время выполнения этих двух запросов, если их оформить в одной хранимой процедуре, а не посылать из программы?
б) Тут я прочитал, что можно подготавливать запросы, чтобы при последующем их вызове серверу посылался только дескриптор запроса, а не весь запрос, что может значительно повысить быстродействие. Как это можно сделать при помощи компонентов FIBPlus?
2) Может вообще исключить возможность одновременного чтения одной записи, если использовать блокировки? Я много прочитал про транзакции в небезызвестной книге Хелен Борри, а также другие материалы в сети, но так и не понял, можно ли установить какие-то параметры транзакции, чтобы select записи блокировал эту запись (а не всю таблицу)? Если заблокировать удастся, запросы самой старой записи из других потоков просто не будут видеть эту запись и выберут самую старую из оставшихся или вообще ничего не выберут?


--------------------
Ничто так не развивает аналитическое мышление, как отладка сложной программы без возможности пошагового выполнения (с)
PM MAIL   Вверх
Akella
Дата 11.5.2007, 10:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: 20
Всего: 329



Цитата(ksili @  11.5.2007,  09:55 Найти цитируемый пост)
Проблема в том, что иногда (по-моему, примерно в половине случаев) заявка читается не одним, а скажем двумя потоками. 

предлагаю добавить в таблицу поле Editing типа Integer и хранить там 0-запись не редактируется или 1-запись редактируется. Тогда для выборки можно указывать условия типа where editing <> 1 и ненужно делать delete после select, а delete можно сделать после успешного редактирования записи из таблицы-буфера

Добавлено через 2 минуты и 59 секунд
Цитата(ksili @  11.5.2007,  09:55 Найти цитируемый пост)
Проблема в том, что иногда (по-моему, примерно в половине случаев) заявка читается не одним, а скажем двумя потоками. 

нужно попробовать сделать так, чтобы в один момент времени к таблице имел доступ только один поток. Т.е. перед select устанавливаем глобальную переменную в программе, а лучше на сервере, которая скажет остальным: "курите, парни, я работаю", а после select`а изменить значение глобальной переменной, чтобы остальные потоки значи, что можно работать и так в каждом потоке.

Добавлено через 6 минут и 4 секунды
Цитата(ksili @  11.5.2007,  09:55 Найти цитируемый пост)
 BufferQuery->SQL->Add("delete from T_BUFFER where ID_CALL in ");
 BufferQuery->SQL->Add("(select first 1 ID_CALL from T_BUFFER");

разве так можно?

Добавлено через 8 минут и 2 секунды
Цитата(ksili @  11.5.2007,  09:55 Найти цитируемый пост)
 Снизить вероятность одновременного чтения сократив время выполнения связки select-delete. 

используй хранимую процедуру
PM MAIL   Вверх
ksili
Дата 11.5.2007, 10:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2069
Регистрация: 3.11.2005
Где: Красноярск

Репутация: нет
Всего: 17



Цитата(Akella @  11.5.2007,  15:35 Найти цитируемый пост)
Цитата(ksili @  11.5.2007,  09:55 )
 BufferQuery->SQL->Add("delete from T_BUFFER where ID_CALL in ");
 BufferQuery->SQL->Add("(select first 1 ID_CALL from T_BUFFER");


разве так можно? 


Так это же ты меня научил! smile)
http://forum.vingrad.ru/index.php?showtopi...t&p=1062480


--------------------
Ничто так не развивает аналитическое мышление, как отладка сложной программы без возможности пошагового выполнения (с)
PM MAIL   Вверх
Akella
Дата 11.5.2007, 10:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: 20
Всего: 329



Нужно учесть тот факт, что запись может остаться необработанной, т.к. ты её удаляешь из таблицы,
смотри:
1. Select
2. Delete
3. Исключение...  smile

Добавлено через 29 секунд
Цитата(ksili @  11.5.2007,  10:47 Найти цитируемый пост)
Так это же ты меня научил! smile)

ааа, я не увидил IN  smile

Добавлено через 4 минуты и 28 секунд
попробуй вместо IN использовать EXISTS, т.е. запрос переделай на использование EXISTS - серверу немного, но полегче будет
PM MAIL   Вверх
ksili
Дата 11.5.2007, 10:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2069
Регистрация: 3.11.2005
Где: Красноярск

Репутация: нет
Всего: 17



Ок. Попробую сделать хранимку. 
Идея с глобальной переменной на сервере тоже понравилась. Чтобы её сделать, надо ли создавать отдельную таблицу, поле которой будет этой самой переменной? Или уже есть какие-то служебные таблицы которые можно использовать?


--------------------
Ничто так не развивает аналитическое мышление, как отладка сложной программы без возможности пошагового выполнения (с)
PM MAIL   Вверх
Akella
Дата 11.5.2007, 11:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: 20
Всего: 329



глобальные переменные

http://forum.vingrad.ru/index.php?showtopi...t&p=1024894
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Interbase"
Alex

Обязательно указание:

1. Версию InterBase (Firebird, Yaffil)

2. Способа доступа (ADO, BDE, IBX и т.д.)

  • КАК ПРАВИЛЬНО ОФОРМИТЬ КОД - ЗДЕСЬ
  • КАК ПРАВИЛЬНО УКАЗАТЬ ТЕКСТ ОШИБКИ - ЗДЕСЬ
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • FAQ раздела лежит здесь!

Если Вам понравилась атмосфера форума, заходите к нам чаще! С Уважением, Akella.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Firebird, Interbase | Следующая тема »


 




[ Время генерации скрипта: 0.0472 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.