No Dynamics AX, existe um mecanismo de armazenamento em cache no qual as tabelas podem ser configuradas para serem carregadas na memória e armazenadas em cache. Esse cache é limitado a uma certa quantidade de KB para evitar problemas de memória. A configuração da qual estou falando é chamada entiretablecache
e carrega a tabela inteira na memória assim que um único registro é solicitado.
Até recentemente, contávamos com alguns scripts para verificar o tamanho das tabelas que possuem essa configuração para ver se o tamanho da tabela está acima desse limite.
Agora, no entanto, a compactação entra em ação e coisas como sp_spaceused ou sys.allocation_units parecem relatar o espaço realmente usado pelos dados compactados.
Obviamente, o servidor de aplicativos está trabalhando com dados não compactados, portanto o tamanho dos dados no disco no SQL Server é irrelevante. Preciso do tamanho real dos dados não compactados.
Eu sei de sp_estimate_data_compression_savings, mas como o nome diz, isso é apenas uma estimativa.
Eu preferiria ter o tamanho o mais correto possível.
A única maneira que eu conseguia pensar era em um SQL dinâmico complicado, criando tabelas não compactadas com a mesma estrutura que as tabelas compactadas, inserindo os dados compactados nessa tabela de sombra e, em seguida, verifique o tamanho dessa tabela de sombra.
Escusado será dizer que isso é um pouco tedioso e leva um tempo para rodar em um banco de dados de várias centenas de GB.
O Powershell poderia ser uma opção, mas eu não gostaria de repetir todas as tabelas para executá select *
-las e verificar o tamanho do script, pois isso apenas inundaria o cache e provavelmente levaria muito tempo.
Em resumo, preciso de uma maneira de obter o tamanho de cada tabela, pois ela será descompactada e com a fragmentação da equação apresentada ao aplicativo, se isso for possível. Sou aberto a diferentes abordagens, o T-SQL é o preferido, mas não sou contra o Powershell ou outras abordagens criativas.
Suponha que o buffer no aplicativo seja do tamanho dos dados. Um bigint sempre tem o tamanho de um bigint e um tipo de dados de caractere é de 2 bytes por caractere (unicode). Os dados BLOB também assumem o tamanho dos dados, uma enumeração é basicamente uma int e os dados numéricos são numéricos (38,12), data e hora é o tamanho de uma data e hora. Além disso, não há NULL
valores, eles são armazenados como uma sequência vazia 1900-01-01
ou zero.
Não há documentação sobre como isso é implementado, mas as suposições são baseadas em alguns testes e nos scripts usados pelo PFE e pela equipe de suporte (que também ignoram a compactação aparentemente, uma vez que a verificação é incorporada ao aplicativo e o aplicativo não pode dizer). se os dados subjacentes estiverem compactados), que também verificam os tamanhos da tabela. Este link, por exemplo, afirma:
Evite usar caches de tabela inteira para tabelas grandes (no AX 2009, com mais de 128 KB ou 16 páginas, no AX 2012, sobre a configuração do aplicativo 'tamanho da cache da tabela inteira' [padrão: 32 KB ou 4 páginas]) - mova para gravar o cache.
fonte
Respostas:
Embora o desejo por essas informações seja certamente compreensível, obtê-las, especialmente no contexto de "o mais correto possível", é mais complicado do que todo mundo espera devido a suposições incorretas. Seja executando a idéia da tabela de sombra descompactada mencionada na pergunta ou a sugestão de @ sp_BlitzErik em um comentário sobre restaurar o banco de dados e descompactar para verificação, não se deve presumir que o tamanho da tabela descompactada == o tamanho dos dados na memória no servidor de aplicativos:
Todas as linhas da tabela estão sendo armazenadas em cache? Ou apenas dentro de um intervalo? A suposição aqui é que é tudo, e isso pode estar correto, mas achei que pelo menos deveria ser mencionado que esse pode não ser o caso (a menos que a documentação indique o contrário, mas esse é um ponto menor, mas não queria) para não ser mencionado).
A pergunta foi atualizada para indicar: sim, todas as linhas estão sendo armazenadas em cache.
Sobrecarga da estrutura
Página e sobrecarga de linha no lado do banco de dados: quantas linhas cabem em uma página são determinadas por muitos fatores que podem prejudicar as estimativas. Mesmo com um
FILLFACTOR
de 100 (ou 0), ainda é provável que haja algum espaço não utilizado restante na página devido ao fato de não ser suficiente para uma linha inteira. E isso é um acréscimo ao cabeçalho da página. Além disso, se qualquer funcionalidade de isolamento de captura instantânea estiver ativada, acredito que haverá 13 bytes adicionais por linha ocupados pelo número da versão e isso reduzirá as estimativas. Há outras minúcias relacionadas ao tamanho real da linha (bitmap NULL, colunas de comprimento variável, etc.), mas os itens mencionados até agora devem ser o único argumento.Que tipo de coleção está sendo usada para armazenar os resultados em cache? Presumo que este é um aplicativo .NET, então é um
DataTable
? Uma lista genérica? Um SortedDictionary? Cada tipo de coleção tem uma quantidade diferente de ouvida. Eu não esperaria que nenhuma das opções espelhasse necessariamente as despesas gerais de Página e Linha no lado do banco de dados, especialmente em escala (tenho certeza de que uma pequena quantidade de linha pode não ter várias o suficiente para importar, mas você não está procurando diferenças em centenas de bytes ou apenas alguns kB).CHAR
/VARCHAR
data é armazenado em 1 byte por caractere (ignorando caracteres de byte duplo no momento).XML
é otimizado para não ocupar tanto espaço quanto a representação de texto implicaria. Esse tipo de dados cria um dicionário de nomes de elementos e atributos e substitui as referências reais a eles no documento por seus respectivos IDs (na verdade, bem legais). Caso contrário, os valores da sequência serão todos UTF-16 (2 ou 4 bytes por "caractere"), assim comoNCHAR
/NVARCHAR
.DATETIME2
está entre 6 e 8 bytes.DECIMAL
está entre 5 e 17 bytes (dependendo da precisão).Strings (novamente, assumindo .NET) são sempre UTF-16. Não há otimização para cadeias de 8 bits, como o que
VARCHAR
contém. MAS, as strings também podem ser "internadas", que é uma cópia compartilhada que pode ser referenciada várias vezes (mas não sei se isso funciona para strings em coleções ou, se for, se funciona para todos os tipos de coleções).XML
pode ou não ser armazenado da mesma maneira na memória (vou ter que procurar isso).DateTime
é sempre 8 bytes (como T-SQLDATETIME
, mas não comoDATE
,TIME
ouDATETIME2
).Decimal
é sempre 16 bytes .Tudo isso a dizer: não há praticamente nada que você possa fazer no lado do banco de dados para obter tamanho de pegada de memória ainda bastante preciso no lado do servidor de aplicativos. Você precisa encontrar uma maneira de interrogar o próprio servidor de aplicativos, depois de ser carregado com uma tabela específica, para saber o tamanho dele. E não tenho certeza se um depurador permitiria ver o tamanho do tempo de execução de uma coleção preenchida. Se não, então a única maneira de chegar perto seria a de passar por todas as linhas de uma tabela, multiplicando cada coluna pela apropriada .NET tamanho (por exemplo,
INT
=* 4
,VARCHAR
=DATALENGTH() * 2
,NVARCHAR
=DATALENGTH()
,XML
= 🙃, etc), mas que ainda deixa a questão da sobrecarga da coleção mais cada elemento da coleção.Dada uma nova definição na pergunta, provavelmente seria possível fazer a seguinte consulta para se aproximar. E não importa se a tabela está compactada ou não, embora seja responsabilidade de cada pessoa determinar se a varredura de todas as linhas é apropriada na Produção (talvez seja de uma restauração ou fora do horário de pico):
Mas lembre-se, isso não leva em consideração a sobrecarga da coleção ou do elemento da coleção. E não tenho certeza se podemos obter esse valor sem um depurador (ou possivelmente algo como ILSpy, mas não estou recomendando isso, pois isso pode violar o EULA, dependendo das leis locais).
fonte
Da sua pergunta, parece que você tem um tamanho máximo de cache
S
e não deseja carregar tabelas no cache que excedam esse tamanho. Se isso for verdade, você não precisará saber o tamanho exato de cada tabela. Você só precisa saber se uma tabela é maior ou menor que o tamanho máximo do cacheS
. Esse é um problema significativamente mais fácil, dependendo das definições de coluna e contagem de linhas de suas tabelas.Eu concordo com a grande resposta de Solomon Rutzky em que analisar dados não compactados não é o caminho a seguir e pode ser difícil apresentar uma boa aproximação para o tamanho real de uma tabela no cache. No entanto, vou trabalhar dentro da estrutura da pergunta e supor que você possa desenvolver uma fórmula suficientemente próxima, com base nas definições de coluna para tipos de dados estáticos e no comprimento real de suas colunas dinâmicas.
Se você tiver esse mapeamento de tipos de dados para armazenar em cache o tamanho, poderá avaliar algumas tabelas sem sequer olhar para os dados nelas:
sys.partitions
e calculando o tamanho da tabela usando definições de coluna.BIGINT
colunas pode ter o tamanho desses dados dimensionados como 10000000 * (8 + 8 + 8 + 8 + 8) = 400 M bytes, que podem ser maiores que o limite de tamanho do cacheS
. Não importa se ele também possui várias colunas de string.BIGINT
coluna e umaNVARCHAR(20)
coluna não pode exceder 100 * (8 + 2 * 20) = 4800 bytes.S
, é extremamente improvável que caiba no cache. Você precisaria fazer testes para descobrir se esse valor existe.Talvez você precise consultar os dados de tabelas que não se enquadram em nenhum dos critérios acima. Existem alguns truques que você pode usar para minimizar o impacto no desempenho disso. Eu diria que você tem duas prioridades concorrentes aqui: você valoriza a precisão, mas também não deseja verificar todos os dados em seu banco de dados. Pode ser possível adicionar algum tipo de buffer aos seus cálculos. Não sei se é mais aceitável excluir uma tabela que esteja ligeiramente abaixo do tamanho máximo de cache
S
ou incluir uma tabela que esteja ligeiramente acima do tamanho máximo de cache.Aqui estão algumas idéias para acelerar as consultas que analisam os dados da tabela:
TABLESAMPLE
enquanto o tamanho da amostra for grande o suficiente.SUM()
que saia cedo com base no valor desse agregado. Eu só vi esse trabalho para sempreROW_NUMBER()
. Mas você pode digitalizar os primeiros 10% da tabela, economizar o tamanho calculado dos dados, digitalizar os próximos 10% e assim por diante. Para tabelas muito grandes para o cache, você poderá salvar uma quantidade significativa de trabalho com essa abordagem ao sair mais cedo.Percebo que não incluí nenhum código SQL nesta resposta. Deixe-me saber se seria útil escrever um código de demonstração para alguma das idéias que discuti aqui.
fonte