@Railchik

Как заблокировать таблицу в SQL?

Собственно есть программка на С++( назовем ее create_task), которая тянет данные из таблички tasks(mssql). Эта табличка постоянно меняется. За минуту может добавиться от 10 до 100 новых записей. create_task кидает по таймауту в 5 сек запрос к табличке tasks(ищет записи с time_start = тгдд), и считав данные создает поток, в потоке меняет состояние записи в create_task(time_start = текущая дата) и выполняет некоторые вычисления, которые длятся от 2 секунд до 30 мин. По окончании вычислений удаляет запись из create_task. Все хорошо и нормально работает, однако запустив несколько копий create_task происходит дублирование, т.е. для одной и той же записи в tasks запускается по одному потоку в разных копиях create_task. Происходит это не всегда, а тогда, когда первая копия create_task считала данные,создала поток но еще не изменила статус записи, а вторая копия уже считала данные.
вот код
...
//для работы с com
	CoInitialize(NULL);

	//база данных
	ADODB::_ConnectionPtr m_conn = 0;
	ADODB::_RecordsetPtr m_rs = 0;

	//бесконечный цикл для процесс
	while (TRUE)
       {
                        HRESULT hr;
			try 
			{					
				hr = m_conn.CreateInstance(__uuidof(ADODB::Connection));
				if FAILED(hr) {
					throw _com_error(hr);
				}
				m_conn->CursorLocation = ADODB::adUseClient;	
				m_conn->Open(cons, L"", L"", ADODB::adConnectUnspecified);
				//m_conn->PutIsolationLevel( ADODB::adXactReadUncommitted );
				hr = m_rs.CreateInstance(__uuidof(ADODB::Recordset));
				if FAILED(hr) 
				{
					throw _com_error(hr);
				}
                               //char  sql_query[200] = "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE ;\0 BEGIN TRANSACTION ;\0 ";
				//m_rs->Open(sreq_param, m_conn.GetInterfacePtr(), ADODB::adOpenStatic, ADODB::adLockReadOnly, ADODB::adCmdUnspecified);

                               //запрос на получение задач на выполнение
                               char sreq_param[200] = "SELECT t.id, t.user_id, t.reports_id, r.name FROM tasks t, reports r WHERE time_start is NULL AND t.reports_id = r.id;\0";
				m_rs->Open(sreq_param, m_conn.GetInterfacePtr(), ADODB::adOpenStatic, ADODB::adLockReadOnly, ADODB::adCmdUnspecified);
				while ( !m_rs->ADOEOF)
                               {
//создаем поток и меняем значение в записи  таблицы

                               }
                              //char sql_query2[200] = "COMMIT TRANSACTION ;\0 ";
			     //m_rs->Open(sql_query2, m_conn.GetInterfacePtr(), ADODB::adOpenStatic, ADODB::adLockReadOnly, ADODB::adCmdUnspecified);
                               m_rs->Close();
				m_conn->Close();
                        }
                        catch(...)
			{
				printf("Unhandled exception...");
			};
                        Sleep(5000);

         }
...


Копал в сторону SET TRANSACTION ISOLATION LEVEL SERIALIZABLE и BEGIN TRANSACTION / COMMIT TRANSACTION Попытки использования оформлены ввиде комментария. Я не понимаю как их использовать и как они работают.
Если одна копия create_task запустит BEGIN TRANSACTION а потом вторая, как будут себя вести запросы? Какова область видимости у BEGIN TRANSACTION( если например в потоке я сделаю коннект к БД)? Возможно ли методами ADODB заблокировать таблицу?
Очень большая просьба "разжевать " вопрос. Заранее благодарю.
  • Вопрос задан
  • 4139 просмотров
Решения вопроса 2
sim3x
@sim3x
По-хорошему тебе нужна очередь.

По-плохому, сделай метки в таблице о том что происходит с тасками (в обработке, в очереди, ...)
Ответ написан
@Sumor
Прочитай про разные уровни изоляции. Например:
MSDN SQL server
MSDN ADO
Wikipedia
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@bobzer
Java EE Developer
когда первая копия create_task считала данные,создала поток но еще не изменила статус записи, а вторая копия уже считала данные
Лично я уже много лет в таких случаях использую конструкцию SELECT ... FOR UPDATE - т.е. считывание с одновременной блокировкой, которое осуществляется в пределах открытой транзакции. Первый поток считывает запись и блокирует ее на уровне СУБД. При этом любой другой поток(и), попытавшийся считать запись также с модификатором FOR UPDATE будет ожидать своей очереди до тех пор, пока первый не завершит транзакцию (изменив при этом статус обработки). Только после этого следующий поток продолжит исполнение, и завершит операцию чтения. При этом надо не забыть проверить статус - если после захвата блокировки мы видим, что статус "обработано", то текущий поток "опоздал" и должен завершиться без дальнейшей обработки записи.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы