(c) kdv, iBase.ru, 20.09.2025
В Firebird есть несколько разных кэшей - кэш базы данных, кэш сортировок, кэш подготовленных запросов, и кэш метаданных.
Все эти кэши как-то регулируются, через firebird.conf и databases.conf, кроме кэша метаданных.
Кэш метаданных представляет собой область, в которую загружается скомпилированный BLR (Binary Language Representation), т.е. бинарный код текста PSQL, при обращении к таблицам (вычисляемые поля, defaults и проч.), триггерам, процедурам и view. В Firebird при сохранении (create/alter) кода PSQL генерируется (и сохраняется) код BLR, который выполняется при обращении к такому объекту. Дальше в процессе работы исходный код SQL/PSQL этих объектов не используется.
В архитектуре Classic и SuperClassic каждый коннект это сам по себе "сервер", поэтому все метаданные загружались только в память такого коннекта. В SuperServer 2.х использовался общий кэш метаданных, но это приводило к проблемам конкуренции, и к тому что для использования измененных метаданных нужно было полностью отключить все коннекты от БД, и запустить их снова.
В Firebird 3.0 вернули раздельный кэш метаданных для каждого коннекта, даже для архитектуры SuperServer (и таким кэш метаданных остается в версиях 4.0 и 5.0).
В общем, в отличие от других кэшей, размер которых можно задавать, было бы неплохо понимать, каков размер кэша метаданных, например, если у вас в базе 2 тысячи таблиц, 5 тысяч триггеров, и 10 тысяч процедур.
Увы, явно это сложно определить. Для примерного определения можно воспользоваться следующими способами:
- извлечь все метаданные из базы командой isql -x database >script.sql, и посмотреть на размер script.sql.
Как уже было сказано выше, код компилируется, поэтому размер такого script.sql будет существенно больше, чем выполняемый на самом деле BLR. Как минимум, комментарии в BLR не сохраняются.
- сделать gbak -b -g -m database.gdb database_meta.fbk
где в database_meta вы получите только структуру базы данных, без данных, но опять же, там будет все вместе - и SQL метаданных и BLR.
Но этими способами можно хотя бы определить максимальный объем метаданных (с избытком), который будет распределяться каждым коннектом, который будет обращаться ко всем объектам этой БД сразу.
(понятно, что если коннект вызывает только одну процедуру, то в его память метаданных будет загружена процедура, таблицы к которым она обращается, триггеры, которые есть на этой или этих таблицах, и т.д.)
Тем не менее, есть возможность узнать размер памяти, который выделен под метаданные. Сделать это можно следующим запросом
select
d.mon$page_buffers pg_buffers
,d.mon$page_size pg_size
,iif(m.memo_db_used = 0, 'dedicated', 'shared') pg_cache_type
,d.mon$page_buffers * d.mon$page_size * iif( m.memo_db_used = 0, total_attachments_cnt, 1 ) as page_cache_size
,m.memo_used_att - (memo_used_trn + memo_used_stm) - iif( m.memo_db_used = 0, d.mon$page_buffers * d.mon$page_size * total_attachments_cnt, 0) as meta_cache_size
,m.memo_db_used
,m.memo_db_allo
,m.memo_used_att
,m.memo_used_trn
,m.memo_used_stm
,m.total_attachments_cnt
,m.active_attachments_cnt
,m.page_cache_operating_stm_cnt
,m.data_transfer_paused_stm_cnt
from (
select
sum( iif( u.stat_gr = 0, m.mon$memory_used, 0) ) memo_db_used -- SC/CS: 0; SS: >0
,sum( iif( u.stat_gr = 0, m.mon$memory_allocated, 0) ) memo_db_allo -- SC/CS: 0; SS: >0
,sum( iif( u.stat_gr = 1, m.mon$memory_used, 0) ) memo_used_att
,sum( iif( u.stat_gr = 2, m.mon$memory_used, 0) ) memo_used_trn
,sum( iif( u.stat_gr = 3, m.mon$memory_used, 0) ) memo_used_stm
,sum( iif( u.stat_gr = 1, 1, 0 ) ) total_attachments_cnt
,sum( iif( u.stat_gr = 1 and u.state = 1, 1, 0 ) ) active_attachments_cnt
,sum( iif( u.stat_gr = 2 and u.state = 1, 1, 0 ) ) active_transactions_cnt
,sum( iif( u.stat_gr = 3 and u.state = 1, 1, 0 ) ) page_cache_operating_stm_cnt -- server_side_run_stm_cnt
,sum( iif( u.stat_gr = 3 and u.state = 2, 1, 0 ) ) data_transfer_paused_stm_cnt -- data_transf_run_stm_cnt
from mon$memory_usage m
join
(
select 0 as stat_gr, m.mon$stat_id as stat_id, null as att_id, null as state
from mon$memory_usage m
where m.mon$stat_group =0
UNION ALL
select 1 as stat_gr, a.mon$stat_id as stat_id, a.mon$attachment_id as att_id, a.mon$state as state
from mon$attachments a
-- added 07.05.2020, actual for SuperServer 3.x+:
-- total_attachments_cnt must not include GC and CW
where mon$remote_protocol is not null -- common for 2.5 and 3.x+
-- FB 3.x+ only: a.mon$system_flag is distinct from 1
UNION ALL
select 2, t.mon$stat_id, t.mon$attachment_id, t.mon$state
from mon$transactions t
UNION ALL
select 3, s.mon$stat_id, s.mon$attachment_id, s.mon$state
from mon$statements s
-- ?! --> where upper( s.mon$sql_text ) not similar to upper('EXECUTE[[:WHITESPACE:]]+BLOCK%')
) u
on
m.mon$stat_id = u.stat_id and
m.mon$stat_group = u.stat_gr
) m
cross join mon$database d
Запрос вроде бы работает нормально, например, для стандартной employee.fdb в 3.0 выдает 614 килобайт, а в 5.0 - 1 мегабайт.
(в метаданных 5.0 больше mon$ таблиц, и они содержат больше столбцов. А указанный запрос учитывает не только метаданные самой БД, но и размер mon$ при обращении к ним).
Смотреть нужно на данные в столбце META_CACHE_SIZE. Здесь указан "одиночный" размер кэша метаданных БД. В столбце MEMO_DB_ALLOCATED указано суммарное значение метаданных для всех активных коннектов (в случае SuperServer).
Ради интереса приведены и другие столбцы, такие как размер памяти для транзакций, для sql-запросов, и проч.
Вопросы?
support@ibase.ru, Также можете поделиться своим размером метаданных, например относительно gbak -b -g -m ...