Interpreted Stored Procedure Vs. Natively Stored Procedure

Posted on abril 3, 2014

4


 

Introdução

A cada dia fica mais transparente que a necessidade de performance e escalabilidade é necessário para qualquer banco de dados de alta performance. Com o lançamento do Hekaton diversos recursos dentro de sua engine faz com que ele seja o recurso preparado para trabalhar em ambientes de perfomance crítica aonde se procura, melhorias de concorrência (Lock-FREE), escalabilidade, Trroughput…..

A integração do Hekaton com a Engine Tradicional do SQL Server é feita por um recurso chamado Query Interop. Ele é o responsável por toda a integração do Hekaton, desde Backup do Banco de Dados, AlwaysOn, a JOINS com tablelas que não estão In-Memory chamadas de Disk-Based Tables, o papel do Query Interop é primordial.

Inside The Engine

Otey SQL2449 Fig 1

(Figura 1 – Parte do SQLOS – Query Interop)

Com isso após uma consulta realizada ser executada – T-SQL Query Execution a mesma passa pelo – Query Interop para depois chegar ao Banco de Dados In-Memory. Porém existe uma outra forma mais eficiente de buscar dados dentro de tabelas In-Memory. Podendo ganhar de 10% a 300% em operações DML. Conseguimos isso passando pelo In-Memory Native Compiler, e isso é possível quando temos Tabelas In-Memory com Stored Procedures In-Memory chamadas de Natively Stored Procedure.

Natively Stored Procedure

Nesse modelo as procedures são traduzidas em C e compiladas em assembly DDL, o  resultado disso é uma efficiência gigantesca para a execução do código em código nativo. Para que nós possamos nos beneficiar desse recurso, o mesmo só pode acessar tabelas In-Memory dentro do banco de dados que possua o FILEGROUP que possibilite a utilização do Hekaton.  Uma outra grande vantagem é que as Interpreted Stored Procedures (In-Disk) são compiladas somente quando são executadas pela primeira vez, diferente das Natively Stored Procedure que já são compiladas durante sua criação. Para mais informações recomendo a documentação do MSDN está excelente – http://msdn.microsoft.com/en-us/library/dn133186(v=sql.120).aspx

Interpreted Stored Procedure Vs. Natively Stored Procedure

Agora iremos comparar as Interpreted Stored Procedures Vs. Natively Stored Procedure. Temos duas tabelas estruturalmente iguais, porém uma tabela In-Memory e a outra tabela In-Disk.

———————————————————————-

–[dbo].[ondsk_Atendimentos] –Disk-Based

———————————————————————-

             DROP TABLE [dbo].[ondsk_Atendimentos]

CREATE TABLE [dbo].[ondsk_Atendimentos]

(

                [ID] [bigint] NOT NULL,

                [NumeroAtendimento] BIGINT NOT NULL INDEX idxNCL_NumeroAtendimento,

                [NomePosto] [varchar](50) NOT NULL,

                [StatusPosto] [char](15) NOT NULL,

                [FormaAtendimento] [varchar](40) NOT NULL,

                [BaixaAtendimento] [varchar](80) NOT NULL,

                [StatusAtendimento] [varchar](30) NOT NULL,

                [ValorPagoCliente] [numeric](14, 2) NOT NULL,

                [ValorRecuperadoPosto] [numeric](14, 2) NOT NULL,

                [DataCadastro] [datetime] NOT NULL INDEX idxNCL_DataCadastro

) ON [PRIMARY]

GO

 

ALTER TABLE [dbo].[ondsk_Atendimentos]

ADD CONSTRAINT PK_ondsk_Atendimentos_ID

PRIMARY KEY (ID)

 

———————————————————————-

–[dbo].[inmem_Atendimentos] –In-Memory Based

———————————————————————-

             DROP TABLE [dbo].[inmem_Atendimentos]

CREATE TABLE [dbo].[inmem_Atendimentos]

(

                [ID] [bigint] NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000),

                [NumeroAtendimento] BIGINT NOT NULL INDEX idxNCL_NumeroAtendimento NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000),

                [NomePosto] [varchar](50) NOT NULL,

                [StatusPosto] [char](15) NOT NULL,

                [FormaAtendimento] [varchar](40) NOT NULL,

                [BaixaAtendimento] [varchar](80) NOT NULL,

                [StatusAtendimento] [varchar](30) NOT NULL,

                [ValorPagoCliente] [numeric](14, 2) NOT NULL,

                [ValorRecuperadoPosto] [numeric](14, 2) NOT NULL,

                [DataCadastro] [datetime] NOT NULL INDEX idxNCL_DataCadastro NONCLUSTERED

) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)

GO

 

 

Após a criação da tabelas iremos realizar a Criação das Procedures

  • Interpreted Stored Procedure de Nome – [dbo].[proc_Insere_ondsk_Atendimentos] irá realizar a inserção na tabela On-Disk – dbo.ondsk_Atendimentos
  • Nativelty Stored Procedure de Nome – [dbo].[proc_Insere_inmem_Atendimentos] irá realizar a inserção na tabela In-Memory – dbo.inmem_Atendimentos

———————————————————————-

–[dbo].[proc_Insere_ondsk_Atendimentos]

–Interpreted Stored Procedure – Disk-Based

———————————————————————-

             DROP PROCEDURE [dbo].[proc_Insere_ondsk_Atendimentos]

CREATE PROCEDURE [dbo].[proc_Insere_ondsk_Atendimentos]

                @QuantidadeRegistros BIGINT

AS

BEGIN

 

