Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Базы";
Текущий архив: 2009.07.05;
Скачать: [xml.tar.bz2];

Вниз

Утечка памяти при работе с TIbDataSet   Найти похожие ветки 

 
DelphiN!   (2008-10-06 16:09) [0]

Доброго времени суток!
Написал следующее:

     for j := 0 to Res.Count-1 do
     begin
       ServerFT.SelectSQL.Text := "INSERT INTO SERVERFT (FILENAME,FILESIZE,FILECRC,BASEDIR) VALUES("""+Res.FileName[j]+""","+
         IntToStr(Res.FileSize[j])+","+IntToStr(Res.FileCRC[j])+", """+Res.BaseDir[j]+""")";
       ServerFT.ExecSQL;
       ServerFT.Transaction.Commit;
     end;


В массиве Res более 50 000 записей, при дохождении цикла до 5080ой записи программа вываливается с ошибкой о нехватке памяти. Куда девается память? Что я делаю неправильно?


 
Сергей М. ©   (2008-10-06 16:51) [1]


> SelectSQL.Text := "INSERT


Ты , наверно, заболел ?)


 
Виталий Панасенко   (2008-10-06 17:16) [2]

При чем, серьёзно заболел...


 
clickmaker ©   (2008-10-06 17:31) [3]

а не проще ли завести параметры, чем каждый раз строку записывать?


 
Виталий Панасенко   (2008-10-06 17:43) [4]


> clickmaker ©   (06.10.08 17:31) [3]

А смысл?После заполнения ВСЕХ сиквелов, IBDataSet ведет себя как обычный DataSet: Insert/Post/Edit/Delete


 
Johnmen ©   (2008-10-06 17:47) [5]


> clickmaker ©   (06.10.08 17:31) [3]
> а не проще ли завести параметры, чем каждый раз строку записывать?

Более того - именно параметрические запросы и должны быть у данного компонента!


 
clickmaker ©   (2008-10-06 17:48) [6]

> А смысл?

в некоторых случаях сиквел составит более оптимальный план запроса.
В данном случае, правда, это не очень актуально.


 
Правильный$Вася   (2008-10-06 18:02) [7]


> for j := 0 to Res.Count-1 do      begin ...
>    ServerFT.Transaction.Commit;

50 тыс коммитов? ну-ну


 
kaif ©   (2008-10-06 18:16) [8]

А этот код работает?
Предлагаю для начала переписать его хотя бы в таком виде:

with TIBSQL.Create(nil) do
try
 Transaction := <компонент транзакции>;

 if not Transaction.InTransaction then
   Transaction.StartTransaction;

 SQL.Text := "INSERT INTO SERVERFT (FILENAME,FILESIZE,FILECRC,BASEDIR)
   VALUES(:FILENAME,:FILESIZE,:FILECRC,:BASEDIR)";

 for j := 0 to Res.Count-1 do
 begin
      Params[0].AsString := Res.Filename[j];
      Params[1].AsInteger := Res.Res.FileSize[j];
      Params[2].AsInteger := Res.FileCRC[j];
      Params[3].AsString := Res.BaseDir[j];
      ExecQuery;
 end;

 Transaction.Commit;
finally
 Free;
end;


 
sniknik ©   (2008-10-06 19:55) [9]

> хотя бы в таком виде:
предлагаю добавить/заменить
>     ExecQuery;
> end;

> Transaction.Commit;

на
...
      ExecQuery;
      if j mod 5000 = 0 then begin
        Transaction.Commit;
        Transaction.StartTransaction;
      end;
 end;

 if Transaction.InTransaction then
   Transaction.Commit;
...


 
MsGuns ©   (2008-10-06 21:02) [10]

Не вижу великого смысла в подтверждении КАЖДОЙ вставки.
Ну а кроме этого, зачем всякий раз завершать транзакцию и затем неявно вновь ее стартовать ?


 
MsGuns ©   (2008-10-06 21:10) [11]

И вообще.
В подобных случаях (большие объемы вставок-модификаций, фрмируемые на клиенте) лучше все-таки следовать "пакетной" технологии:

- Создается временная таблица на сервере
- Туда заносятся все записи пакета
- Запускается от 1 до 3 запросов на вставку, удаление, изменение.

Результат:
а) Сервер не "напрягается" на длительное время обслуживанием одного клиента и не "блокирует" таблицы (в смысле не держит длительное время версии кучи записей)
б) Удобно управлять транзакциями, не "карауля" каждый пук с клиента, а ловя сбой лишь в момент ошибки, если она возникает
в) Просто намного прозрачнее с т.зр.логики приложения

>kaif ©   (06.10.08 18:16) [8]

Настоящие пачаны на обработку ошибки не заморачиваются ?


 
Johnmen ©   (2008-10-06 21:28) [12]


> MsGuns ©   (06.10.08 21:02) [10]
> Не вижу великого смысла в подтверждении КАЖДОЙ вставки.

Не каждая, а каждые 5000. В соответствии с рекомендациями лучших ИБ/ФБ"водов надо каждые ~500. Что и есть "пакет". Без неуместной вр.таблицы.
Остальные "результаты" а) б) в) как минимум странны...


 
Игорь Шевченко ©   (2008-10-06 22:19) [13]

можно дураку объяснить - какой смысл в commit-е порциями (ну скажем, по 500-1000 штук) ?


 
MsGuns ©   (2008-10-07 00:50) [14]

>Johnmen ©   (06.10.08 21:28) [12]
>Не каждая, а каждые 5000. В соответствии с рекомендациями лучших >ИБ/ФБ"водов надо каждые ~500.

>Игорь Шевченко ©   (06.10.08 22:19) [13]
>можно дураку объяснить - какой смысл в commit-е порциями (ну скажем, >по 500-1000 штук) ?

И мне заодно тоже


 
DelphiN!   (2008-10-07 06:28) [15]

Попробовал выше описаный вариант

     with TIBSQL.Create(nil) do
     try
       Transaction := IBDatabase1.DefaultTransaction;

       if not Transaction.InTransaction then
         Transaction.StartTransaction;

       SQL.Text := "INSERT INTO SERVERFT (FILENAME,FILESIZE,FILECRC,BASEDIR) "+
         "VALUES(:FILENAME,:FILESIZE,:FILECRC,:BASEDIR)";

       for j := 0 to Res.Count-1 do
       begin
         labState.Caption := "Статус : Обновление файловой таблицы сервера [Импорт файлов("+IntToStr(j)+"/"+IntToStr(Res.Count)+")] ... ";
         Application.ProcessMessages;
         Params[0].AsString := Res.Filename[j];
         Params[1].AsInteger := Res.FileSize[j];
         Params[2].AsInteger := Res.FileCRC[j];
         Params[3].AsString := Res.BaseDir[j];
         ExecQuery;
         if j mod 5000 = 0 then
         begin
           Transaction.Commit;
           Transaction.StartTransaction;
         end;
       end;

       if Transaction.InTransaction then
         Transaction.Commit;
     finally
       Free;
     end;
   end;

Всеравно утечка, только уже на 35011 записи память кончается :)


 
Сергей М. ©   (2008-10-07 08:31) [16]


> DelphiN!   (07.10.08 06:28) [15]


Укажи конкретную строчку кода из [15], при выполнении которой возбуждается исключение.
Приведи дословно текст сообщения об исключении.


 
MsGuns ©   (2008-10-07 09:14) [17]

Еще раз (настойчиво):

зачем всякий раз завершать транзакцию и затем вновь ее стартовать ?

И еще вопрос - сервер где запущен - на твоем компе ?


 
Виталий Панасенко   (2008-10-07 09:27) [18]


> DelphiN!   (07.10.08 06:28) [15]

Может, IBX надо проапдейтить?


 
jack128_   (2008-10-07 10:12) [19]


> можно дураку объяснить - какой смысл в commit-е порциями
> (ну скажем, по 500-1000 штук) ?

быстрее вставка происходить будет.


 
MsGuns ©   (2008-10-07 10:26) [20]

>jack128_   (07.10.08 10:12) [19
>быстрее вставка происходить будет.

и фиг с ней, с целостностью ?


 
Johnmen ©   (2008-10-07 10:27) [21]


> Игорь Шевченко ©   (06.10.08 22:19) [13]
> можно дураку объяснить - какой смысл в commit-е порциями
> (ну скажем, по 500-1000 штук) ?

Смысл - не накапливать версии записей, ускорение. Почитать, как обычно, можно на ibase.ru

> MsGuns ©   (07.10.08 00:50) [14]
> И мне заодно тоже

В чем сомнения?

> DelphiN!

Рекомендуется к прочтению http://www.ibase.ru/devinfo/ibx.htm


 
jack128_   (2008-10-07 10:50) [22]


> и фиг с ней, с целостностью ?

а ты уверен, что вставка 500 записей нарушит целостность?


 
Игорь Шевченко ©   (2008-10-07 10:53) [23]

Johnmen ©   (07.10.08 10:27) [21]


> Смысл - не накапливать версии записей, ускорение. Почитать,
>  как обычно, можно на ibase.ru


Тогда чем плохо коммитить по одной ? Вопрос был о том, зачем именно пакет организовывать


 
DelphiN!   (2008-10-07 14:33) [24]

Проблему решил обновлением компонентов IBX. Стояла старая, глючная версия ..
Всем большое спасибо за помощь!


 
MsGuns ©   (2008-10-07 16:20) [25]

>jack128_   (07.10.08 10:50) [22]
>а ты уверен, что вставка 500 записей нарушит целостность?

А ты уверен, что НЕ нарушит ?

А вообще корень проблемы раскрыт в [23]


 
DrPass ©   (2008-10-07 16:41) [26]


> Игорь Шевченко ©   (07.10.08 10:53) [23]
> Johnmen ©   (07.10.08 10:27) [21]
>
> Тогда чем плохо коммитить по одной ? Вопрос был о том, зачем
> именно пакет организовывать

Потому что коммит после каждой записи также заметно замедляет работу. Оптимальный по производительности способ вставки больших массивов в IB - именно пакетами (количество записей в пакете зависит от многих параметров)


 
Игорь Шевченко ©   (2008-10-07 16:44) [27]

DrPass ©   (07.10.08 16:41) [26]


> Потому что коммит после каждой записи также заметно замедляет
> работу. Оптимальный по производительности способ вставки
> больших массивов в IB - именно пакетами (количество записей
> в пакете зависит от многих параметров)


ну понятно - black voodoo. Желающие подкручивать гайки без хлеба не останутся. Но все-таки - насколько мне известно, IB не запускает сборку мусора после каждого commit, версии все равно зависят от количества одновременно стартовавших транзакций - какая физика механизма, что при транзакции "пакетом" быстродействие выше ? Если не затруднит, то ссылочку с ibase.ru


 
DrPass ©   (2008-10-07 17:28) [28]


> IB не запускает сборку мусора после каждого commit,

Причем здесь вообще сборка мусора, если речь идет о вставке (а не удалении, обновлении и выборке) записей?
Игорь, скажите, что эффективнее будет работать - пятьсот раз переписать страницу данных и один раз - страницу состояний транзакций в конце всего этого, или пятьсот раз переписать страницу данных и пятьсот раз страницу состояний транзацкий после каждой вставки? Имейте в виду, сервер IB обычно работает в режиме прямой записи на диск, без кеширования!
Никакой черной магии, элементарная логика, подтвержденная опытом работы с сервером. Я понимаю, вы любитель поумничать. Но зачем умничать в тех областях, в которых вы слабо разбираетесь-то?


 
MsGuns ©   (2008-10-07 17:32) [29]

> количество записей в пакете зависит от многих параметров

Ерунда, это зависит только от дурости программиста.
При работе с многострочным документом используется статус документа, коммитится же КАЖДАЯ введенная (исправленная) запись,
при всевозможных "заливках" ничего лучше раскритикованного джонменом способа с временной таблицей нет
во всех остальных случаях вполне достаточно "чистого" сиквеля

Все остальное - от лукавого


 
Правильный$Вася   (2008-10-07 17:34) [30]


>  сервер IB обычно работает в режиме прямой записи на диск,
>  без кеширования!

да ну
по умолчанию как раз наоборот
а вот если принудительно выставить ForceWrite, тогда да, это это для параноиков и на особо ответственных базах
но массовая загрузка данных всегда отличалась от обычной работы, причем независимо от субд


 
Правильный$Вася   (2008-10-07 17:36) [31]


> MsGuns ©   (07.10.08 17:32) [29]

холиваров не надо, каждый свое мнение может иметь, основанное на опыте


 
MsGuns ©   (2008-10-07 17:39) [32]

>DrPass ©   (07.10.08 17:28) [28]
>Никакой черной магии, элементарная логика, подтвержденная опытом работы с сервером. Я >понимаю, вы любитель поумничать. Но зачем умничать в тех областях, в которых вы слабо >разбираетесь-то?

Давай без зеленых человечеков и словокатанием в духе "бывают случаи"
Приведи практический пример, в котором приведенных в [29] вариантов не хватает для решения задачи. Только не надо мне рассказывать, что сервер на временную таблицу с последующим ЕДИНСТВЕННЫМ insert to ОСНОВНАЯ ТАБЛИЦА from select будет заморачиваться круче, чем "держать" версии стотыщмильонов записей, агрументируя аргументов, что он де работает без кеширования (причем тут это вообще непонятно)

А то мы так до третьих петухов будем "умничать"


 
Игорь Шевченко ©   (2008-10-07 17:49) [33]

DrPass ©   (07.10.08 17:28) [28]


> Никакой черной магии, элементарная логика, подтвержденная
> опытом работы с сервером. Я понимаю, вы любитель поумничать.
>  Но зачем умничать в тех областях, в которых вы слабо разбираетесь-
> то?


То есть, ссылочки на ibase.ru не будет, я верно понимаю ?


 
kaif ©   (2008-10-07 18:13) [34]

2 MsGuns ©  

Временные таблицы - это стиль MSSQL,  а не IB.

Насчет оптималности подтверждения после каждых 5тыс. мог бы поспорить, так как конркетно для IB все равно, коммитить после каждых 5000 или после каждых 50 тыс. Почему, например, не после каждых 500?

Насчет обработки ошибок согласен, я не показал этого в коде. Сознательно посчитал это излишним. Как, впрочем, и коммит после каждых 5000. Так как правильная обработка ошибок от задачи зависит. Например, человека может быть устроит вообще наглухо подавить все ошибки, нарушения уникального ключа (unique key violation) и при этом одновременно ни в коем случае не подавлять ошибки, связанные с обрезанием строки (в данном случае у него имя файла) по длине (string truncation). А так как у него (как он сам сказал) всего 50 тыс записей, я и посчитал излишним и подтверждение после "каждых стольких-то". Так как 50 тыс для IB - цифра, допускающая откатить всю вставку, а логика обработки ошибок в его случае может требовать такого отката (например, если в условии задачи - не потерять ни один файл при импорте, если что-то не так - отменить весь импорт, откатив транзакцию, разобраться и попробовать импорт вновь).

Могу добавить еще одну вещь. Если уж гнаться за совершенством, то тот код, что я написал, возможно вообще неоптимален.

Оптимальный и легко в дальнейшем развиваемый код я бы оформил так:

var
 FilenameParam,
 FielSizeParam,
 FileCRCParam,
 BaseDirParam : TParam;

with TIBSQL.Create(nil) do
try
Transaction := <компонент транзакции>;

if not Transaction.InTransaction then
  Transaction.StartTransaction;

SQL.Text := "INSERT INTO SERVERFT (FILENAME,FILESIZE,FILECRC,BASEDIR)
  VALUES(:FILENAME,:FILESIZE,:FILECRC,:BASEDIR)";

FilenameParam := ParamByName("FILENAME");
FielSizeParam := ParamByName("FILESIZE);
FileCRCParam := ParamByName("FILECRC");
BaseDirParam := ParamByName("BASEDIR");

for j := 0 to Res.Count-1 do
begin
     FilenameParam.AsString := Res.Filename[j];
     FielSizeParam.AsInteger := Res.Res.FileSize[j];
     FileCRCParam.AsInteger := Res.FileCRC[j];
     BaseDirParam.AsString := Res.BaseDir[j];
     ExecQuery;

  и т.д.
 


 
DrPass ©   (2008-10-07 18:26) [35]


> kaif ©   (07.10.08 18:13) [34]


> Насчет оптималности подтверждения после каждых 5тыс. мог
> бы поспорить, так как конркетно для IB все равно, коммитить
> после каждых 5000 или после каждых 50 тыс. Почему, например,
>  не после каждых 500?

Никто и не говорит про 5 тыс. Вполне вероятно, что лучшим будет вариант одного-единственного коммита в конце всего пакета. Лишь бы не после каждой записи


> MsGuns ©   (07.10.08 17:39) [32]


> Приведи практический пример, в котором приведенных в [29]
> вариантов не хватает для решения задачи

Мы говорим о "возможности решения задачи" или об ее оптимальном решении? Чтобы просто вставлять записи в БД можно вообще не заморачиваться. Тем более что предложенные варианты никаких преимуществ не имеют. Например, по поводу редактирования многострочного документа коммит каждой строки вообще неудобная крайность. Временные таблицы - тоже неправильный подход. Если надо просто залить данные в плоскую таблицу - зачем делать промежуточное хранилище? Что мы этим выиграем?

> Игорь Шевченко ©   (07.10.08 17:49) [33]


> То есть, ссылочки на ibase.ru не будет, я верно понимаю
> ?

Не будет. А вы очевидных решений без ссылочек не принимаете? Жаль. На все случаи в жизни ссылочек не напасешься...


 
Игорь Шевченко ©   (2008-10-07 18:37) [36]

DrPass ©   (07.10.08 18:26) [35]


> Не будет.


Тогда нефиг "
> Я понимаю, вы любитель поумничать. Но зачем умничать в тех
> областях, в которых вы слабо разбираетесь-то?

"

в IB я чуть-чуть разбираюсь. Это так, к слову


 
Правильный$Вася   (2008-10-07 18:50) [37]


> То есть, ссылочки на ibase.ru не будет, я верно понимаю

http://www.ibase.ru/devinfo/dontdoit.htm
пункты 7 и 8


 
DrPass ©   (2008-10-07 19:01) [38]


> Игорь Шевченко ©   (07.10.08 18:37) [36]
> DrPass ©   (07.10.08 18:26) [35]
>
> Тогда нефиг "
>
> в IB я чуть-чуть разбираюсь. Это так, к слову

Очень хорошо. Практика - критерий истины. Согласны? Сделайте так:
1. Табличку в базе (у меня FB 2.1, но думаю, это не принципиально)
create table test(a int, b int)

2. Простенькое приложение - две кнопки, мемо, ibdatabase, ibtransaction, ibsql
Запрос такой:
insert into test(a, b) values (:a, :b)

3. Ну и соответственно код:
procedure TForm1.Button1Click(Sender: TObject);
var
 i: integer;
 a, b: cardinal;
begin
 a:= GetTickCount;
 IBTransaction1.StartTransaction;
 for i := 0 to 10000 - 1 do
 begin
   IBSQL1.ParamByName("a").Value:= random(1000);
   IBSQL1.ParamByName("b").Value:= random(1000);
   IBSQL1.ExecQuery;
 end;
 IBTransaction1.Commit;
 b:= GetTickCount;
 Memo1.Lines.Add(inttostr(b - a));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
 i: integer;
 a, b: cardinal;
begin
 a:= GetTickCount;
 for i := 0 to 10000 - 1 do
 begin
   IBTransaction1.StartTransaction;
   IBSQL1.ParamByName("a").Value:= random(1000);
   IBSQL1.ParamByName("b").Value:= random(1000);
   IBSQL1.ExecQuery;
   IBTransaction1.Commit;
 end;
 b:= GetTickCount;
 Memo1.Lines.Add(inttostr(b - a));
end;


У меня в первом случае (запускал по пять раз):
422
453
438
422
421

Во втором:
9719
9797
9641
9891
10171

Как по-вашему, есть разница - делать коммит после каждой записи, или нет?


 
Johnmen ©   (2008-10-07 20:02) [39]


> DrPass ©   (07.10.08 19:01) [38]

Да не надо ему ничего объяснять. Если бы ему действительно было интересно, он бы давно набрал "массовая вставка" в поиске на ibase.ru и изучил бы тему...
А на самом деле Игорь занимается любимым делом - занудством :)
Конечно же позанудствовать можно и даже бывает нужно. Но чувство меры не должно отказывать...


 
Игорь Шевченко ©   (2008-10-07 20:03) [40]

DrPass ©   (07.10.08 19:01) [38]


> Очень хорошо. Практика - критерий истины. Согласны?


Нет, не согласен. Критерий истинности - всегда задача. Я не зря спросил именно про искусственное разделение всего объема на порции по N операторов. Допустим, у меня есть задача вставить, скажем, 20000 записей.
В зависимости от того, что надо сделать, если 15732-ая не вставилась, я и буду строить логику - если требуется откатить все изменения, то и транзакция будет одна.

Правильный$Вася   (07.10.08 18:50) [37]

Ну так по сылке говорят "не делайте". А почему - не говорят. Меня интересовала ссылка на "почему". Например, тот же Кайт, рассказывая про оракл, честно говорит, почему невыгодно при массовых операциях делать commit после каждого оператора. Но точно также и говорит, что искусственно разбивать большое обновление на кучу мелких транзакций еще более невыгодно.



Страницы: 1 2 вся ветка

Форум: "Базы";
Текущий архив: 2009.07.05;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.58 MB
Время: 0.005 c
15-1240818026
12
2009-04-27 11:40
2009.07.05
tsql, sql express. Как ускорить этот запрос.


15-1240884969
dmk
2009-04-28 06:16
2009.07.05
PHP и параметры строки


8-1194953765
Lebedev
2007-11-13 14:36
2009.07.05
Проблема с MediaPlayer.


15-1241075071
ford
2009-04-30 11:04
2009.07.05
помогите найти литературу в сети


15-1241762623
oldman
2009-05-08 10:03
2009.07.05
Случайно нашел сайт. Что это такое?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский