Расухание базы данных

IBX, FIBPlus, UIB, ADO, .Net и прочее-прочее-прочее, в общем все, что относится к созданию приложений, работающих с InterBase, Firebird и Yaffil - клиент-серверных, трехзвенных, консольных и т.п.

Модератор: kdv

hvlad
Разработчик Firebird
Сообщения: 1244
Зарегистрирован: 21 мар 2005, 10:48

Сообщение hvlad » 05 май 2007, 02:13

Попробуй сделать свип и повторить заливку. Размер БД должен остаться на месте

ivl
Сообщения: 59
Зарегистрирован: 22 мар 2006, 15:29

Сообщение ivl » 05 май 2007, 13:50

Каковы размеры входных блобов, выходных блобов и есть ли промежуточные блобы ?
Суммарный размер блобов хранимых
в таблице TASK_TB - 610200 B
ANSWER_TB - 1687275 B

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 05 май 2007, 19:15

я в такую мистику не верю, чтобы залить 5 мб, а база стала 125 метров.
Или человек не понимает, что у него в базе с триггерами творится, или делает какие-то эксперименты (туда-сюда данные гоняет), а потом удивляется, почему бд "выросла".

ivl
Сообщения: 59
Зарегистрирован: 22 мар 2006, 15:29

Сообщение ivl » 06 май 2007, 15:22

Попробуй сделать свип и повторить заливку. Размер БД должен остаться на месте
Свип сделал после первой транзакции, потом сделал вторую транзакцию. Размер базы данных, действительно, почти не увеличился.

ivl
Сообщения: 59
Зарегистрирован: 22 мар 2006, 15:29

Сообщение ivl » 06 май 2007, 15:59

я в такую мистику не верю, чтобы залить 5 мб, а база стала 125 метров.
Или человек не понимает, что у него в базе с триггерами творится, или делает какие-то эксперименты (туда-сюда данные гоняет), а потом удивляется, почему бд "выросла".
А что тут верить? Я это собственными глазами вижу. Ранее я тоже такого не видел, хотя много баз сделал.
Отключил все триггеры, оставил только на BEFORE INSERT, в них
только одна строка теперь, вида: NEW.IND=GEN_ID(MYGEN, 1), и больше ничего. Результат тот же: база пухнет!
Лично я грешить могу теперь только на свою UDF, хотя понять не могу, как это может привести к распуханию базы?
Мне нужно узнать, для чего в файле базы выделяется память во время вставки данных. Как-нибудь это можно сделать?

hvlad
Разработчик Firebird
Сообщения: 1244
Зарегистрирован: 21 мар 2005, 10:48

Сообщение hvlad » 06 май 2007, 18:16

Блобы твои её раздувают. Без тестового примера тут телепаты закончились

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 06 май 2007, 22:57

Мне нужно узнать, для чего в файле базы выделяется память во время вставки данных. Как-нибудь это можно сделать?
для данных. вставь данные без блобов, и с блобами. увидишь разницу.
А что тут верить? Я это собственными глазами вижу.
ничего ты не видишь. Допустим, в таблице 500 записей. допустим, в каждой блоб. даже если по одной странице и на запись и на блоб выделять, то никак больше 8 мегабайт такая таблица не тянет.

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

Как ты записи и блобы вставляешь - это только тебе известно.

ivl
Сообщения: 59
Зарегистрирован: 22 мар 2006, 15:29

Сообщение ivl » 14 май 2007, 17:53

Как ты записи и блобы вставляешь - это только тебе известно.
Вот моё наблюдение, может кто-нибудь его прокомментирует или даст полезную информацию.
Вставляем в представление подобного вида (дано только для справки):

Код: Выделить всё

CREATE VIEW TEST_VIEW(
    IND,
    TEST_BLOB)
AS
SELECT IND, BL_BLOB(TEST_BLOB) FROM NEW_TABLE
С триггером замещения

Код: Выделить всё

CREATE TRIGGER TEST_VIEW_BI FOR TEST_VIEW
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
  INSERT INTO NEW_TABLE 
       VALUES (NEW.IND, BL_BLOB(NEW.TEST_BLOB));
END
База пухнет!
Если в триггере и представлении функцию "BL_BLOB" убрать -- база НЕ пухнет.
Сама функция BL_BLOB тестовая (я поставил её для эксперимента вместо реальной): ничего не делает, только прогоняет данные из одного блоба в другой.
Вот она:

