Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 Lazarus SQL многопоточность INSERT  [new]
Moneo
Member

Откуда:
Сообщений: 59
Добрый время суток. Только начал изучать многопоточность, сейчас мне нужно делать INSERT в одну таблицу большим количеством потоков, так как очень большой объем данных. Использую Lazarus, SQL server 2014, прочитал что нужно создавать для каждого потока свои компоненты подключения и прочитал вики страницу по использованию потоков по паскалю.

После этого сделал 2 потока и из метода Execute только вызываю одну и туже процедуру в которой идет обработка строк, создаются динамически компоненты подключения, в потоках указана 1 процедура в которой частями получаются данные с стороннего сервера, обрабатываются строки, происходит вставка INSERT SQL. В результате получаю ошибку RUNTIME 204, когда 1 поток, то все работает нормально. В процедуре вставки есть еще локальные строковые переменные процедуры (для будущей вставки) которые копируются из огромной строки и они в потоках не пересекаются, но SQL Insert дает сбой при 2 потоках и более. Привожу код, что мне надо сделать чтобы исправить ситуацию? Прошу ссылки на примеры использования баз данных в потоках и\или конкретные советы по задаче. Как правильно использовать переменные и компоненты в потоках - должны быть уникальные имена компонентов или 2 потока могут ссылаться на одну и туже внешнюю процедуру и данные не будут сбиваться (пример что будет если 2 потока ссылаюся на процедуру с sql_db:=tsqlquery.create(self) с разными запросами - будет ли это корректно работать? Потоки в программе только вставляют данные в одну таблицу, с интерфейсом не соприкасаются.

procedure Mythread.Execute;
begin
form1.GET_MAAINDATA('250','0','1');
end;

procedure Mythread2.Execute;
begin
form1.GET_MAAINDATA('500','250','2');
end;   

procedure TForm1.GET_MAAINDATA(limit,offset,numpotok:string);
begin    
//ПОЛУЧИЛ СОРС СТРАНИЦЫ С ИНТЕРНЕТА
temp:=PChar(HTTPs.Document.Memory);

while pos(',{"uuid"',temp)<>0 do
begin
get_main_data2(copy(temp,1,pos(',{"uuid"',temp)-1));
delete(temp,1,pos(',{"uuid"',temp)+9);
end; 

end;

function TForm1.get_main_data2(input:string):string;
var
SQL_Database:tsqlconnector;
SQL_Transaction:tsqlTransaction;
SQL_Query:tsqlquery;
SQL_Datasource:tDatasource; 
//СТРОКОВЫЕ ПЕРЕМЕННЫЕ ДЛЯ ОБРАБОТКИ
begin

SQL_Database:=tsqlconnector.Create(self);
SQL_Transaction:=tsqlTransaction.Create(self);
SQL_Datasource:=tDatasource.Create(self);
SQL_Query:=tsqlquery.Create(self);

SQL_Database.connectortype:='MSSQLServer';
SQL_Database.DatabaseName:='test';
SQL_Database.HostName:='хост';
SQL_Database.username:='пользователь';
SQL_Database.password:='пароль';
SQL_Database.Transaction:=SQL_Transaction;
SQL_Transaction.database:=SQL_Database;
SQL_Query.database:=SQL_Database;
SQL_Query.Transaction:=SQL_Transaction;
SQL_Query.PacketRecords:=-1;
SQL_Datasource.dataset:=SQL_Query;

SQL_Database.connected:=true;  

//ОБРАБОТКА СТРОК

//SQL INSERT ВСТАВКА СТРОК В ТАБЛИЦУ

end;
15 апр 19, 14:55    [21862597]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 25560
Moneo
//ПОЛУЧИЛ СОРС СТРАНИЦЫ С ИНТЕРНЕТА
temp:=PChar(HTTPs.Document.Memory);

while pos(',{"uuid"',temp)<>0 do
begin
get_main_data2(copy(temp,1,pos(',{"uuid"',temp)-1));
delete(temp,1,pos(',{"uuid"',temp)+9);
end; 

На вскидку: оба потока работают с одним участком памяти.

ЗЫ. На какой строке возникает ошибка lazarus не показывает?
15 апр 19, 15:17    [21862629]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Cobalt747
Member

Откуда:
Сообщений: 2097
Многопоточность нужна не для ускорения!
15 апр 19, 15:19    [21862636]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Moneo
Member

Откуда:
Сообщений: 59
Ошибка при открытии SQL, перед вставкой идет открытие на проверку наличия ID в таблице, если нету, то вставляется.
15 апр 19, 15:31    [21862660]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 25560
Еще и коннект на каждый чих стартует, когда достаточно при старте потока его создать, а при окончании уже уничтожить.
15 апр 19, 15:33    [21862663]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Moneo
Member

Откуда:
Сообщений: 59
Cobalt747,

ну тогда поясните почему в итоге ускоряется обработка данынх
15 апр 19, 15:40    [21862675]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Moneo
Member

Откуда:
Сообщений: 59
wadman,

в каком месте он стартует на каждый чих. Расскажите как правильно в данном случае вставку в потоках делать?
15 апр 19, 15:41    [21862677]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Moneo
Member

Откуда:
Сообщений: 59
Мой главный вопрос - как избежать написание однотипного кода при потоках и как работать с запросами к одной таблице базы данных на вставку, обновление?
15 апр 19, 15:43    [21862680]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Moneo
Member

Откуда:
Сообщений: 59
Может мне нужно создавать компоненты SQL в execute каждого потока отдельно, а потом их передать функции get_main_data2 в параметрах - это способствует разделению подключений к базе и не нужно будет передавать и обрабатывать кучу строк?
15 апр 19, 15:48    [21862689]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
goldmi45
Member

Откуда:
Сообщений: 1161
Moneo
procedure TForm1.GET_MAAINDATA(limit,offset,numpotok:string);
begin    
//ПОЛУЧИЛ СОРС СТРАНИЦЫ С ИНТЕРНЕТА
temp:=PChar(HTTPs.Document.Memory);

while pos(',{"uuid"',temp)<>0 do
begin
  get_main_data2(copy(temp,1,pos(',{"uuid"',temp)-1)); // Вот это вызывается на каждый чих, а в ней создаётся коннект и др.
  delete(temp,1,pos(',{"uuid"',temp)+9);
end; 

end;

function TForm1.get_main_data2(input:string):string;
var
SQL_Database:tsqlconnector;
SQL_Transaction:tsqlTransaction;
SQL_Query:tsqlquery;
SQL_Datasource:tDatasource; 
//СТРОКОВЫЕ ПЕРЕМЕННЫЕ ДЛЯ ОБРАБОТКИ
begin
  //... skip
end;
15 апр 19, 16:49    [21862806]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Gator
Member

Откуда: Москва
Сообщений: 14343
А я бы посоветовал посмотреть на план исполнения в MSSQL,
Профайлер что говорит?
Про блокировки таблицы, про сам запрос. (может, HINT нужно добавить?)
Разобраться, наконец, с блокировками. Transactionlog позырить...
__________
ну и запроса INSERT/UPDATE не видно пока.
15 апр 19, 18:28    [21862913]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
энди
Member

Откуда: Киров, Россия
Сообщений: 983
для заливки данных есть bcp, в круйнем случае есть специальный интерфейс для быстрой заливки данных в таблицы, на память сейчас не вспомню но посмотрите доку на TLoader у SDAC, он его использует, в доке сказано через какой интерфейс загоняет. По итогу там десятки тысяч записей в секунду получаются потому что льются сразу данные в таблицу без всяких insert
15 апр 19, 20:40    [21862992]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Gator
Member

Откуда: Москва
Сообщений: 14343
энди,

bcp Действительно хороша, но она ведь тупо игнорирует триггеры, ключи и пр. А ещё фигатень в журнале транзакций.
Но если залить таблицу(ы) с нуля, - очень впечатляет!. Зато потом надо включить триггеры, констрейнты и перестроить индексы. На этом время и схомячится.
15 апр 19, 22:16    [21863046]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Gator
Member

Откуда: Москва
Сообщений: 14343
Gator,

Например, моя Собачья База весит всего 30 ГБ, и там всего две больший таблицы Dogs & Person + линк между ними. Reindex для меня - жесть!
15 апр 19, 22:23    [21863052]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Dmitry Arefiev
Member

Откуда:
Сообщений: 9731
Array DML
15 апр 19, 22:32    [21863058]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Gator
Member

Откуда: Москва
Сообщений: 14343
Cobalt747
Многопоточность нужна не для ускорения!
А для чего ещё?
Именно для ускорения обработки одних и тех же данных. Ну или совмещения обработки разных данных
15 апр 19, 22:36    [21863062]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Gator
Member

Откуда: Москва
Сообщений: 14343
Dmitry Arefiev
Array DML
Насколько мои тупые мозги осознали, в MSSQL физически это реализуется через выборки/временные таблицы. В чём выгода?
15 апр 19, 22:45    [21863067]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Док
Member

Откуда: Казань
Сообщений: 6202
Moneo
сейчас мне нужно делать INSERT в одну таблицу большим количеством потоков, так как очень большой объем данных.

как-то странно ты потоки используешь: из execute доп.потока вызываешь процедуры основного :)
+ Может сделать примерно так?
type
  TMythread = class(TThread);
  ....
  public 
    сonstructor Create(<параметры подключения/инсерта и проч.проч.>)
  ...
  end;
