Biblioteca

Como calcular o tamanho dos metadados no Firebird SQL

(c) Kuzmenko Dmitry, 20-SEP-2025
Veja versao Ingles
No Firebird existem vários caches diferentes - o cache do banco de dados, cache de ordenação, cache de comandos preparados e cache de metadados.
Todos esses caches são regulados de alguma forma através do firebird.conf e databases.conf, exceto o cache de metadados.
O cache de metadados é uma área na qual o BLR compilado (Binary Language Representation) é carregado, ou seja, o código binário do texto PSQL, ao acessar tabelas (campos calculados, defaults, etc.), triggers, procedures e views. No Firebird, ao salvar (create/alter) código PSQL, o código BLR é gerado (e salvo), que é executado ao acessar tal objeto. Subsequentemente, durante a operação, o código SQL/PSQL fonte desses objetos não é utilizado.
Na arquitetura Classic e SuperClassic, cada conexão é em si um "servidor", então todos os metadados eram carregados apenas na memória daquela conexão. No SuperServer 2.x, um cache de metadados compartilhado era usado, mas isso levou a problemas de contenção, e para usar metadados modificados era necessário desconectar completamente todas as conexões do banco de dados e reiniciá-las.
No Firebird 3.0, eles trouxeram de volta o cache de metadados separado para cada conexão, mesmo para a arquitetura SuperServer (e tal cache de metadados permanece nas versões 4.0 e 5.0).
Em geral, diferentemente de outros caches cujo tamanho pode ser especificado, seria bom entender qual é o tamanho do cache de metadados, por exemplo, se você tem 2 mil tabelas, 5 mil triggers e 10 mil procedures no seu banco de dados.
Infelizmente, isso é difícil de determinar explicitamente. Para uma determinação aproximada, você pode usar os seguintes métodos:
Extrair todos os metadados do banco de dados com o comando

isql -x database >script.sql

e verificar o tamanho do script.sql.
Como mencionado acima, o código é compilado, então o tamanho de tal script.sql será significativamente maior do que o BLR realmente executado. No mínimo, comentários não são salvos no BLR.
Executar

gbak -b -g -m database.gdb database_meta.fbk

onde no database_meta você obterá apenas a estrutura do banco de dados, sem dados, mas novamente, tudo estará lá junto - tanto metadados SQL quanto BLR.
Mas com esses métodos você pode pelo menos determinar o volume máximo de metadados (com excesso) que será alocado por cada conexão que acessa todos os objetos deste banco de dados de uma vez.
(É compreensível que se uma conexão chama apenas uma procedure, então apenas essa procedure será carregada em sua memória de metadados, as tabelas que ela acessa, as triggers que existem nesta tabela ou nestas tabelas, e assim por diante.)

No entanto, existe uma maneira de descobrir o tamanho da memória alocada para metadados. Isso pode ser feito com a seguinte consulta:

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
Checar essa consulta - para o employee.fdb padrão no 3.0 retorna 614 kilobytes, e no 5.0 - 1 megabyte.
(Nos metadados do 5.0 existem mais tabelas mon$, e elas contêm mais colunas. E a consulta especificada contabiliza não apenas os metadados do banco de dados em si, mas também o tamanho das mon tabelas).

Você precisa observar os dados na coluna META_CACHE_SIZE. Isso mostra o tamanho "único" do cache de metadados do banco de dados. A coluna MEMO_DB_ALLOCATED mostra o valor total de metadados para todas as conexões ativas (no caso do SuperServer).
Por curiosidade, outras colunas também são mostradas, como tamanho de memória para transações, para consultas SQL, e assim por diante.