Код: Выделить всё

procedure bl_blob(InBlob, OutBlob: PBlob); cdecl; export;
var
  InBuffer: PChar;
  OutBuffer, OutBufferTmp: PChar;
  InBufferLength: Long;
  OutBufferLength: Long;
  PutLength: Long;
begin
  InBuffer:=nil;
  OutBuffer:=nil;
  if (not Assigned(InBlob)) or
     (not Assigned(InBlob^.BlobHandle)) or
     (InBlob^.TotalSize=0) then
  begin
    OutBlob^.TotalSize:=0;
    exit;
  end;
  BlobAsBuffer(InBlob, InBuffer, InBufferLength);
  if InBuffer<>nil then
  begin
    try
      OutBuffer:=InBuffer;
      OutBufferLength:=InBufferLength;

      OutBufferTmp:=OutBuffer;
      OutBlob^.TotalSize:=OutBufferLength;
      if (OutBuffer<>nil) then
      begin
        while OutBufferLength > 0 do
        begin
          if OutBufferLength > MaxBLObPutLength
             then PutLength := MaxBLObPutLength
             else PutLength := OutBufferLength;

          with OutBlob^ do PutSegment(BlobHandle, OutBufferTmp, PutLength);

          Dec(OutBufferLength, PutLength);
          Inc(OutBufferTmp, PutLength);
        end;
      end;
    finally
      if InBuffer<>nil then SysFreeMem(InBuffer);
    end;
  end;
end;
Замечено, что в процессе вставки в программе происходит несколько выборок (SELECT) вставляемых данных (в этой же транзакции), что и приводит к её разбуханию, но только в том случае, если используется UDF "BL_BLOB". Если же её нет, или она есть, но нет выборок в ходе вставки, то разбухание базы не происходит.

hvlad
Разработчик Firebird
Сообщения: 1244
Зарегистрирован: 21 мар 2005, 10:48

Сообщение hvlad » 14 май 2007, 18:23

ivl писал(а):Замечено, что в процессе вставки в программе происходит несколько выборок (SELECT) вставляемых данных (в этой же транзакции), что и приводит к её разбуханию, но только в том случае, если используется UDF "BL_BLOB". Если же её нет, или она есть, но нет выборок в ходе вставки, то разбухание базы не происходит.
И чего же ты хотел ? Каждый вызов твоей ф-ции создаёт временный блоб, в который ты копируешь её входной аргумент.
Удалятся они только по окончанию выборки

ivl
Сообщения: 59
Зарегистрирован: 22 мар 2006, 15:29

Сообщение ivl » 14 май 2007, 18:28

И чего же ты хотел ? Каждый вызов твоей ф-ции создаёт временный блоб, в который ты копируешь её входной аргумент.
Удалятся они только по окончанию выборки
Так то оно так, но после окончания выборки они не удаляются. Выборок несколько, и, по всей видимости, мусор накапливается.
Он не удаляется даже после окончания транзакции (без принудительного свип). Почему?

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 14 май 2007, 19:39

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

ivl
Сообщения: 59
Зарегистрирован: 22 мар 2006, 15:29

Сообщение ivl » 14 май 2007, 20:28

мда. зачем при чтении блоба его каждый раз модифицировать? отсюда действительно распухание. насчет "не удаляется" - он то наверняка удаляется, только пустое место остается, которое по каким-то причинам повторно не используется. Или ты нарыл специфический баг - менять блоб при чтении с его сохранением - это ж придумать надо....
Не совсем так. Просто при записи (выполнении INSERT) в базу, происходила выборка из этой же таблицы (всё это последовательно в одной транзакции). Собственно проблему распухания я почти решил путем почти полного отказа от выборок в процессе вставки. Но все равно, мне не даёт покоя вопрос: почему свободное место не использовалось повторно? Вот ссылка на "сфабрикованный" мной пример, приводящий к воспроизведению данной ситуации.
За пример прошу сильно не бить.
http://www.ivl.front.ru/_test.zip
Хочу предупредить, что базу данных в этом примере раздует до
1200МБ (при том, что полезной информации в ней будет всего 5,6МБ)

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 14 май 2007, 21:28

За пример прошу сильно не бить.
а следовало бы. в первую очередь за вставку в dataset в цикле.

объясни пожалуйста. зачем тебе при чтении нужно вызывать функцию, которая модифицирует блоб на диске?