procedure TMythread.Execute;
begin
//создал подключение к БД

//ПОЛУЧИЛ СОРС СТРАНИЦЫ С ИНТЕРНЕТА
... твой код

//ОБРАБОТКА СТРОК
... твой код
//SQL INSERT ВСТАВКА СТРОК В ТАБЛИЦУ
... твой код

//убил подключение к БД
end;

procedure TForm1.CreateInsertTask();
var TempThread: TMythread;
begin    
  TempThread:= TMythread.Create(<параметры подключения/инсерта и проч.проч.>);

  // после отработки доп.поток умирает или убивается вручную
end;
15 апр 19, 22:55    [21863071]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Dmitry Arefiev
Member

Откуда:
Сообщений: 9731
Gator
Dmitry Arefiev
Array DML
Насколько мои тупые мозги осознали, в MSSQL физически это реализуется через выборки/временные таблицы. В чём выгода?

В сокращении накладных расходов на один за одним вызовом. Можно и TVP - где-то быстрее (зависит, может быть и много).
BCP - наибыстрейший, но с ним не спрашивай о консистентности :)
15 апр 19, 23:24    [21863084]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Vlad F
Member

Откуда:
Сообщений: 815
Dmitry Arefiev
Array DML

Дмитрий, ты сказуемое пропустил, как обычно.))
Однако, было бы очень любопытно, если бы кто-нибудь сравнил многопоточную вставку в MS SQL
через Array DML и многопоточный же Bulk Inset посредством все того же Fire DAC.
15 апр 19, 23:56    [21863098]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Gator
Member

Откуда: Москва
Сообщений: 14343
Dmitry Arefiev,

И всё равно всё сводится к временным таблицам IMHO
Допустим, загрузка данных в таблицы. Затратность навскидку
Метод Данные Консистентность
BCP 10%90%долгий реиндекс и т.п.
TVP 99%1%индексы на пустых таблицах строятся моментально

Т.е. исходные данные надо как-то формировать заранее, а затем их писать в таблицы
16 апр 19, 00:12    [21863103]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Gator
Member

Откуда: Москва
Сообщений: 14343
Moneo, вот ещё почитай https://docs.microsoft.com/ru-ru/sql/tools/bcp-utility?view=sql-server-2017
16 апр 19, 00:39    [21863115]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
энди
Member

Откуда: Киров, Россия
Сообщений: 983
Читать сюда отсюда и до обеда :) https://docs.microsoft.com/en-us/sql/connect/oledb/ole-db-interfaces/irowsetfastload-ole-db?view=sql-server-2017
16 апр 19, 10:18    [21863338]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Moneo
Member

Откуда:
Сообщений: 59
Док,спасибо за наводку и всем остальным за ссылки. Буду пробовать
16 апр 19, 13:38    [21863675]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus SQL многопоточность INSERT  [new]
Moneo
Member

Откуда:
Сообщений: 59
Док, сделал так как вы говорили, но при получении большого числа страниц новые POST запросы валяться с ошибкой: Resultcode 0, Resultstring пусто - какая-то часть страниц получается хорошо, но большая часть сыпит ошибки, пробовал делать задержку в виде sleep, но не помогает.

