powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Lazarus SQL многопоточность INSERT
31 сообщений из 31, показаны все 2 страниц
Lazarus SQL многопоточность INSERT
    #39801548
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый время суток. Только начал изучать многопоточность, сейчас мне нужно делать INSERT в одну таблицу большим количеством потоков, так как очень большой объем данных. Использую Lazarus, SQL server 2014, прочитал что нужно создавать для каждого потока свои компоненты подключения и прочитал вики страницу по использованию потоков по паскалю.

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

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
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;
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801558
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Moneo
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
//ПОЛУЧИЛ СОРС СТРАНИЦЫ С ИНТЕРНЕТА
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 не показывает?
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801561
Cobalt747
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Многопоточность нужна не для ускорения!
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801568
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ошибка при открытии SQL, перед вставкой идет открытие на проверку наличия ID в таблице, если нету, то вставляется.
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801569
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Еще и коннект на каждый чих стартует, когда достаточно при старте потока его создать, а при окончании уже уничтожить.
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801575
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Cobalt747,

ну тогда поясните почему в итоге ускоряется обработка данынх
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801576
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
wadman,

в каком месте он стартует на каждый чих. Расскажите как правильно в данном случае вставку в потоках делать?
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801578
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Мой главный вопрос - как избежать написание однотипного кода при потоках и как работать с запросами к одной таблице базы данных на вставку, обновление?
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801585
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Может мне нужно создавать компоненты SQL в execute каждого потока отдельно, а потом их передать функции get_main_data2 в параметрах - это способствует разделению подключений к базе и не нужно будет передавать и обрабатывать кучу строк?
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801652
goldmi45
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Moneo
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
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;
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801713
Фотография Gator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А я бы посоветовал посмотреть на план исполнения в MSSQL,
Профайлер что говорит?
Про блокировки таблицы, про сам запрос. (может, HINT нужно добавить?)
Разобраться, наконец, с блокировками. Transactionlog позырить...
__________
ну и запроса INSERT/UPDATE не видно пока.
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801760
энди
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
для заливки данных есть bcp, в круйнем случае есть специальный интерфейс для быстрой заливки данных в таблицы, на память сейчас не вспомню но посмотрите доку на TLoader у SDAC, он его использует, в доке сказано через какой интерфейс загоняет. По итогу там десятки тысяч записей в секунду получаются потому что льются сразу данные в таблицу без всяких insert
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801776
Фотография Gator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
энди,

bcp Действительно хороша, но она ведь тупо игнорирует триггеры, ключи и пр. А ещё фигатень в журнале транзакций.
Но если залить таблицу(ы) с нуля, - очень впечатляет!. Зато потом надо включить триггеры, констрейнты и перестроить индексы. На этом время и схомячится.
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801778
Фотография Gator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Gator,

Например, моя Собачья База весит всего 30 ГБ, и там всего две больший таблицы Dogs & Person + линк между ними. Reindex для меня - жесть!
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801781
Фотография Dmitry Arefiev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Array DML
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801782
Фотография Gator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cobalt747Многопоточность нужна не для ускорения!А для чего ещё?
Именно для ускорения обработки одних и тех же данных. Ну или совмещения обработки разных данных
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801786
Фотография Gator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dmitry ArefievArray DMLНасколько мои тупые мозги осознали, в MSSQL физически это реализуется через выборки/временные таблицы. В чём выгода?
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801788
Фотография Док
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Moneoсейчас мне нужно делать INSERT в одну таблицу большим количеством потоков, так как очень большой объем данных.
как-то странно ты потоки используешь: из execute доп.потока вызываешь процедуры основного :)
Может сделать примерно так?
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
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;

...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801793
Фотография Dmitry Arefiev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
GatorDmitry ArefievArray DMLНасколько мои тупые мозги осознали, в MSSQL физически это реализуется через выборки/временные таблицы. В чём выгода?
В сокращении накладных расходов на один за одним вызовом. Можно и TVP - где-то быстрее (зависит, может быть и много).
BCP - наибыстрейший, но с ним не спрашивай о консистентности :)
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801795
Vlad F
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dmitry ArefievArray DML
Дмитрий, ты сказуемое пропустил, как обычно.))
Однако, было бы очень любопытно, если бы кто-нибудь сравнил многопоточную вставку в MS SQL
через Array DML и многопоточный же Bulk Inset посредством все того же Fire DAC.
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801798
Фотография Gator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dmitry Arefiev,

И всё равно всё сводится к временным таблицам IMHO
Допустим, загрузка данных в таблицы. Затратность навскидку
Метод Данные КонсистентностьBCP 10%90%долгий реиндекс и т.п.TVP 99%1%индексы на пустых таблицах строятся моментально
Т.е. исходные данные надо как-то формировать заранее, а затем их писать в таблицы
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801806
Фотография Gator
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39801915
энди
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39802105
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Док,спасибо за наводку и всем остальным за ссылки. Буду пробовать
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39802931
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Док, сделал так как вы говорили, но при получении большого числа страниц новые POST запросы валяться с ошибкой: Resultcode 0, Resultstring пусто - какая-то часть страниц получается хорошо, но большая часть сыпит ошибки, пробовал делать задержку в виде sleep, но не помогает.

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

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
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;
        



Код потоков:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
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)); 
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39803066
Фотография Док
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Moneo,
с сетевым протоколом не работал, деталей реализации подсказать не смогу.

По коду: вот это
Код: pascal
1.
form1.memo1.lines.add('Complete '+numpotok+': '+timetostr(endtime-starttime));


я бы завернул в Queue(PostMessage) или Synchronize(SendMessage).
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39803140
goldmi45
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Moneo
Код: pascal
1.
2.
3.
4.
  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);


Зачем из потока показываете диалоговое окно?! Ошибки выводите в лог к примеру.
Ну и заворачивайте использование экземпляров в блоки try-finally. Иначе при ошибках будут утечки памяти.
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39803187
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Это все конечно хорошо, спасибо за советы, но может кто-то знает как в данном случае реализовать очередь потоков с пост запросами? Я ставлю 5 потоков по 30 раз с задержкой в 2 секунды между ними - тогда все отрабатывается на ура, но я хочу сделать это все как надо реализовав очередь и убрав не нужные простои в 2 секунды, но не знаю как. Кто подскажет?

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
for i:=30 downto 1 do
begin

for l:=0 to 4 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;
sleep(2000);

end;
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39803196
Moneo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Как только встречаются большие файлы я даже с таким количеством потоков и задержкой получаю ошибки, на один файл приходится до 8мб, но мое предположение состоит в том, что несколько потоков забивают сетевой канал и следующий пост запрос в потоке уже не может получить код страницы, так как канал сетевой забит.
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39803216
Zelius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Moneo,

забитый канал скорее приведет к ошибкам таймаута, а не просто пустой странице. сам сервер при нагрузке может тупить и возвращать что то не то.
...
Рейтинг: 0 / 0
Lazarus SQL многопоточность INSERT
    #39803535
Фотография Док
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MoneoКто подскажет?
В главном потоке любые циклы с задержкой будут приводить к "подвисаниям" UI, что воспринимается, как фризы и лаги. Отсюда не видно, по каким критериям ты контроллируешь, что ты успел выкачать на клиента удачно, а что - нет. В твоем случае я бы схематично сделал цикл внутри Execute доп.потока как-нибудь так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
var
   counter: integer;
   result: boolean;
begin
   ...
   counter:= 0;
   result:= false;
   while not result and (counter < 100) do
   begin
      try
        result:= HTTPs.HTTPMethod('POST','https://contragent.cdek.ru/api/preback');
        //возвращаем результат в файл или куда там еще...  
      except
        //пишем ошибку в лог и проч.
      end;
      
      inc(counter);
      sleep(1000); //время задержки взято от балды
   end;
   ... ///тут твой дальнейший код
end;



Повторюсь, я с синапсом никогда не работал, поэтому ничего дельного посоветовать не могу. Попробуй глянуть еще тут
...
Рейтинг: 0 / 0
31 сообщений из 31, показаны все 2 страниц
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Lazarus SQL многопоточность INSERT
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]