DECLARE @PontoParada BIGINT

 

SELECT @PontoParada = ISNULL(MAX(ID),0)

FROM [dbo].[ondsk_Atendimentos]

 

INSERT INTO [dbo].[ondsk_Atendimentos]

SELECT TOP (@QuantidadeRegistros) ID, NumeroAtendimento, NomePosto, StatusPosto, FormaAtendimento, BaixaAtendimento,

                   StatusAtendimento, ValorPagoCliente, ValorPagoCliente, DataCadastro

FROM [dbo].[ondsk_DadosAtendimentoClientes]

WHERE ID > @PontoParada

 

END

 

———————————————————————-

–[dbo].[proc_Insere_inmem_Atendimentos]

–Natively Stored Procedure – In-Memory-Based

———————————————————————-

             DROP PROCEDURE [dbo].[proc_Insere_inmem_Atendimentos]

 

CREATE PROCEDURE [dbo].[proc_Insere_inmem_Atendimentos]

                @QuantidadeRegistros BIGINT

WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER

AS

BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N’us_english’)

BEGIN

 

DECLARE @PontoParada BIGINT

 

SELECT @PontoParada = ISNULL(MAX(ID),0)

FROM [dbo].[inmem_Atendimentos]

 

INSERT INTO [dbo].[inmem_Atendimentos]

SELECT TOP (@QuantidadeRegistros) ID, NumeroAtendimento, NomePosto, StatusPosto, FormaAtendimento, BaixaAtendimento,

                   StatusAtendimento, ValorPagoCliente, ValorPagoCliente, DataCadastro

FROM [dbo].[inmem_DadosAtendimentoClientes]

WHERE ID > @PontoParada

 

END

END

 

Agora iremos realizar a inserção de 1 Milhão de Registros dentro das Tabelas, lembrando que uma tabela On-Disk e outra tabela In-Memory

EXEC [dbo].[proc_Insere_ondsk_Atendimentos] 1000000

 

go

EXEC [dbo].[proc_Insere_inmem_Atendimentos] 1000000

Informações da Inserção da Tabela On-Disk

SQL Server parse and compile time:

   CPU time = 0 ms, elapsed time = 0 ms.

 

 SQL Server Execution Times:

   CPU time = 0 ms,  elapsed time = 0 ms.

SQL Server parse and compile time:

   CPU time = 0 ms, elapsed time = 0 ms.

SQL Server parse and compile time:

   CPU time = 0 ms, elapsed time = 8 ms.

Table ‘ondsk_Atendimentos’. Scan count 1, logical reads 3, physical reads 0, readahead reads 0, lob logical reads 0, lob physical reads 0, lob readahead reads 0.

 

(1 row(s) affected)

 

 SQL Server Execution Times:

   CPU time = 15 ms,  elapsed time = 6 ms.

Table ‘ondsk_DadosAtendimentoClientes’. Scan count 5, logical reads 17068, physical reads 0, readahead reads 20999, lob logical reads 0, lob physical reads 0, lob readahead reads 0.

Table ‘ondsk_Atendimentos’. Scan count 0, logical reads 9112166, physical reads 4, readahead reads 0, lob logical reads 0, lob physical reads 0, lob readahead reads 0.

Table ‘Worktable’. Scan count 2, logical reads 2941829, physical reads 0, readahead reads 0, lob logical reads 0, lob physical reads 0, lob readahead reads 0.

 

(1000000 row(s) affected)

 

(1 row(s) affected)

 

 SQL Server Execution Times:

   CPU time = 26750 ms,  elapsed time = 33528 ms.

 

 SQL Server Execution Times:

   CPU time = 26765 ms,  elapsed time = 33543 ms.

SQL Server parse and compile time:

   CPU time = 0 ms, elapsed time = 7 ms.

 

 SQL Server Execution Times:

   CPU time = 0 ms,  elapsed time = 0 ms.

Informações da Inserção da Tabela In-Memory

SQL Server parse and compile time:

   CPU time = 0 ms, elapsed time = 0 ms.

 

 SQL Server Execution Times:

   CPU time = 5109 ms,  elapsed time = 5700 ms.

 

 

Tempo Total de Inserção Interpreted Stored Procedure On-Disk Table = 00:35 segs

Tempo Total de Inserção Natively Stored Procedure In-Memory = 00:09 segs

image

(Figura 2 – Tempo de Inserção das Procedures.)

A porcentagem de ganho é de mais de 350 % por somente colocar a tabela In-Memory e colocar o mesmo código da procedure On-Disk para a procedure In-Memory. O ganho é muito grande porque essa execução não passa pelo Query Interop como temos a tabela In-Memory e a Procedure In-Memory utilizamos o canal mais rápido para a comunicação entre os meios chamado den In-Memory Natively Compiler.

 

Cenários de Utilização

As Natively Stored Procedures são utilizadas para cenários de alta performance, as mesmas se beneficiam de agregações, Nested Loops Joins, Expressões complexas, lógicas procedurais, condicionais e loops ou seja uma ótima recomendação é que não somente escreva o mesmo código na transição da sua procedure, mas se possível reescreva a lógica de sua procedure In-Memory para utilizar o que citei acima e assim o ganho será ainda maior.

Uma boa recomendação é a utilização do – Native Compilation Advisor. Esse recurso possibilita de forma gráfica a ajuda em passos para a migração de sua procedure In-Disk para sua procedure In-Memory.

Screen Shot 2014-04-03 at 4.43.59 PM

(Figura 3 – Native Compilation Advisor.)