по объему - см. выше. у тебя цикл идет 200 раз, и каждый раз датасет читает +1 запись и соответственно столько же раз модифицируются блобы. т.е. 1+2+3+4+5 и далее до 200 раз. Что-то я вечером слаб в математике, но это вроде бы получается в итоге 20 тысяч раз - т.е. размер блоба одной записи * 20000.
Разумеется, в куске кода "не пухнет" чтений блоба нет - только вставка 200 раз.

А свободного места в базе и нет. Потому что в коде "пухнет" нигде нет commit, соответственно никакого мусора в базе нет, а значит нет и сборки мусора, и нет освободившегося пространства.
Да и с использованием CommitRetaining (нифига не читаешь www.ibase.ru/devinfo/ibx.htm и там же ibtrans.htm и там же utl.htm) мусор как правило не собирается.

p.s. кстати, в ibx.htm и в impexp.htm русским языком написано - не используйте dataset-ы для циклической вставки записи.

ivl
Сообщения: 59
Зарегистрирован: 22 мар 2006, 15:29

Сообщение ivl » 14 май 2007, 23:45

а следовало бы. в первую очередь за вставку в dataset в цикле.
Побить побили, да только не за то, что надо.
объясни пожалуйста. зачем тебе при чтении нужно вызывать функцию, которая модифицирует блоб на диске?
В моем примере функция в представлении и триггере одна и таже -- дана только для демонстрации техники обработки данных. В реальной базе две разные функции: одна используется при вставке (в триггере замещения), другая в представлении -- выполняет обратное преобразование данных. Данные хранятся и используются в разных форматах. Так надо. Еще раз: в моем примере одна и таже функция используется для демонстрации. Просто гоняет данные, чтобы продемонстрировать проблему.
по объему - см. выше. у тебя цикл идет 200 раз, и каждый раз датасет читает +1 запись и соответственно столько же раз модифицируются блобы. т.е. 1+2+3+4+5 и далее до 200 раз. Что-то я вечером слаб в математике, но это вроде бы получается в итоге 20 тысяч раз - т.е. размер блоба одной записи * 20000.
Разумеется, в куске кода "не пухнет" чтений блоба нет - только вставка 200 раз.
Это и так понятно, мною дано для сравнения, я хотел подчеркнуть то, что писал ранее о множественных выборках.
А свободного места в базе и нет. Потому что в коде "пухнет" нигде нет commit, соответственно никакого мусора в базе нет, а значит нет и сборки мусора, и нет освободившегося пространства.
Я Commit (CommitRetaining) в том примере для удобства посадил на кнопку.
Да и с использованием CommitRetaining (нифига не читаешь www.ibase.ru/devinfo/ibx.htm и там же ibtrans.htm и там же utl.htm) мусор как правило не собирается.
Всё я читаю. И об этом читал. И вовсе здесь не тот случай. Если ставить именно Commit результат будет такой же -- база распухнет. Распухнет и в случае Rollback.
Коммит выполняется в примере с такой частотой, с которой он выполняется в реальной программе -- приблизительно 1 раз на 200 вставок. Это технологическая необходимость. Но если я поставлю его после каждой вставки, база пухнуть не перестанет.
p.s. кстати, в ibx.htm и в impexp.htm русским языком написано - не используйте dataset-ы для циклической вставки записи.
Это тут тоже не к месту. Файл этот я читал многократно. В реальной базе (не в примере) вставка происходит через хранимую процедуру.
Может тоже не лучший способ, но нам подходит.

