Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Aplica-se a:SQL Server
Banco de Dados SQL do Azure
Visão geral
Este exemplo mostra o recurso OLTP na memória. Ele mostra tabelas com otimização de memória e procedimentos armazenados compilados nativamente e pode ser usado para demonstrar os benefícios de desempenho do OLTP na memória.
Observação
Para exibir este artigo do SQL Server 2014 (12.x), consulte Extensões para AdventureWorks que demonstram In-Memory OLTP.
O exemplo a seguir migra cinco tabelas do banco de dados do AdventureWorks2025 para a otimização de memória e inclui uma carga de trabalho de demonstração para o processamento de pedidos de vendas. Você pode usar essa carga de trabalho de demonstração para ver o benefício de desempenho do uso do OLTP na memória em seu servidor.
Na descrição do exemplo, discutimos as compensações feitas na migração das tabelas para o OLTP na memória para considerar os recursos que ainda não têm suporte para tabelas com otimização de memória.
A documentação do exemplo foi estruturada como segue:
Pré-requisitos para instalar o exemplo e executar a carga de trabalho de demonstração.
Instruções para instalar o exemplo do In-Memory OLTP baseado no AdventureWorks.
Descrição das tabelas e procedimentos de exemplo inclui descrições das tabelas e procedimentos adicionados ao pelo exemplo OLTP em memória, bem como considerações para migrar algumas das tabelas originais
AdventureWorks2025para serem otimizadas em memória.As instruções para executar medidas de desempenho usando a carga de trabalho de demonstração incluem instruções para instalar e executar ostress, uma ferramenta que usa para conduzir a carga de trabalho e executar a carga de trabalho de demonstração em si.
Pré-requisitos
-
SQL Server 2016 (13.x)
Para testes de desempenho, um servidor com as especificações semelhantes ao ambiente de produção. Para este exemplo específico, você deve ter pelo menos 16 GB de memória disponível para o SQL Server. Para obter diretrizes gerais sobre hardware para OLTP na memória, consulte a seguinte postagem no blog: Considerações de hardware para In-Memory OLTP no SQL Server
Instale o exemplo OLTP em memória baseado no AdventureWorks
Siga estas etapas para instalar o exemplo:
Baixe
AdventureWorks2016_EXT.bakeSQLServer2016Samples.zipde: https://github.com/microsoft/sql-server-samples/releases/tag/adventureworks para uma pasta local, por exemploC:\Temp.Restaure o backup do banco de dados usando o Transact-SQL ou o SQL Server Management Studio:
Identifique a pasta de destino e o nome do arquivo de dados, por exemplo:
H:\DATA\AdventureWorks2022_Data.mdfIdentifique a pasta de destino e o nome do arquivo para o arquivo de log, por exemplo:
I:\DATA\AdventureWorks2022_log.ldf- O arquivo de log deve ser colocado em uma unidade que não seja a do arquivo de dados, preferencialmente uma unidade de baixa latência como um armazenamento de SSD ou de PCIe, para obter o desempenho máximo.
Exemplo de script T-SQL:
RESTORE DATABASE [AdventureWorks2022] FROM DISK = N'C:\temp\AdventureWorks2022.bak' WITH FILE = 1, MOVE N'AdventureWorks2022_Data' TO N'h:\DATA\AdventureWorks2022_Data.mdf', MOVE N'AdventureWorks2022_Log' TO N'i:\DATA\AdventureWorks2022_log.ldf', MOVE N'AdventureWorks2022_mod' TO N'h:\data\AdventureWorks2022_mod' GOPara exibir os scripts de exemplo e a carga de trabalho, descompacte o arquivo
SQLServer2016Samples.zipem uma pasta local. Consulte o arquivoIn-Memory OLTP\readme.txtpara obter instruções sobre como executar a carga de trabalho.
Descrição das tabelas e procedimentos de exemplo
O exemplo cria tabelas para produtos e pedidos de vendas, com base em tabelas existentes no AdventureWorks2025. O esquema das novas tabelas é semelhante às tabelas existentes, com algumas diferenças, conforme explicado posteriormente nesta seção.
As novas tabelas com otimização de memória carregam o sufixo _inmem. O exemplo também inclui tabelas correspondentes que carregam o sufixo _ondisk – essas tabelas podem ser usadas para fazer uma comparação um-para-um entre o desempenho de tabelas com otimização de memória e tabelas baseadas em disco em seu sistema.
As tabelas com otimização de memória usadas na carga de trabalho para a comparação de desempenho são totalmente duráveis e registradas. Eles não sacrificam durabilidade ou confiabilidade para obter o ganho de desempenho.
A carga de trabalho de destino para esse exemplo é o processamento de pedidos de vendas, onde também consideramos informações sobre produtos e descontos. Para esse fim, usamos as tabelas SalesOrderHeader, SalesOrderDetail, Product, SpecialOffer e SpecialOfferProduct.
Dois novos procedimentos armazenados, Sales.usp_InsertSalesOrder_inmem e Sales.usp_UpdateSalesOrderShipInfo_inmem, são usados para inserir pedidos de venda e atualizar as informações de envio de um determinado pedido de venda.
O novo esquema Demo contém tabelas e procedimentos armazenados auxiliares para executar uma carga de trabalho de demonstração.
Concretamente, a amostra do Na Memória OLTP adiciona os seguintes objetos ao AdventureWorks2025:
Tabelas adicionadas pelo exemplo
As novas tabelas
Sales.SalesOrderHeader_inmem
- Informações de cabeçalho sobre os pedidos de vendas. Cada pedido de vendas tem uma linha nesta tabela.
Sales.SalesOrderDetail_inmem
- Detalhes dos pedidos de vendas. Cada item de linha de um pedido de vendas tem uma linha nesta tabela.
Sales.SpecialOffer_inmem
- Informações sobre as ofertas especiais, incluindo o percentual de desconto associado a cada oferta especial.
Sales.SpecialOfferProduct_inmem
- Tabela de referência entre ofertas especiais e produtos. Cada oferta especial pode caracterizar zero ou mais produtos, e cada produto pode ser caracterizado em zero ou mais ofertas especiais.
Production.Product_inmem
- Informações sobre os produtos, inclusive o preço da lista.
Demo.DemoSalesOrderDetailSeed
- Usado na carga de trabalho de demonstração para criar exemplos de pedidos de vendas.
Variações das tabelas baseadas em disco:
Sales.SalesOrderHeader_ondiskSales.SalesOrderDetail_ondiskSales.SpecialOffer_ondiskSales.SpecialOfferProduct_ondiskProduction.Product_ondisk
Diferenças entre as tabelas originais baseadas em disco e as novas tabelas com otimização de memória
Normalmente, as novas tabelas introduzidas por este exemplo usam as mesmas colunas e os mesmos tipos de dados que as tabelas originais. No entanto, há algumas diferenças. Listamos as diferenças nesta seção, juntamente com uma lógica para as alterações.
Sales.SalesOrderHeader_inmem
Restrições padrão têm suporte para tabelas com otimização de memória, e migramos a maioria das restrições padrão como tal. No entanto, a tabela
Sales.SalesOrderHeaderoriginal contém duas restrições padrão que recuperam a data atual, para as colunasOrderDateeModifiedDate. Em uma carga de trabalho de processamento de pedidos com alta taxa de transferência e muita simultaneidade, qualquer recurso global pode se tornar um ponto de contenção. O tempo do sistema é um recurso global tão importante, e observamos que ele pode ser um ponto de estrangulamento quando estamos executando uma carga de trabalho OLTP In-Memory que insere pedidos de vendas, especialmente se o tempo do sistema precisar ser recuperado para várias colunas no cabeçalho do pedido de vendas e nos detalhes do pedido de vendas. O problema é tratado neste exemplo através da recuperação do tempo do sistema apenas uma vez para cada pedido de vendas que é inserido, e o uso desse valor para as colunas de data e hora emSalesOrderHeader_inmeme emSalesOrderDetail_inmem, no procedimento armazenadoSales.usp_InsertSalesOrder_inmem.UDTs (Tipos de dados definidos pelo usuário) de alias – a tabela original usa dois UDTs de alias
dbo.OrderNumberedbo.AccountNumber, para as colunasPurchaseOrderNumbereAccountNumber, respectivamente. O SQL Server 2016 (13.x) não dá suporte ao Alias UDT para tabelas com otimização de memória, portanto, as novas tabelas usam os tipos de dados do sistema nvarchar(25) e nvarchar(15), respectivamente.Colunas anuláveis em chaves de índice – na tabela original, a coluna
SalesPersonIDé anulável, enquanto nas novas tabelas a coluna não é anulável e tem uma restrição padrão com valor (-1). Essa circunstância ocorre porque índices em tabelas com otimização de memória não podem ter colunas anuláveis na chave de índice; -1 é um substituto para NULL nesse caso.Colunas computadas – As colunas computadas e
SalesOrderNumbersão omitidasTotalDue, pois o SQL Server 2016 (13.x) não dá suporte a colunas computadas em tabelas com otimização de memória. A nova exibiçãoSales.vSalesOrderHeader_extended_inmemreflete as colunasSalesOrderNumbereTotalDue. Por disso, você pode usar essa exibição se essas colunas são necessárias.- Aplica-se a: SQL Server 2017 (14.x). A partir do SQL Server 2017 (14.x), há suporte para colunas computadas em índices e tabelas com otimização de memória.
Restrições de chave estrangeira são compatíveis com tabelas com otimização de memória no SQL Server 2016 (13 x), mas somente se as tabelas referenciadas também têm otimização de memória. Chaves estrangeiras que referenciam tabelas que também são migradas para a otimização de memória são mantidas nas tabelas migradas, enquanto outras chaves estrangeiras são omitidas. Além disso,
SalesOrderHeader_inmemé uma tabela frequente na carga de trabalho de exemplo, e restrições de chaves estrangeiras exigem processamento extra para todas as operações DML, pois exige pesquisas em todas as outras tabelas referenciadas nessas restrições. Portanto, a suposição é que o aplicativo garante a integridade referencial daSales.SalesOrderHeader_inmemtabela e a integridade referencial não é validada quando as linhas são inseridas.Rowguid - a coluna rowguid é omitida. Embora o uniqueidentifier tenha suporte para tabelas com otimização de memória, a opção ROWGUIDCOL não tem suporte no SQL Server 2016 (13.x). As colunas desse tipo são geralmente usadas para a replicação de mesclagem ou para tabelas com colunas filestream. Este exemplo não inclui isso.
Sales.SalesOrderDetail
Restrições padrão - semelhantes a
SalesOrderHeader, a restrição padrão que exige a data/hora do sistema não é migrada. Em vez disso, o procedimento armazenado que insere pedidos de vendas cuida da inserção da data/hora do sistema atual na primeira inserção.Colunas computadas – a coluna
LineTotalcomputada não foi migrada, pois não há suporte para colunas computadas com tabelas com otimização de memória no SQL Server 2016 (13.x). Para acessar esta coluna, use a exibiçãoSales.vSalesOrderDetail_extended_inmem.Rowguid – a coluna
rowguidé omitida. Para obter detalhes, confira a descrição da tabelaSalesOrderHeader.
Production.Product
UDTs de alias – a tabela original usa tipo de dados definidos pelo usuário
dbo.Flag, que equivalem ao tipo de dados bit do sistema. A tabela migrada usa o tipo de dados bit.Rowguid – a coluna
rowguidé omitida. Para obter detalhes, confira a descrição da tabelaSalesOrderHeader.
Sales.SpecialOffer
-
Rowguid – a coluna
rowguidé omitida. Para obter detalhes, confira a descrição da tabelaSalesOrderHeader.
Sales.SpecialOfferProduct
-
Rowguid – a coluna
rowguidé omitida. Para obter detalhes, confira a descrição da tabelaSalesOrderHeader.
Considerações para índices em tabelas com otimização de memória
O índice de linha de base para tabelas com otimização de memória é o índice NONCLUSTERED, que dá suporte a pesquisas de ponto (busca de índice em predicado de igualdade), exames de intervalo (busca de índice em predicado de desigualdade), verificações de índice completo e exames ordenados. Além disso, os índices NONCLUSTERED dão suporte à pesquisa nas colunas principais da chave de índice. De fato, os índices NONCLUSTERED com otimização de memória dão suporte a todas as operações com o suporte de índices NONCLUSTERED baseados em disco, exceto apenas para verificações regressivas. Portanto, o uso de índices NONCLUSTERED é uma opção confiável para seus índices.
Os índices de HASH podem ser usados para otimizar mais a carga de trabalho. Eles são otimizados para pesquisas de ponto e inserções de linhas. No entanto, deve-se considerar que eles não dão suporte a verificações de intervalo, verificações ordenadas ou pesquisa em colunas de chave de índice principais. Consequentemente, tome cuidado ao usar esses índices. Além disso, é necessário especificar o bucket_count no momento da criação. Normalmente, ele deve ser definido entre uma e duas vezes o número de valores de chave de índice, mas valores superestimados não costumam ser um problema.
Para obter mais informações:
- Diretrizes para operações de índice online
- Como escolher a bucket_count correta
- Índices em tabelas com otimização de memória
Os índices nas tabelas migradas foram ajustados para o processamento de pedidos de venda para demonstração. A carga de trabalho depende de inserções e pesquisas de ponto nas tabelas Sales.SalesOrderHeader_inmem e Sales. SalesOrderDetail_inmem também depende de pesquisas de ponto nas colunas de chave primária nas tabelas Production.Product_inmem e Sales.SpecialOffer_inmem.
Sales.SalesOrderHeader_inmem tem três índices, que são todos os índices de HASH por motivos de desempenho, e porque exames ordenados ou de intervalo não são necessários para a carga de trabalho.
Índice de HASH em (
SalesOrderID): o bucket_count é dimensionado em 10 milhões (arredondado para 16 milhões), pois o número esperado de pedidos de vendas é de 10 milhõesÍndice de HASH em (
SalesPersonID): o bucket_count é de 1 milhão. O conjunto de dados fornecido não tem muitos vendedores. Porém, essa bucket_count grande permite crescimento futuro. Além disso, você não pagará uma penalidade de desempenho por pesquisas de ponto se a bucket_count estiver superdimensionada.Índice de HASH em (
CustomerID): o bucket_count é de 1 milhão. O conjunto de dados fornecido não tem muitos clientes, mas isso permite um crescimento futuro.
Sales.SalesOrderDetail_inmem tem três índices, que são todos os índices de HASH por motivos de desempenho, e porque exames ordenados ou de intervalo não são necessários para a carga de trabalho.
Índice HASH em (
SalesOrderID,SalesOrderDetailID): este é o índice de chave primária e, mesmo que as pesquisas em (SalesOrderID,SalesOrderDetailID) sejam pouco frequentes, usar um índice de hash para a chave acelera as inserções de linha. A bucket_count é dimensionada em 50 milhões (arredondado para 67 milhões): o número esperado de pedidos de vendas é de 10 milhões e esse valor é dimensionado para ter uma média de cinco itens por pedidoÍndice HASH em (
SalesOrderID): pesquisas por ordem de vendas são frequentes: você deseja encontrar todos os itens de linha correspondentes a um único pedido. O bucket_count é dimensionado em 10 milhões (arredondado para 16 milhões), pois o número esperado de pedidos de vendas é de 10 milhõesÍndice de HASH em (
ProductID): o bucket_count é de 1 milhão. O conjunto de dados fornecido não tem muitos produtos, mas isso permite um crescimento futuro.
Production.Product_inmem tem três índices
Índice de HASH em 9
ProductID): as pesquisas emProductIDestão no caminho crítico para a carga de trabalho de demonstração; então, este é um índice hashÍndice NÃO AGRUPADO em (
Name): permite varreduras ordenadas de nomes de produtosÍndice NONCLUSTERED em (
ProductNumber): isso permite verificações ordenadas de números de produto
Sales.SpecialOffer_inmem tem um índice da HASH em (SpecialOfferID): as pesquisas de ponto de ofertas especiais estão na parte essencial da carga de trabalho de demonstração. O bucket_count é dimensionado em 1 milhão para permitir futuro crescimento.
Sales.SpecialOfferProduct_inmem não é referenciado na carga de trabalho de demonstração e, portanto, não há nenhuma necessidade aparente de usar índices de hash nesta tabela para otimizar a carga de trabalho - os índices em (SpecialOfferID, ProductID) e (ProductID) são NONCLUSTERED.
No exemplo anterior, algumas das contagens de buckets são superdimensionadas, mas não as contagens de bucket para os índices SalesOrderHeader_inmem e SalesOrderDetail_inmem: elas são dimensionadas para apenas 10 milhões de pedidos de vendas. Isso foi feito para permitir a instalação do exemplo em sistemas com baixa disponibilidade de memória, embora, nesses casos, a carga de trabalho de demonstração falhe com um erro de memória insuficiente. Se você quiser dimensionar além de 10 milhões de pedidos de vendas, sinta-se à vontade para aumentar o número de buckets de forma correspondente.
Considerações sobre a utilização de memória
A utilização de memória no banco de dados de exemplo, antes e após executar a carga de trabalho de demonstração, é abordada na seção Utilização de memória para as tabelas com otimização de memória.
Procedimentos armazenados adicionados pelo exemplo
Os dois principais procedimentos armazenados para inserir o pedido de vendas e atualizar detalhes do envio são os seguintes:
Sales.usp_InsertSalesOrder_inmemInsere um novo pedido de vendas no banco de dados e gera o
SalesOrderIDpara esse pedido. Como parâmetros de entrada, ele recebe detalhes do cabeçalho do pedido de vendas e dos itens de linha do pedido.Parâmetro de saída:
-
@SalesOrderID int – o
SalesOrderIDpedido de vendas que acabou de ser inserido
-
@SalesOrderID int – o
Parâmetros de entrada (obrigatório):
- @DueDatedatetime2
- @CustomerIDint
- @BillToAddressIDint
- @ShipToAddressIDint
- @ShipMethodIDint
-
@SalesOrderDetails
Sales.SalesOrderDetailType_inmem- parâmetro com valor de tabela (TVP) que contém os itens de linha do pedido
Parâmetros de entrada (opcional):
- @Statustinyint
- @OnlineOrderFlagbit
- @PurchaseOrderNumbernvarchar(25)
- @AccountNumbernvarchar(15)
- @SalesPersonIDint
- @TerritoryIDint
- @CreditCardIDint
- @CreditCardApprovalCodevarchar(15)
- @CurrencyRateIDint
- @Commentnvarchar(128)
Sales.usp_UpdateSalesOrderShipInfo_inmemAtualize as informações de envio de um pedido de vendas específico. Isso também atualiza as informações de envio de todos os itens de linha do pedido de vendas.
Este é um procedimento de wrapper para os procedimentos armazenados compilados nativamente
Sales.usp_UpdateSalesOrderShipInfo_native, com a lógica de repetição para lidar com conflitos em potencial (inesperados) em transações simultâneas que atualizam o mesmo pedido. Para obter mais informações, confira Lógica de repetição.
Sales.usp_UpdateSalesOrderShipInfo_native- Este é o procedimento armazenado compilado nativamente que efetivamente processa a atualização das informações de envio. Ele deve ser chamado a partir do procedimento armazenado do wrapper
Sales.usp_UpdateSalesOrderShipInfo_inmem. Se o cliente pode tratar as falhas e implementa a lógica de repetição, você pode chamar esse procedimento diretamente, e não usar o procedimento armazenado de wrapper.
- Este é o procedimento armazenado compilado nativamente que efetivamente processa a atualização das informações de envio. Ele deve ser chamado a partir do procedimento armazenado do wrapper
O procedimento armazenado a seguir é usado para a carga de trabalho de demonstração.
Demo.usp_DemoReset- Redefine a demonstração esvaziando e propagando novamente as tabelas
SalesOrderHeadereSalesOrderDetail.
- Redefine a demonstração esvaziando e propagando novamente as tabelas
Os procedimentos armazenados a seguir são usados para inserir e excluir das tabelas com otimização de memória para garantir o domínio e a integridade referencial.
Production.usp_InsertProduct_inmemProduction.usp_DeleteProduct_inmemSales.usp_InsertSpecialOffer_inmemSales.usp_DeleteSpecialOffer_inmemSales.usp_InsertSpecialOfferProduct_inmem
Finalmente, o procedimento armazenado a seguir é usado para verificar o domínio e a integridade referencial.
dbo.usp_ValidateIntegrityParâmetro opcional: @object_id – ID do objeto para validar a integridade para
Esse procedimento depende das tabelas
dbo.DomainIntegrity,dbo.ReferentialIntegrityedbo.UniqueIntegritypara as regras de integridade que precisam ser verificadas – o exemplo popula essas tabelas com base na verificação, na chave estrangeira e nas restrições exclusivas que existem para as tabelas originais no banco de dados doAdventureWorks2025.Ele se baseia nos procedimentos auxiliares
dbo.usp_GenerateCKCheck,dbo.usp_GenerateFKCheckedbo.GenerateUQCheckpara gerar o T-SQL necessário para realizar as verificações de integridade.
Medidas de desempenho usando a carga de trabalho de demonstração
ostress é uma ferramenta de linha de comando que foi desenvolvida pela equipe de suporte do MICROSOFT CSS SQL Server. Essa ferramenta pode ser usada para executar consultas ou executar procedimentos armazenados em paralelo. Você pode configurar o número de threads para executar uma determinada instrução T-SQL em paralelo e especificar quantas vezes a instrução deve ser executada neste thread; ostress gira os threads e executa a instrução em todos os threads em paralelo. Após a conclusão da execução de todos os threads, o ostress relata o tempo necessário para que todos os threads terminem a execução.
Instalar ostress
O ostress é instalado como parte dos utilitários RML (Linguagem de Marcação de Relatório) ; não há nenhuma instalação autônoma para ostress.
Etapas da instalação:
Baixe e execute o pacote de instalação do x64 para os utilitários do RML na página: Baixar RML para SQL Server
Se houver uma caixa de diálogo informando que determinados arquivos estão em uso, selecione "Continuar"
Executar ostress
Ostress é executada do prompt de linha de comando. É mais conveniente executar a ferramenta a partir do Prompt de Comando RML, que é instalado como parte dos Utilitários RML.
Para abrir o Prompt Cmd RML, siga estas instruções:
No Windows, abra o menu Iniciar selecionando a tecla Windows e digite rml. Selecione RML Cmd Prompt, que está na lista de resultados da pesquisa.
Verifique se o prompt de comando está localizado na pasta de instalação Utilitários de RML.
As opções de linha de comando para ostress podem ser vistas ao executá-lo simplesmente ostress.exe sem nenhuma opção de linha de comando. As principais opções a serem consideradas para executar o ostress com este exemplo são as seguintes:
| Opção | Description |
|---|---|
-S |
Nome da instância do SQL Server à qual se conectar. |
-E |
Usar a autenticação do Windows para se conectar (padrão); se você usar a autenticação do SQL Server, use as opções -U e especifique -P o nome de usuário e a senha, respectivamente. |
-d |
Nome do banco de dados, para este exemplo AdventureWorks2025. |
-Q |
A instrução T-SQL a ser executada. |
-n |
Número de conexões processando cada arquivo/consulta de entrada. |
-r |
O número de iterações para cada conexão para executar cada arquivo/consulta de entrada. |
Carga de trabalho de demonstração
O procedimento armazenado principal usado na carga de trabalho de demonstração é Sales.usp_InsertSalesOrder_inmem/ondisk. No exemplo a seguir, o script constrói um parâmetro de valor de tabela (TVP) com dados de exemplo e executa o procedimento para inserir um pedido de venda com cinco itens de linha.
A ferramenta ostress é usada para executar os procedimentos armazenados em paralelo, para simular a inserção simultânea de pedidos de venda por clientes.
Redefina a demonstração após cada execução de estresse executando o Demo.usp_DemoReset. Este procedimento exclui as linhas nas tabelas com otimização de memória, trunca as tabelas baseadas em disco e executa um ponto de verificação de banco de dados.
O seguinte script é executado simultaneamente para simular uma carga de trabalho de processamento de pedido de vendas:
DECLARE @i AS INT = 0, @od AS Sales.SalesOrderDetailType_inmem, @SalesOrderID AS INT, @DueDate AS DATETIME2 = sysdatetime(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS INT = (RAND() * 5) + 1;
INSERT INTO @od
SELECT OrderQty,
ProductID,
SpecialOfferID
FROM Demo.DemoSalesOrderDetailSeed
WHERE OrderID = CAST ((RAND() * 106) + 1 AS INT);
WHILE (@i < 20)
BEGIN
EXECUTE Sales.usp_InsertSalesOrder_inmem
@SalesOrderID OUTPUT,
@DueDate,
@CustomerID,
@BillToAddressID,
@ShipToAddressID,
@ShipMethodID,
@od;
SET @i + = 1;
END
Com esse script, cada pedido de exemplo criado é inserido 20 vezes, com 20 procedimentos armazenados executados em um loop WHILE. O loop é usado para considerar o fato de que o banco de dados é usado para criar o pedido de exemplo. Em ambientes de produção típicos, o aplicativo de camada intermediária constrói a ordem de vendas a ser inserida.
O script anterior insere pedidos de vendas em tabelas com otimização de memória. O script para inserir pedidos de vendas em tabelas baseadas em disco é derivado substituindo as duas ocorrências de _inmem por _ondisk.
Usamos a ferramenta ostress para executar os scripts usando várias conexões simultâneas. Usamos o parâmetro -n para controlar o número de conexões e o parâmetro r para controlar quantas vezes o script é executado em cada conexão.
Executar a carga de trabalho
Para testar na escala, inserimos 10 milhões de pedidos de vendas, usando 100 conexões. Esse teste é executado de forma razoável em um servidor modesto (por exemplo, 8 núcleos físicos, 16 núcleos lógicos), e o armazenamento básico de SSD para o log. Se o teste não tiver um bom desempenho em seu hardware, examine a seção Solucionar problemas de testes de execução lenta. Se você quiser reduzir o nível de estresse para este teste, diminua o número de conexões alterando o parâmetro -n. Por exemplo, para reduzir a contagem de conexões para 40, altere o parâmetro -n100 para -n40.
Como medida de desempenho para a carga de trabalho, usamos o tempo decorrido conforme relatado por ostress.exe após a execução da carga de trabalho.
As instruções e medidas a seguir usam uma carga de trabalho que insere 10 milhões de pedidos de vendas. Para obter instruções para executar uma carga de trabalho reduzida inserindo 1 milhão de pedidos de vendas, consulte as instruções presentes em In-Memory OLTP\readme.txt, como parte do arquivo SQLServer2016Samples.zip.
Tabelas com otimização de memória
Começamos executando a carga de trabalho em tabelas com otimização de memória. O comando a seguir abre 100 threads, cada um executado para 5.000 iterações. Cada iteração insere 20 pedidos de vendas em transações separadas. Há 20 inserções por iteração para compensar o fato de que o banco de dados é usado para gerar os dados a serem inseridos. Isso gera um total de 20 * 5.000 * 100 = 10.000.000 inserções de ordens de venda.
Abra o Prompt do Cmd RML e execute o seguinte comando:
Selecione o botão Copiar para copiar o comando, e cole-o no prompt de comando Utilitários de RML.
ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -q -Q"DECLARE @i AS INT = 0, @od AS Sales.SalesOrderDetailType_inmem, @SalesOrderID AS INT, @DueDate AS DATETIME2 = SYSDATETIME(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS INT = (RAND() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID = CAST ((RAND() * 106) + 1 AS INT); WHILE (@i < 20) BEGIN EXECUTE Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; SET @i + = 1; END"
Em um servidor de teste com um número total de 8 núcleos físicos (16 lógicos), isso levou 2 minutos e 5 segundos. Em um segundo servidor de teste com 24 núcleos físicos (48 lógicos), isso levou 1 minuto e 0 segundos.
Observe a utilização da CPU enquanto a carga de trabalho está em execução. Use, por exemplo, o gerenciador de tarefas. Você vê que a utilização da CPU está perto de 100%. Se esse não for o caso, você tem um gargalo de E/S de log, consulte também solução de problemas de testes de execução lenta.
Tabelas baseadas em disco
O comando a seguir executa a carga de trabalho em tabelas baseadas em disco. Essa carga de trabalho pode demorar um pouco para ser executada, o que ocorre em grande parte devido à contenção de trava no sistema. As tabelas com otimização de memória são livres de travas e, portanto, não sofrem com esse problema.
Abra o Prompt Cmd RML e execute o seguinte comando:
Selecione o botão Copiar para copiar o comando, e cole-o no prompt de comando Utilitários de RML.
ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2022 -q -Q"DECLARE @i AS INT = 0, @od AS Sales.SalesOrderDetailType_ondisk, @SalesOrderID AS INT, @DueDate AS DATETIME2 = sysdatetime(), @CustomerID AS INT = RAND() * 8000, @BillToAddressID AS INT = RAND() * 10000, @ShipToAddressID AS INT = RAND() * 10000, @ShipMethodID AS INT = (RAND() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID = CAST ((RAND() * 106) + 1 AS INT); WHILE (@i < 20) BEGIN EXECUTE Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; SET @i + = 1; END"
Em um servidor de teste com um número total de 8 núcleos físicos (16 lógicos), isso levou 41 minutos e 25 segundos. Em um segundo servidor de teste com 24 núcleos físicos (48 lógicos), isso levou 52 minutos e 16 segundos.
O principal fator na diferença de desempenho entre tabelas com otimização de memória e tabelas baseadas em disco neste teste é que, ao usar tabelas baseadas em disco, o SQL Server não pode utilizar totalmente a CPU. A razão é a contenção de trava: as transações simultâneas estão tentando gravar na mesma página de dados; as travas são usadas para garantir que somente uma transação de cada vez possa gravar em uma página. O mecanismo OLTP In-Memory está livre de travas e as linhas de dados não são organizadas em páginas. Assim, as transações simultâneas não bloqueiam as inserções umas das outras, permitindo que o SQL Server utilize totalmente a CPU.
Você pode observar a utilização da CPU enquanto a carga de trabalho está em execução. Use, por exemplo, o gerenciador de tarefas. Você vê com tabelas baseadas em disco que a utilização da CPU está longe de 100%. Em uma configuração de teste com 16 processadores lógicos, a utilização fica em torno de 24%.
Opcionalmente, você pode exibir o número de esperas de trava por segundo usando o Monitor de Desempenho, com o contador de desempenho \SQL Server:Latches\Latch Waits/sec.
Redefinir a demonstração
Para redefinir a demonstração, abra o Prompt Cmd RML e execute o seguinte comando:
ostress.exe -S. -E -dAdventureWorks2022 -Q"EXEC Demo.usp_DemoReset"
Dependendo do hardware, isso pode levar alguns minutos para ser executado.
Recomendamos uma redefinição após cada demonstração executada. Como essa carga de trabalho é somente de inserção, cada execução consome mais memória e, portanto, uma redefinição é necessária para evitar a perda de memória. A quantidade de memória consumida após uma execução é discutida na seção Utilização de memória após executar a carga de trabalho.
Solucionar problemas de testes de execução lenta
Os resultados do teste normalmente variam com o hardware e também com o nível de simultaneidade usado na execução do teste. Algumas coisas a serem buscadas se os resultados não forem os esperados:
Número de transações simultâneas: ao executar a carga de trabalho em um único thread, o ganho de desempenho com In-Memory OLTP provavelmente é menor que 2X. A contenção de trava só será um problema significativo se houver um alto nível de simultaneidade.
Número baixo de núcleos disponíveis para o SQL Server: isso significa que há um baixo nível de simultaneidade no sistema, pois só pode haver tantas transações em execução simultâneas quanto houver núcleos disponíveis para o SQL.
- Sintoma: se a utilização da CPU for alta ao executar a carga de trabalho em tabelas baseadas em disco, isso significa que não há muita contenção, apontando para uma falta de simultaneidade.
Velocidade da unidade de log: se a unidade de log não puder acompanhar o throughput de transações no sistema, a carga de trabalho ficará limitada pela E/S do log. Embora o log seja mais eficiente com OLTP in-memory, se a E/S de log é um gargalo, o ganho de desempenho potencial é limitado.
- Sintoma: se a utilização da CPU não estiver perto de 100% ou estiver muito irregular ao executar a carga de trabalho em tabelas otimizadas para memória, é possível que haja um gargalo de E/S de log. Isso pode ser confirmado abrindo o Monitor de Recursos e examinando o comprimento da fila para a unidade de log.
Utilização de espaço em disco e memória no exemplo
No exemplo a seguir, descrevemos o que esperar em termos de utilização de espaço em disco e memória para o banco de dados de exemplo. Também mostramos os resultados de um servidor de teste com 16 núcleos lógicos.
Utilização de memória para as tabelas com otimização de memória
Utilização geral do banco de dados
A consulta a seguir pode ser usada para obter a utilização total de memória para OLTP in-memory no sistema.
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Instantâneo logo após a criação do banco de dados:
| tipo | name | pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Padrão | 94 |
| MEMORYCLERK_XTP | DB_ID_5 | 877 |
| MEMORYCLERK_XTP | Padrão | 0 |
| MEMORYCLERK_XTP | Padrão | 0 |
Os administradores de memória padrão contêm estruturas de memória do sistema e são relativamente pequenos. O gerenciador de memória do banco de dados do usuário, nesse caso, o banco de dados com ID 5 (a ID database_id pode ser diferente na sua instância), tem cerca de 900 MB.
Utilização da memória por tabela
A seguinte consulta pode ser usada para fazer uma busca detalhada na utilização da memória das tabelas individuais e de seus índices:
SELECT object_name(t.object_id) AS [Table name],
memory_allocated_for_table_kb,
memory_allocated_for_indexes_kb
FROM sys.dm_db_xtp_table_memory_stats AS dms
INNER JOIN sys.tables AS t
ON dms.object_id = t.object_id
WHERE t.type = 'U';
A tabela a seguir mostra os resultados dessa consulta para uma instalação atualizada do exemplo:
| Nome da tabela | memory_allocated_for_table_kb |
memory_allocated_for_indexes_kb |
|---|---|---|
SpecialOfferProduct_inmem |
64 | 3840 |
DemoSalesOrderHeaderSeed |
1984 | 5,504 |
SalesOrderDetail_inmem |
15316 | 663552 |
DemoSalesOrderDetailSeed |
64 | 10,432 |
SpecialOffer_inmem |
3 | 8192 |
SalesOrderHeader_inmem |
7168 | 147456 |
Product_inmem |
124 | 12352 |
Como você pode ver, as tabelas são bastante pequenas: SalesOrderHeader_inmem tem cerca de 7 MB e SalesOrderDetail_inmem tem cerca de 15 MB de tamanho.
O que chama a atenção aqui é o tamanho da memória alocada para índices, comparado ao tamanho dos dados da tabela. Isso ocorre porque os índices de hash na amostra são pré-dimensionados para um tamanho de dados maior. Os índices de hash têm um tamanho fixo e, portanto, seu tamanho não cresce com o tamanho dos dados na tabela.
Utilização de memória após executar a carga de trabalho
Depois de inserir 10 milhões de pedidos de vendas, a utilização de memória total será semelhante à seguinte consulta:
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Veja aqui o conjunto de resultados.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Padrão | 146 |
| MEMORYCLERK_XTP | DB_ID_5 | 7374 |
| MEMORYCLERK_XTP | Padrão | 0 |
| MEMORYCLERK_XTP | Padrão | 0 |
Como você pode notar, o SQL Server está usando um pouco menos de 8 GB para as tabelas e os índices com otimização de memória no banco de dados de exemplo.
Verificando o uso detalhado de memória pela tabela após a execução de um exemplo:
SELECT object_name(t.object_id) AS [Table name],
memory_allocated_for_table_kb,
memory_allocated_for_indexes_kb
FROM sys.dm_db_xtp_table_memory_stats AS dms
INNER JOIN sys.tables AS t
ON dms.object_id = t.object_id
WHERE t.type = 'U';
Veja aqui o conjunto de resultados.
Table name |
memory_allocated_for_table_kb |
memory_allocated_for_indexes_kb |
|---|---|---|
| SalesOrderDetail_inmem | 5113761 | 663552 |
| DemoSalesOrderDetailSeed | 64 | 10368 |
| SpecialOffer_inmem | 2 | 8192 |
| SalesOrderHeader_inmem | 1575679 | 147456 |
| Product_inmem | 111 | 12032 |
| SpecialOfferProduct_inmem | 64 | 3712 |
| DemoSalesOrderHeaderSeed | 1984 | 5,504 |
Podemos ver um total de aproximadamente 6,5 GB de dados. O tamanho dos índices na tabela SalesOrderHeader_inmem e SalesOrderDetail_inmem é o mesmo que o tamanho dos índices antes de inserir os pedidos de vendas. O tamanho do índice não foi alterado porque ambas as tabelas estão usando índices de hash e os índices de hash são estáticos.
Após a redefinição de demonstração
O procedimento armazenado Demo.usp_DemoReset pode ser usado para redefinir a demonstração. Ele exclui os dados nas tabelas SalesOrderHeader_inmem e SalesOrderDetail_inmem e regarrega os dados das tabelas originais SalesOrderHeader e SalesOrderDetail.
Agora, mesmo que as linhas nas tabelas tenham sido excluídas, isso não significa que a memória seja recuperada imediatamente. O SQL Server recupera a memória das linhas excluídas em tabelas com otimização de memória em segundo plano, conforme necessário. Você verá que imediatamente após o reset de demonstração, sem carga de trabalho transacional no sistema, a memória das linhas excluídas ainda não foi recuperada:
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Veja aqui o conjunto de resultados.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Padrão | 2261 |
| MEMORYCLERK_XTP | DB_ID_5 | 7396 |
| MEMORYCLERK_XTP | Padrão | 0 |
| MEMORYCLERK_XTP | Padrão | 0 |
Isso é esperado: a memória é recuperada quando a carga de trabalho transacional está em execução.
Se você iniciar uma segunda execução da carga de trabalho de demonstração, verá a utilização da memória diminuir inicialmente, pois as linhas excluídas anteriormente serão limpas. Em algum momento, o tamanho da memória aumenta novamente até que a carga de trabalho seja concluída. Depois de inserir 10 milhões de linhas após a redefinição da demonstração, a utilização da memória é muito semelhante à utilização após a primeira execução. Por exemplo:
SELECT type,
name,
pages_kb / 1024 AS pages_MB
FROM sys.dm_os_memory_clerks
WHERE type LIKE '%xtp%';
Veja aqui o conjunto de resultados.
type |
name |
pages_MB |
|---|---|---|
| MEMORYCLERK_XTP | Padrão | 1,863 |
| MEMORYCLERK_XTP | DB_ID_5 | 7390 |
| MEMORYCLERK_XTP | Padrão | 0 |
| MEMORYCLERK_XTP | Padrão | 0 |
Utilização de disco para as tabelas com otimização de memória
O tamanho geral em disco para os arquivos de ponto de verificação de um banco de dados em determinado momento pode ser localizado usando a consulta:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups AS f
INNER JOIN sys.database_files AS df
ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';
Estado inicial
Quando o grupo de arquivos de exemplo e as tabelas com otimização de memória de exemplo são criados inicialmente, vários arquivos de ponto de verificação são pré-criados e o sistema começa a preencher os arquivos - o número de arquivos de ponto de verificação pré-criados depende do número de processadores lógicos no sistema. Como o exemplo é inicialmente muito pequeno, os arquivos pré-criados ficam vazios principalmente após a criação inicial.
O código a seguir mostra o tamanho inicial em disco para o exemplo em um computador com 16 processadores lógicos:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups AS f
INNER JOIN sys.database_files AS df
ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';
Veja aqui o conjunto de resultados.
| Tamanho em disco em MB |
|---|
| 2312 |
Como você pode ver, há uma grande discrepância entre o tamanho em disco dos arquivos de ponto de verificação, que é de 2,3 GB, e o tamanho real dos dados, que é mais próximo de 30 MB.
Levando em conta a origem da utilização do espaço em disco, você pode usar a seguinte consulta. O tamanho do disco retornado por esta consulta é aproximado para os arquivos com estado 5 (NECESSÁRIO PARA BACKUP/AD), 6 (EM TRANSIÇÃO PARA MARCA DE EXCLUSÃO) ou 7 (MARCA DE EXCLUSÃO).
SELECT state_desc,
file_type_desc,
COUNT(*) AS [count],
SUM(CASE WHEN state = 5 AND file_type = 0 THEN 128 * 1024 * 1024
WHEN state = 5 AND file_type = 1 THEN 8 * 1024 * 1024
WHEN state IN (6, 7) THEN 68 * 1024 * 1024
ELSE file_size_in_bytes END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
Para o estado inicial do exemplo, o resultado se parece com a tabela a seguir para um servidor com 16 processadores lógicos:
| state_desc | file_type_desc | count | tamanho em disco em MB |
|---|---|---|---|
| PRECREATED | DADOS | 16 | 2048 |
| PRECREATED | DELTA | 16 | 128 |
| EM CONSTRUÇÃO | DADOS | 1 | 128 |
| EM CONSTRUÇÃO | DELTA | 1 | 8 |
Como você pode ver, a maior parte do espaço é usado por dados e arquivos delta criados anteriormente. O SQL Server pré-criou um par de arquivos (dados, delta) por processador lógico. Além disso, os arquivos de dados são pré-dimensionados em 128 MB e arquivos delta a 8 MB, a fim de tornar a inserção de dados nesses arquivos mais eficiente.
Os dados reais nas tabelas com otimização de memória estão no único arquivo de dados.
Após executar a carga de trabalho
Após a execução de um único teste que insere 10 milhões de pedidos de vendas, o tamanho geral em disco tem a seguinte aparência (para um servidor de teste com 16 núcleos):
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups AS f
INNER JOIN sys.database_files AS df
ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';
Veja aqui o conjunto de resultados.
| Tamanho em disco em MB |
|---|
| 8828 |
O tamanho em disco é em torno de 9 GB, um valor próximo ao tamanho dos dados na memória.
Verificando melhor os tamanhos dos arquivos de ponto de verificação em vários estados:
SELECT state_desc,
file_type_desc,
COUNT(*) AS [count],
SUM(CASE WHEN state = 5 AND file_type = 0 THEN 128 * 1024 * 1024
WHEN state = 5 AND file_type = 1 THEN 8 * 1024 * 1024
WHEN state IN (6, 7) THEN 68 * 1024 * 1024
ELSE file_size_in_bytes END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
Veja aqui o conjunto de resultados.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DADOS | 16 | 2048 |
| PRECREATED | DELTA | 16 | 128 |
| EM CONSTRUÇÃO | DADOS | 1 | 128 |
| EM CONSTRUÇÃO | DELTA | 1 | 8 |
Ainda temos 16 pares de arquivos pré-criados, prontos para uso conforme os pontos de verificação são encerrados.
Há um par em construção, que é usado até que o ponto de verificação atual seja fechado. Junto com os arquivos de ponto de verificação ativos, isso representa em torno de 6,5 GB de utilização de disco para 6,5 GB de dados na memória. Lembre-se de que os índices não são persistidos no disco e, portanto, o tamanho geral no disco é menor do que o tamanho na memória nesse caso.
Após a redefinição de demonstração
Após a redefinição da demonstração, o espaço em disco não será recuperado imediatamente se não houver nenhuma carga de trabalho transacional no sistema e não houver pontos de verificação de banco de dados. Para que os arquivos de ponto de verificação passem por seus vários estágios e sejam eventualmente descartados, vários pontos de verificação e eventos de truncamento de log precisam ocorrer, a fim de iniciar a mesclagem de arquivos de ponto de verificação, bem como iniciar a coleta de lixo. Isso ocorrerá automaticamente se você tiver uma carga de trabalho transacional no sistema (e fizer backups de log regulares, caso esteja usando o modelo de recuperação FULL), mas não quando o sistema estiver ocioso, como em um cenário de demonstração.
No exemplo, após a redefinição da demonstração, você poderá ver algo como:
SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]
FROM sys.filegroups AS f
INNER JOIN sys.database_files AS df
ON f.data_space_id = df.data_space_id
WHERE f.type = N'FX';
Veja aqui o conjunto de resultados.
| Tamanho em disco em MB |
|---|
| 11839 |
Com quase 12 GB, isso é significativamente mais do que os 9 GB que tínhamos antes da redefinição de demonstração. Isso ocorre porque algumas mesclagens de arquivos de ponto de verificação foram iniciadas, mas alguns dos destinos de mesclagem ainda não foram instalados e alguns dos arquivos de origem de mesclagem ainda não foram limpos, como pode ser visto no exemplo a seguir:
SELECT state_desc,
file_type_desc,
COUNT(*) AS [count],
SUM(CASE WHEN state = 5 AND file_type = 0 THEN 128 * 1024 * 1024
WHEN state = 5 AND file_type = 1 THEN 8 * 1024 * 1024
WHEN state IN (6, 7) THEN 68 * 1024 * 1024
ELSE file_size_in_bytes END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
Veja aqui o conjunto de resultados.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DADOS | 16 | 2048 |
| PRECREATED | DELTA | 16 | 128 |
| ACTIVE | DADOS | 38 | 5152 |
| ACTIVE | DELTA | 38 | 1331 |
| DESTINO DE MESCLAGEM | DADOS | 7 | 896 |
| DESTINO DE MESCLAGEM | DELTA | 7 | 56 |
| ORIGEM MESCLADA | DADOS | 13 | 1,772 |
| ORIGEM MESCLADA | DELTA | 13 | 4:55 |
Os destinos de mesclagem serão instalados e a origem mesclada será limpa à medida que a atividade transacional ocorrer no sistema.
Após uma segunda execução da carga de trabalho de demonstração, inserindo 10 milhões de pedidos de vendas após a redefinição da demonstração, você verá que os arquivos construídos durante a primeira execução da carga de trabalho foram limpos. Se você executar a consulta anterior várias vezes enquanto a carga de trabalho estiver em execução, você poderá ver os arquivos de ponto de verificação percorrerem os vários estágios.
Após a segunda execução da carga de trabalho que insere 10 milhões de pedidos de vendas, você verá que a utilização do disco é muito semelhante, embora não necessariamente igual à que ocorreu após a primeira execução, pois o sistema é dinâmico por natureza. Por exemplo:
SELECT state_desc,
file_type_desc,
COUNT(*) AS [count],
SUM(CASE WHEN state = 5 AND file_type = 0 THEN 128 * 1024 * 1024
WHEN state = 5 AND file_type = 1 THEN 8 * 1024 * 1024
WHEN state IN (6, 7) THEN 68 * 1024 * 1024
ELSE file_size_in_bytes END) / 1024 / 1024 AS [on-disk size MB]
FROM sys.dm_db_xtp_checkpoint_files
GROUP BY state, state_desc, file_type, file_type_desc
ORDER BY state, file_type;
Veja aqui o conjunto de resultados.
state_desc |
file_type_desc |
count |
on-disk size MB |
|---|---|---|---|
| PRECREATED | DADOS | 16 | 2048 |
| PRECREATED | DELTA | 16 | 128 |
| EM CONSTRUÇÃO | DADOS | 2 | 268 |
| EM CONSTRUÇÃO | DELTA | 2 | 16 |
| ACTIVE | DADOS | 41 | 5608 |
| ACTIVE | DELTA | 41 | 328 |
Nesse caso, há dois pares de arquivos de ponto de verificação no estado UNDER CONSTRUCTION, o que significa que vários pares de arquivos foram movidos para o estado UNDER CONSTRUCTION, provavelmente devido ao alto nível de concorrência na carga de trabalho. Várias threads simultâneas requisitaram um novo par de arquivos ao mesmo tempo e, portanto, moveram um par de PRECREATED para UNDER CONSTRUCTION.