В коде идет получение страниц с сервера за 120 дней, в потоке получение кода страницы и сохранение в stringlist, потом txt. Объем каждой страницы от 200кб до 8мб. Скажите есть ли вообще смысл использовать потоки для выкачивания такого объема информации, ведь на сеть ограничение 100 мегабит и ошибки почему-то сыпит.

procedure TForm1.Button2Click(Sender: TObject);
var
 MyThread : TMyThread;
 i,l:integer;
 k:tdate;
 YY,MM,DD : Word;
 day,month:string;
begin
k:=strtodate('17.04.2019');

for i:=120 downto 1 do
begin
k:=k-1;
DecodeDate(k,YY,MM,DD);
day:=inttostr(DD);
if length(day)=1 then day:='0'+day;
month:=inttostr(MM);
if length(month)=1 then month:='0'+month;

MyThread := TMyThread.Create(True,'9500','0',inttostr(i),'{"field":"dateCreatedFrom","value":"'+inttostr(YY)+'-'+month+'-'+day+'"},{"field":"dateCreatedTo","value":"'+inttostr(YY)+'-'+month+'-'+day+'"}',inttostr(YY)+'-'+month+'-'+day); // With the True parameter it doesn't start automatically
if Assigned(MyThread.FatalException) then
  raise MyThread.FatalException;
MyThread.Start;
end;
        


Код потоков:

procedure TMyThread.Execute;
var
i:integer;
temp:string;
HTTPs: THTTPSend;
Data: TStringStream;
uuid,tempout: string;
exportfull: tstringlist ;
starttime,endtime:ttime;
begin
starttime:=now;
exportfull:= tstringlist.create;
exportfull.text:='';

https:=thttpsend.Create;
HTTPs.UserAgent := 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4';
https.KeepAlive:=true;
HTTPs.Protocol:= '1.1';
HTTPs.Timeout:= 10000; // 45 seconds
HTTPs.MimeType :='application/json;charset=UTF-8';
HTTPs.Headers.Addstrings(['pwt:1937f8f982e64da2b9e0c6d5fbe889da']);

Data := TStringStream.Create('');
Data.WriteString('{"lang":"rus","apiName":"contragent","apiPath":"/contragentEk5/getFilterData","limit":'+limit+',"offset":'+offset+',"fields":['+fields+',{"field":"type","value":"ur"}],"columns":["ek4Id","country","city","name","type","subdivisionName","kindOfActivity","masterCity","note"],"sort":[]}');
HTTPs.Document.LoadFromStream(Data);
Data.Free;

if not HTTPs.HTTPMethod('POST','https://contragent.cdek.ru/api/preback')  then
    MessageDlg('POST Ошибка '+ IntToStr(HTTPs.Resultcode)+#13#10+HTTPs.Resultstring,mtError,[mbok],0) else
    temp:= PChar(HTTPs.Document.Memory);

HTTPs.free;

exportfull.text:=temp;
exportfull.SaveToFile(ExtractFilePath(Application.ExeName)+'out\'+exportname+'_ur.txt');
exportfull.free;
endtime:=now;
exportfull.free;
form1.memo1.lines.add('Complete '+numpotok+': '+timetostr(endtime-starttime)); 
17 апр 19, 16:31    [21865282]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / Delphi Ответить