LOB_DATA, verificações lentas da tabela e algumas perguntas de E / S

19

Eu tenho uma tabela bastante grande com uma das colunas sendo um dado XML com um tamanho médio de entrada XML sendo ~ 15 kilobytes. Todas as outras colunas são entradas regulares, bigints, GUIDs etc. Para ter alguns números concretos, digamos que a tabela tenha um milhão de linhas e tenha aproximadamente 15 GB de tamanho.

O que notei é que essa tabela é muito lenta para selecionar dados, se eu quiser selecionar todas as colunas. Quando eu faço

SELECT TOP 1000 * FROM TABLE

leva cerca de 20 a 25 segundos para ler os dados do disco - mesmo que eu não imponha nenhuma ordem ao resultado. Eu executo a consulta com o cache frio (ou seja, depois DBCC DROPCLEANBUFFERS). Aqui estão os resultados das estatísticas de IO:

Contagem de varredura 1, leituras lógicas 364, leituras físicas 24, leituras de leitura antecipada 7191, leituras lógicas de lob 7924, leituras físicas de lob 1690, leituras de leitura antecipada de lob 3968.

Ele captura ~ 15 MB de dados. O plano de execução mostra a Verificação de Índice em Cluster como eu esperaria.

Não há IO acontecendo no disco além das minhas consultas; Também verifiquei se a fragmentação do índice em cluster está próxima de 0%. Esta é uma unidade SATA de nível consumidor, mas ainda acho que o SQL Server poderá verificar a tabela mais rapidamente que ~ 100-150 MB / min.

A presença do campo XML faz com que a maioria dos dados da tabela seja localizada nas páginas LOB_DATA (na verdade, ~ 90% das páginas da tabela são LOB_DATA).

Acho que minha pergunta é: estou correto ao pensar que as páginas LOB_DATA podem causar verificações lentas não apenas por causa de seu tamanho, mas também porque o SQL Server não pode verificar o índice clusterizado de forma eficaz quando há muitas páginas LOB_DATA na tabela?

Ainda mais amplamente - é considerado razoável ter tal estrutura de tabela / padrão de dados? As recomendações para o uso do Filestream geralmente indicam tamanhos de campo muito maiores, então eu realmente não quero seguir esse caminho. Eu realmente não encontrei nenhuma informação boa sobre esse cenário em particular.

Eu estive pensando na compactação XML, mas ela precisa ser feita no cliente ou com o SQLCLR e exigiria bastante trabalho para implementar no sistema.

Tentei a compactação e, como os XMLs são altamente redundantes, posso (no ac # app) compactar XML de 20 KB a ~ 2,5 KB e armazená-lo na coluna VARBINARY, impedindo o uso de páginas de dados LOB. Isso acelera os SELECTs 20 vezes nos meus testes.

Alexander Shelemin
fonte
Alex: não tenho certeza se você viu a discussão relacionada à minha resposta (o link está em um comentário abaixo da minha resposta), mas pude aproximar-me de reproduzir seu cenário. Eu preenchi uma tabela que correspondia (até onde eu tinha informações) à sua descrição e obtive estatísticas de E / S muito semelhantes. Exceto, as "Leituras físicas do LOB" nunca chegaram nem perto. Então, eu queria saber se você atualizou o XML (mas não as outras colunas) e / ou teve muita fragmentação física dos seus arquivos de dados. Eu ainda não me importaria de obter o DDL da sua tabela e sua configuração de crescimento automático para cada arquivo de dados, e você reduz seus arquivos de dados?
Solomon Rutzky
Primeiro de tudo - muito obrigado pela resposta detalhada, não pude participar da discussão no momento devido à falta de tempo. Agora que você mencionou isso (não pensei nisso quando fez a pergunta) - o campo XML é atualizado várias vezes depois de criado, e é criado pequeno. Portanto, eu suspeitaria que, inicialmente, ele seja armazenado em linha e, após algumas atualizações, seja movido para uma estrutura de página LOB e, em seguida, receba mais algumas atualizações.
Alexander Shelemin
(Continuação) Verifiquei a fragmentação física dos arquivos antes de fazer a pergunta e a ferramenta interna do Windows achou que estava tudo bem, então não procurei mais. O crescimento automático é o padrão, em 1 MB, acredito, e os arquivos de dados não foram reduzidos.
Alexander Shelemin
Selecionar as 1000 principais * é de importância no meu caso particular. Eu certamente entendo que isso é considerado uma prática ruim, no entanto, algumas decisões de design de aplicativos são realmente difíceis de mudar depois que elas estão em vigor há muito tempo. O Select * é basicamente usado como uma estratégia de replicação entre bancos de dados entre diferentes componentes em nosso aplicativo. Existem profissionais nisso, por exemplo, podemos fazer muita manipulação arbitrária com dados / esquemas em tempo real, o que seria difícil com as técnicas de replicação internas, mas isso vem com seus problemas.
Alexander Shelemin
Alex, SELECT *não é o problema se você estiver precisando dos dados XML. É apenas um problema se você não deseja os dados XML. Nesse caso, por que diminuir a velocidade da consulta para recuperar dados que você não usa? Perguntei sobre as atualizações no XML perguntando se a fragmentação nas páginas LOB não estava sendo relatada com precisão. Por isso, perguntei na minha resposta como exatamente você determinou que o índice de cluster não estava fragmentado? Você pode fornecer o comando que você executou? E você fez uma REBUILD completa no Índice de Cluster? (continuação)
Solomon Rutzky

Respostas:

11

A presença do campo XML faz com que a maioria dos dados da tabela seja localizada nas páginas LOB_DATA (na verdade, ~ 90% das páginas da tabela são LOB_DATA).

Apenas ter a coluna XML na tabela não tem esse efeito. É a presença de dados XML que, sob certas condições , faz com que uma parte dos dados de uma linha seja armazenada fora da linha, nas páginas LOB_DATA. E enquanto um (ou talvez vários ;-) possa argumentar que sim, a XMLcoluna implica que realmente haverá dados XML, não é garantido que os dados XML precisem ser armazenados fora da linha: a menos que a linha já esteja preenchida além de serem dados XML, documentos pequenos (até 8000 bytes) podem caber na linha e nunca vão para uma página LOB_DATA.

Estou correto ao pensar que as páginas LOB_DATA podem causar verificações lentas não apenas por causa de seu tamanho, mas também porque o SQL Server não pode verificar o índice clusterizado de maneira eficaz quando há muitas páginas LOB_DATA na tabela?

A digitalização refere-se à observação de todas as linhas. Obviamente, quando uma página de dados é lida, todos os dados em linha são lidos, mesmo se você selecionou um subconjunto das colunas. A diferença com os dados LOB é que, se você não selecionar essa coluna, os dados fora da linha não serão lidos. Portanto, não é realmente justo tirar uma conclusão sobre a eficiência com que o SQL Server pode varrer esse índice clusterizado, pois você não testou exatamente isso (ou testou metade dele). Você selecionou todas as colunas, que inclui a coluna XML e, como você mencionou, é onde a maioria dos dados está localizada.

Portanto, já sabemos que o SELECT TOP 1000 *teste não estava apenas lendo uma série de 8k páginas de dados, todas seguidas, mas pulando para outros locais a cada linha . A estrutura exata desses dados LOB pode variar de acordo com o tamanho. Com base na pesquisa mostrada aqui ( Qual é o tamanho do ponteiro LOB para tipos (MAX) como Varchar, Varbinary, Etc? ), Existem dois tipos de alocações LOB fora da linha:

  1. Raiz Inline - para dados entre 8001 e 40.000 (realmente 42.000) bytes, se o espaço permitir, haverá 1 a 5 ponteiros (24 - 72 bytes) IN ROW que apontam diretamente para a (s) página (s) LOB.
  2. TEXT_TREE - para dados com mais de 42.000 bytes, ou se os ponteiros de 1 a 5 não puderem caber em linha, haverá apenas um ponteiro de 24 bytes para a página inicial de uma lista de ponteiros para as páginas LOB (por exemplo, " text_tree ").

Uma dessas duas situações ocorre cada vez que você recupera dados LOB com mais de 8000 bytes ou que simplesmente não se encaixam na linha. Publiquei um script de teste no PasteBin.com (script T-SQL para testar alocações e leituras de LOB ) que mostra os três tipos de alocações de LOB (com base no tamanho dos dados), bem como o efeito que cada um deles exerce sobre a lógica e leituras físicas. No seu caso, se os dados XML realmente forem inferiores a 42.000 bytes por linha, nenhum deles (ou muito pouco) deverá estar na estrutura TEXT_TREE menos eficiente.

Se você quiser testar a rapidez com que o SQL Server pode verificar esse Índice em Cluster, faça o, SELECT TOP 1000mas especifique uma ou mais colunas que não incluem essa coluna XML. Como isso afeta seus resultados? Deve ser um pouco mais rápido.

é considerado razoável ter tal estrutura de tabela / padrão de dados?

Dado que temos uma descrição incompleta da estrutura da tabela e do padrão de dados reais, qualquer resposta pode não ser ideal, dependendo de quais são esses detalhes ausentes. Com isso em mente, eu diria que não há nada obviamente irracional na estrutura da tabela ou no padrão de dados.

Posso (no ac # app) compactar XML de 20 KB a ~ 2,5 KB e armazená-lo na coluna VARBINARY, impedindo o uso de páginas de dados LOB. Isso acelera os SELECTs 20 vezes nos meus testes.

Isso agilizou a seleção de todas as colunas, ou mesmo apenas os dados XML (agora em VARBINARY), mas na verdade gera consultas que não selecionam os dados "XML". Supondo que você tenha cerca de 50 bytes nas outras colunas e um FILLFACTORde 100, então:

  • Sem compactação: 15k de XMLdados devem exigir 2 páginas LOB_DATA, que requerem 2 ponteiros para a raiz embutida. O primeiro ponteiro tem 24 bytes e o segundo é 12, para um total de 36 bytes armazenados em linha para os dados XML. O tamanho total da linha é 86 bytes e você pode ajustar cerca de 93 dessas linhas em uma página de dados de 8060 bytes. Portanto, 1 milhão de linhas requer 10.753 páginas de dados.

  • Compactação personalizada: 2,5 mil VARBINARYdados caberão na linha. O tamanho total da linha é de 2610 (2,5 * 1024 = 2560) bytes e você pode ajustar apenas três dessas linhas em uma página de dados de 8060 bytes. Portanto, 1 milhão de linhas requer 333.334 páginas de dados.

Portanto, a implementação da compactação personalizada resulta em um aumento de 30x nas páginas de dados para o Clustered Index. Ou seja, todas as consultas que usam uma varredura de índice clusterizado agora têm cerca de 322.500 páginas a mais de dados para ler. Consulte a seção detalhada abaixo para obter ramificações adicionais ao fazer esse tipo de compactação.

Eu recomendaria não fazer refatoração com base no desempenho de SELECT TOP 1000 * . Não é provável que seja uma consulta que o aplicativo emita e nem deve ser usada como a única base para otimizações potencialmente desnecessárias.

Para informações mais detalhadas e mais testes para tentar, consulte a seção abaixo.


Esta pergunta não pode receber uma resposta definitiva, mas podemos pelo menos progredir e sugerir pesquisas adicionais para ajudar a nos aproximar de descobrir o problema exato (idealmente com base em evidências).

O que nós sabemos:

  1. A tabela possui aproximadamente 1 milhão de linhas
  2. O tamanho da tabela é de aproximadamente 15 GB
  3. Tabela contém uma XMLcoluna e vários outros tipos de colunas: INT, BIGINT, UNIQUEIDENTIFIER, "etc."
  4. XMLcoluna "tamanho" é, em média, aproximadamente 15k
  5. Após a execução DBCC DROPCLEANBUFFERS, leva de 20 a 25 segundos para que a seguinte consulta seja concluída:SELECT TOP 1000 * FROM TABLE
  6. O Índice de Cluster está sendo verificado
  7. A fragmentação no índice clusterizado é próxima de 0%

O que achamos que sabemos:

  1. Nenhuma outra atividade de disco fora dessas consultas. Você tem certeza? Mesmo se não houver outras consultas do usuário, existem operações em segundo plano? Existem processos externos ao SQL Server em execução na mesma máquina que poderiam estar ocupando parte do IO? Pode não haver, mas não está claro com base apenas nas informações fornecidas.
  2. 15 MB de dados XML estão sendo retornados. Em que esse número se baseia? Uma estimativa derivada das 1000 linhas vezes a média de 15k de dados XML por linha? Ou uma agregação programática do que foi recebido para essa consulta? Se for apenas uma estimativa, eu não confiaria nela, pois a distribuição dos dados XML pode não ser da maneira implícita por uma média simples.
  3. A compactação XML pode ajudar. Como exatamente você faria a compactação no .NET? Através das classes GZipStream ou DeflateStream ? Esta não é uma opção de custo zero. Certamente compactará alguns dados em uma grande porcentagem, mas também exigirá mais CPU, pois você precisará de um processo adicional para compactar / descomprimir os dados a cada vez. Esse plano também removeria completamente sua capacidade de:

    • consultar os dados XML através da .nodes, .value, .query, e .modifyfunções XML.
    • indexe os dados XML.

      Lembre-se (desde que você mencionou que o XML é "altamente redundante")XML tipo de dados já está otimizado, pois armazena os nomes dos elementos e dos atributos em um dicionário, atribuindo um ID de índice inteiro a cada item e, em seguida, usando esse ID inteiro em todo o documento (portanto, ele não repete o nome completo para cada uso, nem o repete novamente como uma marca de fechamento para elementos). Os dados reais também têm espaço em branco externo removido. É por isso que os documentos XML extraídos não mantêm sua estrutura original e por que os elementos vazios são extraídos como <element />se fossem inseridos como<element></element>. Portanto, quaisquer ganhos de compactação via GZip (ou qualquer outra coisa) serão encontrados apenas pela compactação dos valores de elemento e / ou atributo, que é uma área de superfície muito menor que pode ser melhorada do que a maioria esperaria e provavelmente não vale a perda de recursos, conforme observado diretamente acima.

      Lembre-se também de que a compactação dos dados XML e o armazenamento do VARBINARY(MAX)resultado não eliminarão o acesso ao LOB, apenas o reduzirão. Dependendo do tamanho do restante dos dados na linha, o valor compactado pode caber na linha ou ainda pode exigir páginas LOB.

Essas informações, embora úteis, não são suficientes. Existem muitos fatores que influenciam o desempenho da consulta, portanto, precisamos de uma imagem muito mais detalhada do que está acontecendo.

O que não sabemos, mas precisamos:

  1. Por que o desempenho da SELECT *matéria? Esse é um padrão que você usa no código. Se sim, por quê?
  2. Qual é o desempenho de selecionar apenas a coluna XML? Quais são as estatísticas e o tempo se você fizer apenas SELECT TOP 1000 XmlColumn FROM TABLE;:?
  3. Quanto dos 20 a 25 segundos necessários para retornar essas 1000 linhas está relacionado a fatores de rede (obtendo os dados pela conexão) e quanto está relacionado a fatores de cliente (renderizando aproximadamente 15 MB mais o restante dos Dados XML na grade no SSMS ou possivelmente salvando em disco)?

    Fatorar esses dois aspectos da operação às vezes pode ser feito simplesmente não retornando os dados. Agora, pode-se pensar em selecionar uma Tabela Temporária ou Variável de Tabela, mas isso apenas introduziria algumas novas variáveis ​​(por exemplo tempdb, E / S de disco para , escritas no Log de Transações, possível crescimento automático de dados tempdb e / ou arquivo de log). espaço no buffer pool, etc). Todos esses novos fatores podem realmente aumentar o tempo de consulta. Em vez disso, normalmente armazeno as colunas em variáveis ​​(do tipo de dados apropriado; não SQL_VARIANT) que são substituídas a cada nova linha (ou seja SELECT @Column1 = tab.Column1,...).

    NO ENTANTO , conforme indicado por @PaulWhite neste DBA.StackExchange, a Logical lê diferentes ao acessar os mesmos dados LOB , com pesquisas adicionais publicadas no PasteBin ( script T-SQL para testar vários cenários para leituras LOB ) , LOBs não são acessados de forma consistente entre SELECT, SELECT INTO, SELECT @XmlVariable = XmlColumn, SELECT @XmlVariable = XmlColumn.query(N'/'), e SELECT @NVarCharVariable = CONVERT(NVARCHAR(MAX), XmlColumn). Portanto, nossas opções são um pouco mais limitadas aqui, mas aqui está o que pode ser feito:

    1. Elimine os problemas de rede executando a consulta no servidor que executa o SQL Server, no SSMS ou no SQLCMD.EXE.
    2. Para descartar problemas do cliente no SSMS, vá em Opções de consulta -> Resultados -> Grade e verifique a opção "Descartar resultados após a execução". Observe que esta opção impedirá TODAS as saídas, incluindo mensagens, mas ainda pode ser útil para descartar o tempo que o SSMS leva para alocar a memória por cada linha e desenhá-la na grade.
      Alternativamente, você pode executar a consulta via sqlcmd.exe e direcionar a saída para ir para lugar nenhum via: -o NUL:.
  4. Existe um tipo de espera associado a esta consulta? Se sim, o que é esse tipo de espera?
  5. Qual é o tamanho real dos dados para as XMLcolunas que estão sendo retornadas ? O tamanho médio dessa coluna em toda a tabela não importa realmente se as linhas "TOP 1000" contêm uma parte desproporcionalmente grande do total de XMLdados. Se você deseja conhecer as TOP 1000 linhas, consulte essas linhas. Por favor, execute o seguinte:

    SELECT TOP 1000 tab.*,
           SUM(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [TotalXmlKBytes],
           AVG(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [AverageXmlKBytes]
           STDEV(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [StandardDeviationForXmlKBytes]
    FROM   SchemaName.TableName tab;
  6. O esquema exato da tabela. Forneça a declaração completa CREATE TABLE , incluindo todos os índices.
  7. Plano de consulta? Isso é algo que você pode postar? Essa informação provavelmente não mudará nada, mas é melhor saber que não mudará do que adivinhar que não mudará e estará errado ;-)
  8. Existe fragmentação física / externa no arquivo de dados? Embora isso possa não ser um fator importante aqui, já que você está usando "SATA de nível de consumidor" e não SSD ou até mesmo SATA super-caro, o efeito de setores com ordem sub-ideal será mais perceptível, especialmente como o número desses setores isso precisa ser lido aumenta.
  9. Quais são os resultados exatos da seguinte consulta:

    SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(),
                              OBJECT_ID(N'dbo.SchemaName.TableName'), 1, 0, N'LIMITED');

ATUALIZAR

Ocorreu-me que eu deveria tentar reproduzir esse cenário para ver se tenho um comportamento semelhante. Então, criei uma tabela com várias colunas (semelhante à vaga descrição da Pergunta) e a preenchi com 1 milhão de linhas, e a coluna XML possui aproximadamente 15k de dados por linha (veja o código abaixo).

O que eu descobri é que fazer um procedimento SELECT TOP 1000 * FROM TABLEconcluído em 8 segundos na primeira vez e em 2 a 4 segundos a cada momento (sim, executando DBCC DROPCLEANBUFFERSantes de cada execução da SELECT *consulta). E meu laptop de vários anos não é rápido: SQL Server 2012 SP2 Developer Edition, 64 bits, 6 GB de RAM, dual 2.5 Ghz Core i5 e uma unidade SATA de 5400 RPM. Também estou executando o SSMS 2014, SQL Server Express 2014, Chrome e várias outras coisas.

Com base no tempo de resposta do meu sistema, repetirei que precisamos de mais informações (ou seja, informações específicas sobre a tabela e os dados, resultados dos testes sugeridos etc.) para ajudar a diminuir a causa do tempo de resposta de 20 a 25 segundos que você está vendo.

SET ANSI_NULLS, NOCOUNT ON;
GO

IF (OBJECT_ID(N'dbo.XmlReadTest') IS NOT NULL)
BEGIN
    PRINT N'Dropping table...';
    DROP TABLE dbo.XmlReadTest;
END;

PRINT N'Creating table...';
CREATE TABLE dbo.XmlReadTest 
(
    ID INT NOT NULL IDENTITY(1, 1),
    Col2 BIGINT,
    Col3 UNIQUEIDENTIFIER,
    Col4 DATETIME,
    Col5 XML,
    CONSTRAINT [PK_XmlReadTest] PRIMARY KEY CLUSTERED ([ID])
);
GO

DECLARE @MaxSets INT = 1000,
        @CurrentSet INT = 1;

WHILE (@CurrentSet <= @MaxSets)
BEGIN
    RAISERROR(N'Populating data (1000 sets of 1000 rows); Set # %d ...',
              10, 1, @CurrentSet) WITH NOWAIT;
    INSERT INTO dbo.XmlReadTest (Col2, Col3, Col4, Col5)
        SELECT  TOP 1000
                CONVERT(BIGINT, CRYPT_GEN_RANDOM(8)),
                NEWID(),
                GETDATE(),
                N'<test>'
                  + REPLICATE(CONVERT(NVARCHAR(MAX), CRYPT_GEN_RANDOM(1), 2), 3750)
                  + N'</test>'
        FROM        [master].[sys].all_columns sac1;

    IF ((@CurrentSet % 100) = 0)
    BEGIN
        RAISERROR(N'Executing CHECKPOINT ...', 10, 1) WITH NOWAIT;
        CHECKPOINT;
    END;

    SET @CurrentSet += 1;
END;

--

SELECT COUNT(*) FROM dbo.XmlReadTest; -- Verify that we have 1 million rows

-- O.P. states that the "clustered index fragmentation is close to 0%"
ALTER INDEX [PK_XmlReadTest] ON dbo.XmlReadTest REBUILD WITH (FILLFACTOR = 90);
CHECKPOINT;

--

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 * FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,       physical reads 1,     read-ahead reads 4436,
              lob logical reads 5676, lob physical reads 1, lob read-ahead reads 3967.

 SQL Server Execution Times:
   CPU time = 171 ms,  elapsed time = 8329 ms.
*/

E, como queremos calcular o tempo necessário para ler as páginas que não são do LOB, executei a consulta a seguir para selecionar tudo, exceto a coluna XML (um dos testes sugeridos acima). Isso retorna em 1,5 segundos de forma bastante consistente.

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 ID, Col2, Col3, Col4 FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,    physical reads 1,     read-ahead reads 4436,
              lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1666 ms.
*/

Conclusão (no momento)
Com base na minha tentativa de recriar seu cenário, acho que não podemos apontar a unidade SATA ou a E / S não sequencial como a principal causa dos 20 - 25 segundos, especialmente porque ainda estamos não sabe a rapidez com que a consulta retorna quando não inclui a coluna XML. E não pude reproduzir o grande número de leituras lógicas (não LOB) que você está mostrando, mas tenho a sensação de que preciso adicionar mais dados a cada linha à luz disso e da declaração de:

~ 90% das páginas da tabela são LOB_DATA

Minha tabela possui 1 milhão de linhas, cada uma com pouco mais de sys.dm_db_index_physical_stats15 mil dados XML e mostra que existem 2 milhões de páginas LOB_DATA. Os 10% restantes seriam 222k IN_ROW páginas de dados, mas eu tenho apenas 11.630 delas. Portanto, mais uma vez, precisamos de mais informações sobre o esquema da tabela e os dados reais.

Solomon Rutzky
fonte
Esta discussão foi movida para o bate-papo .
Paul White diz GoFundMonica
10

Estou correto ao pensar que as páginas LOB_DATA podem causar verificações lentas não apenas por causa de seu tamanho, mas também porque o SQL Server não pode verificar o índice clusterizado de maneira eficaz

Sim, a leitura de dados LOB não armazenados em linha leva a E / S aleatória em vez de E / S sequencial. A métrica de desempenho do disco a ser usada aqui para entender por que é rápida ou lenta é IOPS de leitura aleatória.

Os dados LOB são armazenados em uma estrutura em árvore onde a página de dados no índice clusterizado aponta para uma página de Dados LOB com uma estrutura raiz LOB que, por sua vez, aponta para os dados LOB reais. Ao percorrer os nós raiz no índice clusterizado, o SQL Server só pode obter os dados em linha por leituras seqüenciais. Para obter os dados LOB, o SQL Server precisa ir para outro lugar no disco.

Acho que se você mudasse para um disco SSD, não sofreria muito com isso, já que o IOPS aleatório para um SSD é muito maior do que para um disco giratório.

é considerado razoável ter tal estrutura de tabela / padrão de dados?

Sim, poderia ser. Depende do que esta tabela está fazendo por você.

Geralmente, os problemas de desempenho com XML no SQL Server acontecem quando você deseja usar o T-SQL para consultar o XML e, mais ainda, quando deseja usar valores do XML em um predicado em uma cláusula where ou associação. Se for esse o caso, você pode dar uma olhada na promoção da propriedade ou nos índices XML seletivos ou em um novo design das estruturas da tabela, destruindo o XML nas tabelas.

Eu tentei a compressão

Fiz isso uma vez em um produto há mais de 10 anos e lamento desde então. Eu realmente sentia falta de não poder trabalhar com os dados usando o T-SQL, por isso não recomendaria isso a ninguém, se puder ser evitado.

Mikael Eriksson
fonte
Muito obrigado pela resposta. Com relação à compactação: não tenho certeza se uma recomendação estrita é justificada, pois a necessidade de realmente consultar esses dados do T-SQL depende obviamente da natureza dos dados armazenados. No meu caso, decidi usar a compactação por enquanto.
Alexander Shelemin