Опять раскритиковали по мелочам :(, а по сути ничего нового -- почему же не используется пространство повторно?

У меня тут по ходу еще вопрос возник: где можно подробно почитать об архитектуре FIireBird? С подробным описанием где хранится информация на различных этапах обработки. Где хранится временная информация и т.п. Имею в виду книгу.

hvlad
Разработчик Firebird
Сообщения: 1244
Зарегистрирован: 21 мар 2005, 10:48

Сообщение hvlad » 15 май 2007, 01:04

Все блобы привязываются к тр-ции, в котором они созданы.
Если блоб не был материализован (назначен таблице), то он будет удалён при окончании тр-ции.
Ты создаёшь 20100 временных блобов по 32К каждый на первом проходе (это 628М), потом, если делаешь коммит, они освобождаются.
Вставленные блобы ты не удаляешь, поэтому на втором проходе ты читаешь 201, ... 400 блобов и создаёшь при этом 60100 временных блобов по 32К каждый (это 1884М).
И т.д.
Коммиты лишь позволяют повторно использовать место от временных блобов, созданных на предыдущем этапе, не более того

ivl
Сообщения: 59
Зарегистрирован: 22 мар 2006, 15:29

Сообщение ivl » 15 май 2007, 02:04

Коммиты лишь позволяют повторно использовать место от временных блобов, созданных на предыдущем этапе, не более того
Теперь мне стал немного более ясен механизм работы с блобами.
Честно сказать, я немного разочарован: при обработке функцией блоб поля в операторе SELECT на большом объеме выбираемой информации в одной транзакции происходит рост базы... Отсюда я делаю для себя один вывод: обработка функцией блоб поля в операторе SELECT есть зло, т.е. раздуть базу можно и ничего в неё не вставляя, только сделав большую выборку с обработкой в SELECT блобов. Ни как это ранее я не предполагал.
Теперь мне просто кажется странным почему нельзя повторно использовать временные блоб до окончания транзакции? Или просто Firebird не имеет механизма проверки используется блоб или нет.
Интересно, наблюдался ли бы такой же эффект если бы использовалось поле VARCHAR аналогичного размера?

Dimitry Sibiryakov
Заслуженный разработчик
Сообщения: 1436
Зарегистрирован: 15 сен 2005, 09:05

Сообщение Dimitry Sibiryakov » 15 май 2007, 08:49

ivl писал(а):Отсюда я делаю для себя один вывод: обработка функцией блоб поля в операторе SELECT есть зло, т.е. раздуть базу можно и ничего в неё не вставляя, только сделав большую выборку с обработкой в SELECT блобов.
Она не есть зло, просто делать ее надо правильно: с помощью блоб-фильтров, а не страшными удфками.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 15 май 2007, 10:07

ivl, ты через строку читаешь, что ли?
Тебе ж ясно сказано, что место используется повторно. А насчёт "до окончания транзакции" - откуда известно, что не обратишься к нему?
Я не увидел фразы, где бы ты утверждал, что при использовании Commit (не CommitRetaining!) и повторении операций база продолжает так же пухнуть, и мусор не собирается.
Но даже если так - то Влад тебе уже сказал, что может быть недоработка со временными блобами в старых версиях, и посоветовал проверить на 2-ке.
Вместо этого твердишь, что "придираемся к мелочам" и "ничего путного ещё не сказано".

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 15 май 2007, 10:26

Честно сказать, я немного разочарован: при обработке функцией блоб поля в операторе SELECT на большом объеме выбираемой информации в одной транзакции происходит рост базы...
мда. "разочарован". прямо анекдот получается. "я разочарован, что пихаю тучу временных блобов в базу".
Отсюда я делаю для себя один вывод: обработка функцией блоб поля в операторе SELECT есть зло, т.е. раздуть базу можно и ничего в неё не вставляя, только сделав большую выборку с обработкой в SELECT блобов. Ни как это ранее я не предполагал.
хреновый вывод. Ты в функции ПИШЕШЬ БЛОБ НА ДИСК! Или ты ожидал, что волшебным образом запись будет происходит "в никуда"?
Как это "обработка ... не вставляя..."??? Кто тебя заставляет такую обработку делать?

hvlad
Разработчик Firebird
Сообщения: 1244
Зарегистрирован: 21 мар 2005, 10:48

Сообщение hvlad » 15 май 2007, 10:58

WildSery писал(а):Я не увидел фразы, где бы ты утверждал, что при использовании Commit (не CommitRetaining!) и повторении операций база продолжает так же пухнуть, и мусор не собирается.
Это не связано с мусором. CommitRetaining здесь тоже не при чём. Он читает всю таблицу (с блобами) целиком каждый раз, когда хочет вставить новую запись - все имеющиеся блобы проходят через его ф-цию и она создаёт новые их копии. Коммит только удалит накопленные тек. тр-цией блобы, но постоянных блобов всё больше и больше.
WildSery писал(а):Но даже если так - то Влад тебе уже сказал, что может быть недоработка со временными блобами в старых версиях, и посоветовал проверить на 2-ке.
2-ка, как и ожидалось, жрёт гораздо меньше памяти во время таких издевательств

Ответить