eManual.ru - электронная документация
Секция 2 из 3 - Предыдущая - Следующая
Все секции
- 1
- 2
- 3
1.20 Каков максимальный размер базы данных IB ?
Borland и независимые эксперты сообщают, что IB может обслуживать файлы БД
размером в ~10-20 гигабайт. Существует также ограничение на размер одного файла
БД, которое в основном зависит от ограничений используемой файловой системы,
и составляет от 2 до 4 гигабайт. В любом случае не стоит допускать увеличения
размера одного файла БД выше 2 гигабайт (создайте один или более вторичных файлов
заранее). Многофайловая БД может состоять из 65535 файлов, таким образом теоретический
предел для одной базы данных IB - 132 терабайта.
1.21 Сколько БД я могу открыть в одной транзакции
?
Не существует архитектурнго ограничения на такую возможность. Пользователи
сообщают о работе одновременно с 17-ю БД.
1.22 Сколько таблиц может быть в одной БД ?
65,536. Количество определяется переменной типа SHORT.
1.23 Сколько строк и столбцов может быть у одной
таблицы ?
Не существует ограничений на количество записей таблицы. Размер записи
может быть не более 64К, поэтому максимальное количество столбцов зависит
от используемых типов полей. Длины BLOB и VARCHAR в это число не входят,
т.к они хранятся отдельно от основной записи.
1.24 Сколько индексов может быть в БД или у таблицы
?
Можно создать в одной БД до 65535 индексов. Все они могут быть и для
одной таблицы.
1.25 Сколько событий можно вызвать из одной процедуры
или триггера ?
Ограничений на количество POST_EVENT нет, однако на клиентском месте ограничения
могут быть. Например компонент IBEventAlerter в поставке Delphi может зарегистрировать
максимально 15 событий.
1.26 Сколько таблиц можно объединять в join?
Нет архитектурных ограничений. Пользователи сообщают о запросах одновременно
по 15-и таблицам.
примечание: оптимизатор 5.x для более чем 7-и объединяемых явным или
неявным join производит операцию SORT MERGE (сортировка слиянием). Это приводит
к сильному замедлению запроса как минимум прямо пропорционально количеству записей
в таблицах. Эту проблему можно исправить только если запрос вроде
select H.id, ...
from H, A, B, C, D, E, F, G
where a.id = h.aid and b.id = h.bid and c.id = h.cid and d.id = h.did and e.id
= h.eid and f.id = h.fid and g.id = h.gid
привести к виду
select H.ID,
H.AID, (select A.NAME from A where A.ID = H.AID),
H.BID, (select B.NAME from B where B.ID = H.BID),
H.CID, (select C.NAME from C where C.ID = H.CID),
H.DID, (select D.NAME from D where D.ID = H.DID),
H.EID, (select E.NAME from E where E.ID = H.EID),
H.FID, (select F.NAME from F where F.ID = H.FID),
H.GID, (select G.NAME from G where G.ID = H.GID)
from H
1.27 Сколько уровней вложенности может иметь запрос
?
Нет архитектурных ограничений. Пользователи сообщают о запросах вложенностью
около 16-и.
1.28 Сколько полей можно использовать в одном
составном (композитном) индексе ?
До 16 полей. Обратите внимание, что в документации по IB сказано: "создание
foreing key по двум и более полям ошибки не вызывает, но не обрабатывается".
Кроме этого, оптимизатор будет использовать такой индекс только в том случае,
если в предложении WHERE или ORDER BY указаны все поля, составляющие этот индекс,
и для ORDER BY именно в том порядке, в котором они упоминаются в индексе.
1.29 Какая глубина рекурсии допустима для триггеров
или хранимых процедур ?
Триггеры и хранимые процедуры имеют ограничение в 1000 рекурсивных вызовов
под UNIX, и около 700 - под NT. Реально это число меньше, и зависит от количества
локальных переменных в хранимой процедуре, а также от объема текста самой процедуры.
Рекурсия для триггеров означает например выполнение UPDATE в триггере BEFORE
UPDATE для одной и той же таблицы, или выполнение в триггере процедуры, которая
модифицирует эту же таблицу, что вызывает срабатывание того же триггера.
1.30 Какой максимальный размер текста хранимой
процедуры или триггера?
Размер скомпилированной процедуры или триггера может быть до 48K. (скомпилированные
процедуры и триггеры находятся в blob-полях RDB$TRIGGER_BLR и RDB$PROCEDURE_BLR
соответствующих системных таблиц RDB$TRIGGERS и RDB$PROCEDURES).
В любом случае не стоит доводить даже исходный текст процедур и триггеров
до таких размеров - разбираться в таких текстах будет очень трудно.
1.31 Сколько UDF и фильтров blob можно определить
для одной БД ?
Имя UDF ограничено длиной в 31 символ. Количество параметров UDF - не более
10. Других ограничений нет.
1.32 Сколько клиентов могут зарегистрироваться
на одно событие ?
Такого ограничения не существует.
1.33 Как освободить ресурсы, занятые IB ?
Сделайте серверу IB ShutDown при помощи Server Manager.
1.34 Как осуществить referental integrity между
разными БД ? Или как связать таблицы из разных БД ?
Автоматической поддержки RI между разными БД нет (т.к. таблицы одной
БД не видны из другой). Вам придется это делать программно, используя two-phase
commit при прямом доступе к IB либо из разных TDatabase через BDE. В последнем
случае при объединении таблиц записи будут извлечены из БД на клиентскую
часть и только затем объединены.
Ситуация с отсутствием возможности связывания таблиц из разных БД считается
нормальной, т.к. целостность данных должна обеспечиваться только в пределах
одной БД. Кроме этого, невозможно объединить словарь данных у разных БД
(это относится практически ко всем SQL-серверам).
1.35 Как скопировать данные между БД ?
Такая возможность присуствует в IB 4.0, 4.1 (утилита QLI) однако отсутствует
в IB 4.2. Вам придется написать собственную программу с использованием BDE или
IB API. В простейшем случае можно обойтись утилитами DATAPUMP или Database Desktop.
Также см. ib.demo.ru/download.htm.
1.36 Как конвертировать разные наборы символов
?
Преобразование кодировок обслуживается автоматически, при операциях
копирования и при запросах. Таким образом, если вы создадите поле с новой
кодировкой символов, то при копировании данных в это поле они отконвертируются.
1.37 Поддерживает-ли IB репликацию и интерфейс
X/Open XA ?
IB не поддерживает репликацию или X/Open в версиях 4.0, 4.1, 4.2, 5.0, 5.1,
5.5, 5.6.
IB 6.0 будет содержать репликатор. В настоящее время существует несколько сторонних
реализаций репликаторов, найти которые можно на ib.demo.ru.
1.38 Можно-ли использовать имя таблицы как параметр
хранимой процедуры ?
Нет, но можно передать какое-либо строковое или числовое значение, чтобы обработать
его по IF .. ELSE.
Хранимая процедура не может динамически получать имя таблицы. (похоже
что причиной является то, что текст хранимой процедуры "скомпилирован"
в BLR, который невозможно интерпретировать).
1.39 Может-ли IB шифровать данные в БД или при
передаче по сети ?
IB не поддерживает шифрование вообще. Считается что быстрые алгоритмы шифрования
использовать не имеет смысла из-за их низкой степени защищенности, а серъезные
алгоритмы шифрования работают слишком медленно для нормальной работы с БД (по
скорости отклика). Поэтому защиту БД от несанкционированного доступа должна
обеспечивать операционная система, а защиту передаваемых данных - специальное
оборудование.
1.40 Как определить дисковое пространство, необходимое
для хранения БД ?
Точно посчитать размер БД не представляется возможным. Однако для предсказания
размера БД можно использовать определенные утверждения:
При импорте данных страницы (data pages) заполняются наполовину, если для
базы данных не указан флаг no_reserve (по умолчанию). Таким образом объем импортированных
данных будет в два раза больше (если данные импортируются из текстовых файлов).
Размер ключа индекса в байтах равен 5 + длина поля, однако индексы упаковываются
даже при вставке записей.
При импорте данных страницы индексов заполняются тем плотнее, чем меньше
разница между индексируемыми значениями. Если IB не может произвести сжатие
ключей, то индексные страницы будут заполнены наполовину.
При восстановлении (restore) данных страницы данных будут заполнены опять-же
наполовину, а страницы индексов - полностью.
Поля типов CHAR и VARCHAR хранят столько символов, сколько занимает значение
поля.
Строковые поля и также числовые последовательности при обновлении могут быть
упакованы по алгоритму RLE.
BLOB-поля хранятся сегментами (по умолчанию 80 байт), т.е. на одной странице
данных может помещаться несколько значений BLOB если их суммарный размер
меньше размера страницы.
Необходимо также учитывать, что при работе с БД возможно динамическое создание
страниц, необходимых для хранения версий записей. Эти страницы не возвращаются
файловой системе даже при операции database sweep. Как следствие этого, БД занимает
минимально возможный объем только после операций backup/restore, и максимальный
- при частом обновлении данных большим количеством пользователей.
1.41 Как выбрать размер страницы БД ?
Этому посвящен один из разделов IB Data Definition Guide. Теоретически имеет
смысл увеличить до 4К (с умолчательного 1К) размер страницы, если большее кол-во
таблиц в вашей БД имеет количество записей > 200000 (200 тыс.). Не забудьте,
что увеличение размера страницы повлечет увеличение размера кэш-буфера IB, т.к.
он измеряется в страницах.
Безусловно, нужно ориентироваться на конкретную задачу - если ваше приложение
часто выбирает случайные записи из БД, то производительность при увеличении
размера страницы может ухудшиться (больше обмена с диском), и наоборот
- для последовательных выборок больший размер страницы предпочтительнее.
1.42 "Архитектура множественных поколений записи"
приводит иногда к странным результатам - большое количество update без
commit "тормозит" некоторые другие запросы, пока для БД не сделать "sweep".
В чем причина ?
Архитектура множественных поколений записи" требует "кооперативной сборки
мусора". Это означает что каждый запрос который обнаружит неактуальные
записи должен удалить их. Это позволяет значительно уменьшить блокировки
и ускорить операции commit/rollback. Однако, как вы заметили, иногда выполнение
запросов может сильно замедляться если на диске много "устаревших" записей.
Это происходит из-за того, что изменения записываются как delta между старой
и новой записью, и при чтении новой записи IB должен ее "собрать" из старых
данных и delta. Для оптимизации "сборки" записей IB заполняет страницы
данных только наполовину, оставляя вторую половину для хранения возможных
delta. Если это пространство исчерпывается, то IB распределяет новые страницы
для хранения delta. В этом случае ухудшается IO (для сборки записей приходится
читать дополнительные страницы), которое и приводит к ухудшению общей производительности.
Реально, большинство приложений не обновляют всю БД непрерывно, поэтому
затраты на поколения записей оказываются несущественными в смысле общей
производительности. (т.е. для хранения delta хватает свободного пространства
на страницах данных, и не возникает лишнего обмена с диском).
Другие SQL-серверы, которые не поддерживают множественные поколения
записей, могут порождать проблемы другого рода - для запросов с изоляцией
Repeatable Read либо полное дублирование данных, либо полное блокирование
обновляемых таблиц.
Кроме того, logging в других SQL-серверах может вызывать похожее ухудшение
производительности.
1.43 Поддерживает-ли IB поля типа autoincrement
?
В явном виде нет - вместо этого в IB существует другой механизм, называемый
"генераторами". Генератор - некая переменная, значение которой может быть
получено и увеличено на некоторое значение (дельту) при помощи встроенной
функции GEN_ID. Создать генератор можно фразой
CREATE GENERATOR MYGENERATOR;
Обычно генераторы используют в триггерах, при этом текст триггера может
быть следующим:
CREATE TRIGGER TI_CLIENTS FOR CLIENTS
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
IF (new.CLIENT_ID IS NULL)
THEN
CLIENT_ID = GEN_ID(MYGENERATOR, 1);
END
Вместо значения "1" может быть использовано любое число, на которое
нужно иметь приращение текущего значения генератора.
Механизм генераторов гарантирует что даже при конкурентном (параллельном)
вызове функции GEN_ID каждому пользователю будет выдаваться уникальное
значение. Последнее значение генератора всегда запоминается в БД, поэтому
разработчику не нужно заботиться о "восстановлении" его максимального значения
после подсоединения к БД.
Генераторы являются переменными типа integer (longint), таким образом
если предположить что новое значение возвращается в среднем с интервалом
в 3 секунды, значений генератора хватит приблизительно на 270 лет.
примечание: если вы воспользуетесь приведенным выше примером использования
генератора в триггере, то у вас может возникнуть следующая проблема - при добавлении
записей с клиентского места новые записи будут "пропадать", или будет появляться
сообщение BDE "Record/Key deleted". Это связано с тем, что клиенту
никаким образом не может быть передана информация об идентификаторе сформированом
в триггере на сервере.
Т.е. новую запись можно будет увидеть только либо перевыполнив запрос либо
переместившись в конец таблицы (если еще не произошел fetch всех записей. Для
исключения такой ситуации можно создать хранимую процедуру возвращающую значение
генератора (так-же как и для триггера), и вызывать эту процедуру _перед_ созданием
новой записи (для Delphi - TTable.BeforePost). Вместо процедуры можно использовать
запрос select gen_id(mygen, 1) from rdb$database.
1.44 На каком языке пишутся хранимые процедуры
?
Хранимые процедуры пишутся на языке SQL включая некоторые расширения
- begin, end, exception, exit, if then else и т.д. За более подробной информацией
обращайтесь к IB Language Reference или IB Data Definition Guide (в печатном
или электронном виде).
1.45 Как сделать модуль UDF видимым IB ?
DLL с UDF нужно поместить либо в тот каталог, где находится исполняемый
файл IB, либо в WINDOWSSYSTEM либо в WINNTSYSTEM32.Не имеет смысла помещать
UDF DLL в каталог, где находится БД.
Регистрацию функций подключаемых UDF можно производить в любой момент
- DLL будет загружена в память только при первом обращении к любой из функций
UDF DLL.
примечание: вы должны учитывать особенности работы с UDF в IB
версий 4.2 и выше (с архитектурой SuperServer).
1.46 Есть-ли в IB функция SUBSTR и вообще, почему
в IB так мало встроенных функций ?
Функции SUBSTR в IB нет, но такую функцию можно достаточно просто написать
и подключиь к IB. Начиная с версии 5.0 SubStr и другие функции поставляются
в комплекте с IB, однако для их использования их нужно объявить в базе данных
явно.
1.47 Как можно создавать UDF ? (определяемые пользователем
функции)
Определяемые пользователем функции (UDF) могут быть на любом языке программирования
(компилятор которого поддерживает создание DLL), например на Delphi 2.0
для IB 4.0 for Windows NT или Windows 95, и на C для IB под Windows- и
Unix-платформах.
Для ознакомления с написанием UDF на Delphi можно обратиться к UDF Starter
Kit, распространяемому ДемоЦентром. Это наиболее полное описание того, как можно
писать UDF и какие параметры в них можно обрабатывать. Вместе с тем предлагаемые
функции можно использовать как готовый набор.
1.48 Что такое SHADOW в IB ?
Shadow - это программное "зеркалирование" БД. Все операции записи, производимые
над каким-либо файлом GDB параллельно производятся и над соответствующим
файлом SHADOW. При сбое GDB вы можете остановить работу пользователей и
просто скопировать Shadow на место оригинальной БД (GDB), после чего продолжить
работу. Необходимо учитывать, что поддержка Shadow замедляет операции изменения
БД. Желательно чтобы Shadow располагалась на другом винчестере, и еще лучше
если винчестеры с GDB и Shadow будут иметь разные контроллеры - в этом
случае запись будет распараллеливаться.
В ДемоЦентре проводились элементарные тесты на Local IB 4.1 (из комплекта
Delphi 2.0) под Windows95. Тест представлял собой добавление 10000 (десять
тысяч) записей в пустую БД, каждые 1000 записей обрамлялись StartTransaction-Commit.
Использовался HDD с контроллером IDE: (одна запись = ~100 байт + BLOB-поле
512К)
усредненное время
без Shadow |
4мин 40сек |
с Shadow на том-же винчестере |
6мин 00сек |
с Shadow на другом винчестере |
4мин 50сек |
Разумеется, при интенсивной многопользовательской работе и достаточно большой
базе данных теоретически должны ожидаться следующие результаты коэффициент
замедления
без Shadow |
1 |
с Shadow на том-же винчестере |
1.6 (минимум в полтора раза) |
с Shadow на разных винчестерах |
~1 (почти без замедления) |
Кроме того, очень сильное влияние на быстродействие IB оказывает параметр
Forced Writes - немедленное сохранение изменений страниц. Вы можете включить или
выключить этот параметр для конкретной БД на ходу при помощи Server Manager. При
выключенном Forced Writes работа с БД происходит в 5-6 раз быстрее, но есть опасность
потерять БД при внезапном выключении питания сервера.
примечание: при работе со SCSI-устройствами, или IDE с драйвером BusMaster,
разница при работе с включенным или выключенным Forced Writes будет минимальна,
если вообще заметна.
1.49 Как посмотреть содержимое или обновить теневую
(shadow) БД ?
Такие операции нельзя проводить над теневой БД. IB автоматически обеспечивает
полное соответствие оригинальной БД и ее тени. Изменения в теневой БД происходят
не на уровне транзакций, а на уровне модификаций страниц основной БД.
1.50 Почему после выдачи Commit или RollBack при
открытых таблицах или запросах они перечитываются полностью ?
Начнем с того, что такое перечитывание в BDE происходит не всегда. Если вы
открываете TTable или TQuery в контексте какой-либо транзакции, то содержимое
этих источников данных определяется уровнем изоляции транзакции (ReadCommitted
или RepeatableRead). Если при открытых источниках данных завершить транзакцию
подтверждением или откатом, то контекст транзакции сменится - вступит в действие
т.н. неявная транзакция, которая стартует после соединения с БД. В результате
данные, прочитанные до смены контекста могут стать неактуальными, поэтому BDE
фактически переоткрывает курсоры. А поскольку неизвестно, на какой строке выборки
завершилась явная транзакция, считываются все записи (fetch до конца таблицы).
Следует избегать таких ситуаций, и по крайней мере достаточно большие
выборки закрывать перед завершением или началом явной транзакции.
Более подробную информацию о поведении BDE при commit/rollback вы сможете
получить в BDE32.HLP, пункт DbiEndTran.
1.51 Как избавиться от ошибки "multiple rows in
singleton select" ?
Очевидно что данная ошибка происходит в вашем триггере или хранимой
процедуре. Обычный SELECT внутри триггера или процедуры должен возвращать
одну строку (row), т.к. при двух и более строках IB не знает куда поместить
значения полей этих строк. Если ваш SELECT возвращает несколько записей,
то нужно пользоваться конструкцией FOR SELECT ... INTO ... DO ... которая
производит обработку возвращаемого набора записей в цикле.
Если-же вы уверены, что ваш SELECT должен вернуть только одну запись,
а ошибка все-таки возникает, то давайте рассмотрим следующую ситуацию:
существуют таблицы ORDERS (заказы) и CLIENTS (клиенты).
обе эти таблицы имеют поле связи CLIENT_ID INTEGER.
для того чтобы вытащить информацию о клиенте используется запрос:
SELECT CLIENT_ID, CLIENT_NAME
FROM CLIENTS
WHERE CLIENT_ID = ?
где ? - либо значение либо переменная.
Теперь представим себе, что этот запрос должен выполняться в триггере
при вставке записи в таблицу ORDERS
CREATE TRIGGER TI_ORDERS FOR ORDERS
ACTIVE AFTER INSERT POSITION 0
AS
DECLARE VARIABLE CID INTEGER;
DECLARE VARIABLE CNAME CHAR(30);
BEGIN
SELECT C.CLIENT_ID, C.CLIENT_NAME
FROM CLIENTS C
WHERE C.CLIENT_ID = CLIENT_ID
INTO :CID, :CNAME;
...
Итак, поскольку в запросе использован псевдоним C (FROM CLIENTS C),
то якобы существует гарантия что в предложении WHERE будут сравниваться
поле C.CLIENT_ID из таблицы CLIENTS и поле CLIENT_ID из таблицы ORDERS
(в триггере доступны имена полей собственной таблицы). На самом деле даже
использование псевдонимов не дает гарантии что переменные будут разичаться,
и получается что в предложении WHERE сравнивается само с собой поле таблицы
CLIENTS.CLIENT_ID, и в запросе возвращается ВСЯ таблица CLIENTS.
Вот почему возникает вышеупомянутое сообщение об ошибке.
Избавиться от него можно несколькими путями:
Использовать разные имена полей для связи между CLIENTS и ORDERS. например
OCLIENT_ID и CCLIENT_ID.
Использовать уточнитель new.CLIENT_ID, несмотря на то что в документации
указано что для триггеров последействия (AFTER) он не имеет смысла.
SELECT C.CLIENT_ID, C.CLIENT_NAME
FROM CLIENTS C
WHERE C.CLIENT_ID = new.CLIENT_ID
...
Перед запросом поместить CLIENT_ID в локальную переменную, и в запросе
использовать сравнение не с полем, а с этой локальной переменной.
CID=CLIENT_ID;
SELECT C.CLIENT_ID, C.CLIENT_NAME
FROM CLIENTS C
WHERE C.CLIENT_ID = :CID
...
1.52 Что такое "метаданные" ?
Метаданные - это информация о таблицах пользователя, триггерах, процедурах,
связях между таблицами, индексах, и т.п.
1.53 Где Borland IB хранит информацию о метаданных
?
IB хранит информацию о метаданных в таблицах, имена которых начинаются
с RDB$. Например, информация о связях между таблицами находится в таблице
RDB$RELATIONS. Список системных таблиц и их назначение вы можете посмотреть
в книге Borland IB Language Reference (или IBLR.PDF в электронном виде.
находится в каталоге MANUALS дистрибутива Delphi 1.0.x)
! Не рекомендуется вносить изменения напрямую в системные таблицы, этим
можно испортить БД. При необходимости изменения системных таблиц вы должны
руководствоваться IB Language Reference !
1.54 Почему невозможно использовать домены (DOMAIN)
в хранимых процедурах ?
Отвечает один из разработчиков IB David Schnepper:
"Потому что мы не стали реализовывать это. Попробую вспомнить причины,
хотя это было около 4-х лет назад:
достаточно трудно осуществлять domain validation в параметрах ввода/вывода
SP (например define domain age smallint check (age >=0 and age <=150);)
также трудно обеспечивать изменение доменов-параметров SP при модификации
домена. (например, используем AGE в SP, затем даем команду alter domain
age float. С другой стороны, в реализации SQL в IB нет возможности модифицировать
домен, но это возможно операторами GDML). Такое изменение домена приведет
к необходимости перекомпиляции SP, а в любой момент времени нельзя гарантировать
что SP не используется в момент изменения домена.
в настоящее время стандарт "draft SQL PSM", на котором базируется наша
реализация SP, не разрешает использовать домены в SP. Кроме того, я не
в курсе текущей ситуации со этим стандартом.
Безусловно, ни один из этих пунктов не облегчит вам жизнь, но хотя-бы даст
понимание почему данная возможность не реализована в IB 4.0, 4.1, 4.2".
1.55 После удаления хранимой процедуры невозможно
удалить таблицы, использовавшиеся в этой процедуре.
После drop procedure нужно выдать commit, и только после этого делать drop
table. В крайнем случае нужно убедиться, что процедура действительно удалена,
и удалить запись с ее именем в таблице RDB$DEPENDENCIES. Этот баг исправлен
в IB 5.x.
1.56 Как получить QUERY PLAN при работе с IB API
?
Нужно использовать функцию isc_dsql_sql_info(), запросив данные isc_info_sql_getplan.
К сожалению, пользоваться этой функцией для запросов, выдаваемых через
BDE невозможно, т.к. она требует предварительного вызова isc_dsql_prepare,
которая в свою очередь использует native transaction handle (недоступный
в BDE).
1.57 Мне нужны временные таблицы, но их нет в
IB. Что делать ?
IB не расчитан на частое изменение метаданных, поэтому создание таблицы
на ходу и ее удаление не является хорошим приемом. Вместо временной таблицы
вы можете создать постоянную таблицу для тех-же целей с первым полем равным
USER (и входящим в первичный ключ). В этом случае даже если несколько пользователей
начнут добавлять записи в такую таблицу, конфликта между ними не будет.
Вместо USER можно использовать генератор (generator), который будет действовать
на все время работы пользователя. Тогда если ваше приложение не использует
регистрацию IB, и все USER одинаковы, генераторы предотвратят конфликт
между пользователями.
1.58 Что такое "глубина индекса" (index depth),
показываемая в Database Analysis?
"Глубина индекса" это то-же что и глубина B-дерева. Если вам незнаком
этот термин, то следует обратиться к соответствующей литературе. Вообще
глубиной индекса определяется скорость поиска в индексе - т.е. именно столько
страниц придется прочитать IB чтобы найти указатель на нужную запись (при
поисках типа WHERE, но не ORDER BY). Естественно, что чем меньше глубина
индекса тем лучше. Оптимальной считается 3.
Если при просмотре в Database Analysis (или GSTAT) глубина какого-то индекса
окажется 4 или выше, то его необходимо деактивировать и активировать обратно
(т.е. перестроить - set index inactive/active). Это не всегда может помочь,
т.к. на глубину индекса в первую очередь влияет длина ключа и количество записей
в таблице.
Кроме того, при анализе заполнения индексных страниц (fill distribution) нужно
учитывать, что IB заполняет страницы в среднем наполовину для оптимизации изменений
индексов (или данных) транзакциями. Т.е. заполнение страниц 40-59% можно считать
нормальным.
Вообще упаковка (пересоздание) индексов считается эффективной только
для таблиц, в которых записи изменяются нечасто.
1.59 Как часто нужно делать Database Sweep
?
БД имеет по умолчанию параметр начала Sweep в 20000 транзакций. Это
может показаться большим, но для реальных приложений 20000 транзакций -
именно то число, когда в БД скапливается достаточно "мусора".
Количество "мусора" в БД определяется тем, насколько часто происходят
вставки, изменения и удаления. Версии записей удаляются только тогда, когда
они не нужны старейшей активной транзакции (OAT).
Вообще "sweep interval" - это разница между OAT и OIT (старейшей активной
и старейшей заинтересованной транзакцией), при котором необходимо делать
sweep. Если номер OIT увеличивается также, как и OAT, автоматический sweep
может и не наступить. Одной из наиболее вероятных причин увеличения номера
OIT является завершение транзакции операцией ROLLBACK.
При выполнении Sweep освободившиеся страницы не возвращаются операционной
системе - они будут использоваться IB как свободные страницы.
Если вы хотите самостоятельно и регулярно делать Sweep, то можно это
делать, например в ночное время когда нет работающих пользователей, при
помощи утилиты GFIX. Кроме того, выключить автоматический sweep можно установив
"sweep interval" = 0.
примечание: следить за OAT-OIT можно периодически получая статистику
по конкретной БД при помощи Server Manager (или утилиты GSTAT).
примечание: например если вы вставите в таблицу 1000000 (миллион) записей,
завершая вставку каждой операцией commit, то автоматический sweep запущен не
будет (OAT-OIT постоянно и очень мало). Однако так поступать тоже не стоит,
т.к. вставка будет происходить очень медленно - лучше всего в одной транзакции
вставлять 100-200 записей.
примечание: в IB 5.5 есть баг, который приводит к падению сервера при
SWEEP, если в базе данных есть индексы по полям, возможно содержащим значения
NULL. В этом случае необходимо отключить автоматический SWEEP (установить в
0), а операцию backup производить с установленным флагом Disable garbage collection.
Данный баг исправлен в IB 5.6.
1.60 Запрос с вычислением максимального (MAX)
значения работает медленно.
Да, к сожалению оптимизатор IB 4.x не использовал индексы при вычислении MAX,
даже если специально указать условие выбора или сортировки по полю, имеющему
индекс. А для IB 5.x индексы могут быть использованы как однонаправленные -
ASC индекс для функции MIN, и DESC индекс для функции MAX.
Тем не менее, проблему можно решить следующим способом:
Создать индекс по убыванию (DESC) по полю, которое используется в MAX.
Если вы работаете из Delphi, то нужно выполнить следующий запрос
SELECT FIELD FROM TABLE ORDER BY FIELD DESC
В этом случае запрос отработает мгновенно, и достаточно будет получить с сервера
первую запись - она и будет содержать максимальное значение нужного поля.
Однако если вы попытаетесь выдать такой запрос например в WISQL или
ISQL, то кроме выполнения запроса будут получены еще и все записи, что
может занять достаточно длительное время. В этом случае нужно создать хранимую
процедуру, возвращающую результат такого запроса (FOR SELECT ...). WISQL
не выбирает все записи из хранимой процедуры, а только первую. Ею и окажется
нужная вам запись. Эту-же хранимую процедуру можно использовать и из Delphi
(StoredProc1.ExecProc) не прибегая к помощи компонента TQuery.
примечание: не стоит использовать MAX для генерации уникальных идентификаторов
- вместо этого лучше использовать механизм генераторов.
1.61 Можно-ли использовать stored procedures во
view
Вообще это нарушает правила построения VIEW, т.к. VIEW должны основываться
на таблицах, и возвращать записи в их естественном порядке (так как они
хранятся в таблицах). В хранимой процедуре возможна выдача как "псевдотаблиц"
(вычисляемые значения) так и записей таблиц в отличном от естественного
порядке (FOR SELECT с ORDER BY).
Кроме этого, view по умолчанию предполагается редактируемым, а хранимые
процедуры как правило не обеспечивают редактирование возвращаемых ими наборов
данных.
К сожалению, в IB отсутствует контроль за использованием SP во view
поэтому для версии 4.1 такое view создать просто невозможно (GPF), а в
версии 4.2 такое view не работает (GPF при SELECT * FROM MYVIEW).
1.62 Ошибка "low volume of metadata change" или
Как часто можно менять метаданные (например изменять структуру таблиц)?
существует ограничение на 255 модификаций метаданных между backup/restore.
Например
create table changes_a_lot (a smallint); /* версия 0 */
alter table changes_a_lot add b smallint; /* версия 1 */
...
alter table changes_a_lot drop b; /* версия 254 */
alter table changes_a_lot add constraint ... /* ой! версия 255 ! */
Кроме этого счетчик метаданных таблицы увеличивается при каждой модификации
триггера этой таблицы, в том числе и при ACTIVE/INACTIVE триггера.
Вообще метаданные изменяются нечасто если ваша БД находится в промышленном
использовании, а не на стадии разработки.
Так что если вы получили подобное сообщение об ошибке - сделайте backup и restore
вашей БД, и продолжайте работать.
примечание: см. материал по версиям метаданных.
Секция 2 из 3 - Предыдущая - Следующая
|