Расухание базы данных
Модератор: kdv
А что тут верить? Я это собственными глазами вижу. Ранее я тоже такого не видел, хотя много баз сделал.я в такую мистику не верю, чтобы залить 5 мб, а база стала 125 метров.
Или человек не понимает, что у него в базе с триггерами творится, или делает какие-то эксперименты (туда-сюда данные гоняет), а потом удивляется, почему бд "выросла".
Отключил все триггеры, оставил только на BEFORE INSERT, в них
только одна строка теперь, вида: NEW.IND=GEN_ID(MYGEN, 1), и больше ничего. Результат тот же: база пухнет!
Лично я грешить могу теперь только на свою UDF, хотя понять не могу, как это может привести к распуханию базы?
Мне нужно узнать, для чего в файле базы выделяется память во время вставки данных. Как-нибудь это можно сделать?
для данных. вставь данные без блобов, и с блобами. увидишь разницу.Мне нужно узнать, для чего в файле базы выделяется память во время вставки данных. Как-нибудь это можно сделать?
ничего ты не видишь. Допустим, в таблице 500 записей. допустим, в каждой блоб. даже если по одной странице и на запись и на блоб выделять, то никак больше 8 мегабайт такая таблица не тянет.А что тут верить? Я это собственными глазами вижу.
если ты привел действительно статистику от той базы в 125 мегабайт, то как ни крути, у тебя получается примерно 100мб блобов.
Как ты записи и блобы вставляешь - это только тебе известно.
Вот моё наблюдение, может кто-нибудь его прокомментирует или даст полезную информацию.Как ты записи и блобы вставляешь - это только тебе известно.
Вставляем в представление подобного вида (дано только для справки):
Код: Выделить всё
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;
И чего же ты хотел ? Каждый вызов твоей ф-ции создаёт временный блоб, в который ты копируешь её входной аргумент.ivl писал(а):Замечено, что в процессе вставки в программе происходит несколько выборок (SELECT) вставляемых данных (в этой же транзакции), что и приводит к её разбуханию, но только в том случае, если используется UDF "BL_BLOB". Если же её нет, или она есть, но нет выборок в ходе вставки, то разбухание базы не происходит.
Удалятся они только по окончанию выборки
Так то оно так, но после окончания выборки они не удаляются. Выборок несколько, и, по всей видимости, мусор накапливается.И чего же ты хотел ? Каждый вызов твоей ф-ции создаёт временный блоб, в который ты копируешь её входной аргумент.
Удалятся они только по окончанию выборки
Он не удаляется даже после окончания транзакции (без принудительного свип). Почему?
мда. зачем при чтении блоба его каждый раз модифицировать? отсюда действительно распухание. насчет "не удаляется" - он то наверняка удаляется, только пустое место остается, которое по каким-то причинам повторно не используется. Или ты нарыл специфический баг - менять блоб при чтении с его сохранением - это ж придумать надо....
Не совсем так. Просто при записи (выполнении INSERT) в базу, происходила выборка из этой же таблицы (всё это последовательно в одной транзакции). Собственно проблему распухания я почти решил путем почти полного отказа от выборок в процессе вставки. Но все равно, мне не даёт покоя вопрос: почему свободное место не использовалось повторно? Вот ссылка на "сфабрикованный" мной пример, приводящий к воспроизведению данной ситуации.мда. зачем при чтении блоба его каждый раз модифицировать? отсюда действительно распухание. насчет "не удаляется" - он то наверняка удаляется, только пустое место остается, которое по каким-то причинам повторно не используется. Или ты нарыл специфический баг - менять блоб при чтении с его сохранением - это ж придумать надо....
За пример прошу сильно не бить.
http://www.ivl.front.ru/_test.zip
Хочу предупредить, что базу данных в этом примере раздует до
1200МБ (при том, что полезной информации в ней будет всего 5,6МБ)
а следовало бы. в первую очередь за вставку в 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-ы для циклической вставки записи.
Побить побили, да только не за то, что надо.а следовало бы. в первую очередь за вставку в dataset в цикле.
В моем примере функция в представлении и триггере одна и таже -- дана только для демонстрации техники обработки данных. В реальной базе две разные функции: одна используется при вставке (в триггере замещения), другая в представлении -- выполняет обратное преобразование данных. Данные хранятся и используются в разных форматах. Так надо. Еще раз: в моем примере одна и таже функция используется для демонстрации. Просто гоняет данные, чтобы продемонстрировать проблему.объясни пожалуйста. зачем тебе при чтении нужно вызывать функцию, которая модифицирует блоб на диске?
Это и так понятно, мною дано для сравнения, я хотел подчеркнуть то, что писал ранее о множественных выборках.по объему - см. выше. у тебя цикл идет 200 раз, и каждый раз датасет читает +1 запись и соответственно столько же раз модифицируются блобы. т.е. 1+2+3+4+5 и далее до 200 раз. Что-то я вечером слаб в математике, но это вроде бы получается в итоге 20 тысяч раз - т.е. размер блоба одной записи * 20000.
Разумеется, в куске кода "не пухнет" чтений блоба нет - только вставка 200 раз.
Я Commit (CommitRetaining) в том примере для удобства посадил на кнопку.А свободного места в базе и нет. Потому что в коде "пухнет" нигде нет commit, соответственно никакого мусора в базе нет, а значит нет и сборки мусора, и нет освободившегося пространства.
Всё я читаю. И об этом читал. И вовсе здесь не тот случай. Если ставить именно Commit результат будет такой же -- база распухнет. Распухнет и в случае Rollback.Да и с использованием CommitRetaining (нифига не читаешь www.ibase.ru/devinfo/ibx.htm и там же ibtrans.htm и там же utl.htm) мусор как правило не собирается.
Коммит выполняется в примере с такой частотой, с которой он выполняется в реальной программе -- приблизительно 1 раз на 200 вставок. Это технологическая необходимость. Но если я поставлю его после каждой вставки, база пухнуть не перестанет.
Это тут тоже не к месту. Файл этот я читал многократно. В реальной базе (не в примере) вставка происходит через хранимую процедуру.p.s. кстати, в ibx.htm и в impexp.htm русским языком написано - не используйте dataset-ы для циклической вставки записи.
Может тоже не лучший способ, но нам подходит.
Опять раскритиковали по мелочам , а по сути ничего нового -- почему же не используется пространство повторно?
У меня тут по ходу еще вопрос возник: где можно подробно почитать об архитектуре FIireBird? С подробным описанием где хранится информация на различных этапах обработки. Где хранится временная информация и т.п. Имею в виду книгу.
Все блобы привязываются к тр-ции, в котором они созданы.
Если блоб не был материализован (назначен таблице), то он будет удалён при окончании тр-ции.
Ты создаёшь 20100 временных блобов по 32К каждый на первом проходе (это 628М), потом, если делаешь коммит, они освобождаются.
Вставленные блобы ты не удаляешь, поэтому на втором проходе ты читаешь 201, ... 400 блобов и создаёшь при этом 60100 временных блобов по 32К каждый (это 1884М).
И т.д.
Коммиты лишь позволяют повторно использовать место от временных блобов, созданных на предыдущем этапе, не более того
Если блоб не был материализован (назначен таблице), то он будет удалён при окончании тр-ции.
Ты создаёшь 20100 временных блобов по 32К каждый на первом проходе (это 628М), потом, если делаешь коммит, они освобождаются.
Вставленные блобы ты не удаляешь, поэтому на втором проходе ты читаешь 201, ... 400 блобов и создаёшь при этом 60100 временных блобов по 32К каждый (это 1884М).
И т.д.
Коммиты лишь позволяют повторно использовать место от временных блобов, созданных на предыдущем этапе, не более того
Теперь мне стал немного более ясен механизм работы с блобами.Коммиты лишь позволяют повторно использовать место от временных блобов, созданных на предыдущем этапе, не более того
Честно сказать, я немного разочарован: при обработке функцией блоб поля в операторе SELECT на большом объеме выбираемой информации в одной транзакции происходит рост базы... Отсюда я делаю для себя один вывод: обработка функцией блоб поля в операторе SELECT есть зло, т.е. раздуть базу можно и ничего в неё не вставляя, только сделав большую выборку с обработкой в SELECT блобов. Ни как это ранее я не предполагал.
Теперь мне просто кажется странным почему нельзя повторно использовать временные блоб до окончания транзакции? Или просто Firebird не имеет механизма проверки используется блоб или нет.
Интересно, наблюдался ли бы такой же эффект если бы использовалось поле VARCHAR аналогичного размера?
-
- Заслуженный разработчик
- Сообщения: 1436
- Зарегистрирован: 15 сен 2005, 09:05
Она не есть зло, просто делать ее надо правильно: с помощью блоб-фильтров, а не страшными удфками.ivl писал(а):Отсюда я делаю для себя один вывод: обработка функцией блоб поля в операторе SELECT есть зло, т.е. раздуть базу можно и ничего в неё не вставляя, только сделав большую выборку с обработкой в SELECT блобов.
ivl, ты через строку читаешь, что ли?
Тебе ж ясно сказано, что место используется повторно. А насчёт "до окончания транзакции" - откуда известно, что не обратишься к нему?
Я не увидел фразы, где бы ты утверждал, что при использовании Commit (не CommitRetaining!) и повторении операций база продолжает так же пухнуть, и мусор не собирается.
Но даже если так - то Влад тебе уже сказал, что может быть недоработка со временными блобами в старых версиях, и посоветовал проверить на 2-ке.
Вместо этого твердишь, что "придираемся к мелочам" и "ничего путного ещё не сказано".
Тебе ж ясно сказано, что место используется повторно. А насчёт "до окончания транзакции" - откуда известно, что не обратишься к нему?
Я не увидел фразы, где бы ты утверждал, что при использовании Commit (не CommitRetaining!) и повторении операций база продолжает так же пухнуть, и мусор не собирается.
Но даже если так - то Влад тебе уже сказал, что может быть недоработка со временными блобами в старых версиях, и посоветовал проверить на 2-ке.
Вместо этого твердишь, что "придираемся к мелочам" и "ничего путного ещё не сказано".
мда. "разочарован". прямо анекдот получается. "я разочарован, что пихаю тучу временных блобов в базу".Честно сказать, я немного разочарован: при обработке функцией блоб поля в операторе SELECT на большом объеме выбираемой информации в одной транзакции происходит рост базы...
хреновый вывод. Ты в функции ПИШЕШЬ БЛОБ НА ДИСК! Или ты ожидал, что волшебным образом запись будет происходит "в никуда"?Отсюда я делаю для себя один вывод: обработка функцией блоб поля в операторе SELECT есть зло, т.е. раздуть базу можно и ничего в неё не вставляя, только сделав большую выборку с обработкой в SELECT блобов. Ни как это ранее я не предполагал.
Как это "обработка ... не вставляя..."??? Кто тебя заставляет такую обработку делать?
Это не связано с мусором. CommitRetaining здесь тоже не при чём. Он читает всю таблицу (с блобами) целиком каждый раз, когда хочет вставить новую запись - все имеющиеся блобы проходят через его ф-цию и она создаёт новые их копии. Коммит только удалит накопленные тек. тр-цией блобы, но постоянных блобов всё больше и больше.WildSery писал(а):Я не увидел фразы, где бы ты утверждал, что при использовании Commit (не CommitRetaining!) и повторении операций база продолжает так же пухнуть, и мусор не собирается.
2-ка, как и ожидалось, жрёт гораздо меньше памяти во время таких издевательствWildSery писал(а):Но даже если так - то Влад тебе уже сказал, что может быть недоработка со временными блобами в старых версиях, и посоветовал проверить на 2-ке.