Fernando Correia

Otimizando o pool de conexões com SQL Azure Federations

In Dicas on 16 mar 2012 at 23:21

No Windows Azure, para aplicações que requerem banco de dados relacional, a melhor alternativa é o SQL Azure, que é um banco de dados como serviço com alta disponibilidade e tolerância a falhas.

Ao desenvolver um serviço na nuvem que atenda vários clientes, um desafio importante para o arquiteto da aplicação é encontrar o equilíbrio ideal entre compartilhamento de recursos e isolamento entre os clientes. Encontrar a melhor arquitetura é uma tarefa complexa devido à quantidade de requisitos e variáveis.

Alguns fatores podem não ser percebidos à primeira vista. Um deles, ao usar bancos de dados, é a possibilidade de incorrer em fragmentação do pool de conexões, o que irá causar uma degradação significativa na performance ao se tentar escalar a aplicação além de uns poucos clientes.

Uma aplicação hospedada na nuvem que siga uma arquitetura tradicional, herdada da modalidade de venda ou locação de licença de uso, poderá ser provisionada criando um ambiente dedicado para cada cliente, como nesse diagrama simplificado:

fragmentacao_ambiente_por_cliente

Esse modelo tem como característica um alto grau de isolamento entre os inquilinos. Isto transmite mais segurança quanto à proteção de acesso a dados, evita a possibilidade de comprometimento do desempenho por conta de utilização excessiva por outros cliente, facilita a customização das aplicações, a adequação dos recursos à demanda individual de cada cliente e a apuração de custos. Por outro lado, este cenário não se benificia plenamente da computação em nuvem, na medida em que o compartilhamento de recursos é limitado (feito apenas a nível de infraestrutura). Recursos ociosos de um cliente não serão utilizados para aumentar o desempenho do atendimento a outros. Outra característica é que este modelo não otimiza os custos de operação, pois haverão vários ambientes a serem monitorados, escalados individualmente e atualizados. Não é o modelo mais eficiente em termos de custos, mas pode ser adequado quando cada cliente individualmente necessita de uma quantidade razoável de recursos e quando os clientes estão dispostos a pagar o custo adicional para ter a vantagem do isolamento.

Uma segunda abordagem para tentar otimizar os custos seria consolidar em um único conjunto todos os servidores Web e de aplicação. Este conjunto de servidores atenderia de forma compartilhada a todos os clientes. Porém, os dados de cada cliente continuariam em um banco de dados separado. Como neste diagrama:

fragmentacao_web_compartilhada

Este modelo é mais eficiente pois os recursos de servidores web e servidores de aplicativos são compartilhados. Quando um cliente tiver uma demanda maior, haverá mais servidores para atendê-lo do que se ele tivesse um ambiente dedicado. Quando a demanda do cliente for menor, os recursos estarão liberados para outros clientes. O comprometimento de desempenho pode ser evitado através de uma arquitetura distribuída e pela elasticidade (adicionando mais servidores ao conjunto quando o tempo de resposta estiver se aproximando do limite máximo do SLA). Outra vantagem é que os dados de cada cliente continuam isolados em bancos de dados separados, o que permite um gerenciamento individualizado, inclusive para restauração de backup, e evita que um cliente comprometa o desempenho de banco de dados de outro cliente.

A deficiência deste modelo é a quantidade de conexões de banco de dados que serão necessárias. Como as requisições são distribuídas pelo balanceador de carga entre todos os servidores web, as conexões de um cliente tenderão a se distribuir por vários servidores. Cada um desses servidores estabelecerá uma conexão com o banco de dados do cliente. Estas conexões ficarão no pool de conexões do .NET com a finalidade de serem reaproveitadas, já que estabelecer uma conexão é uma operação “cara” (demorada). O problema é que cada servidor web terá várias conexões ativas simultaneamente (uma para cada requisição sendo processada de forma concorrente), e uma grande quantidade de conexões no pool. Como cada nova requisição web pode ir para qualquer servidor, a tendência será de um baixo índice de reaproveitamento das conexões que, após um tempo de inatividade, serão liberadas. Isto implicará em uma grande incidência de reconexões, o que irá degradar o desempenho da aplicação. Os servidores de banco de dados, por sua vez, também terão um grande número de conexões abertas (várias conexões para cada servidor web), o que irá degradar a sua performance e potencialmente causar throttling. A degradação de performance causada por este excesso de conexões é descrita no documento SQL Server Connection Pooling (item “Pool Fragmentation”). Em resumo, esta abordagem não é escalável. Para números moderados de clientes, como alguns milhares, a quantidade de conexões será inviável.

A solução criada pela Microsoft para resolver este dilema é o SQL Azure Federations. Cihan Biyikoglu, Program Manager no SQL Azure, explica como o Federations muda o cenário no artigo Connection Pool Fragmentation: Use Federations and you won’t need to learn about these nasty problems that come with sharding!

fragmentacao_sql_azure_federations

O SQL Azure Federations proporciona escalabilidade horizontal também para a camada de banco de dados. Um conjunto de banco de dados compõe uma única “federação”. O grande diferencial do SQL Azure Federations é que a conexão de banco de dados é feita com a federação; um comando USE FEDERATION altera uma propriedade desta conexão, direcionando-a para um servidor específico (o servidor “membro da federação” onde estão os dados do cliente).

O pool de conexões dos servidores web terá um altíssimo grau de reaproveitamento. Todas as conexões serão sempre para a mesma string de conexão, independente de qual seja o servidor web ou qual seja o cliente da requisição.  As conexões nos bancos de dados também são otimizadas.

Note que usando o SQL Azure Federations ainda é possível ter um banco de dados dedicado para cada cliente, se é isto que se deseja. Também é possível compartilhar um banco de dados entre vários clientes. Neste caso, usa-se o monitoramento da federação para subdividir um banco de dados quando seu uso se tornar muito intenso, preservando assim o SLA de desempenho.

Cihan Biyikoglu resume: as palavras mágicas “USE FEDERATION” acabam com o problema da fragmentação do pool de conexões.

Encontrar a arquitetura mais adequada para uma aplicação na nuvem é uma tarefa delicada. Vários fatores precisam ser pesados. É importante ter clareza sobre os requisitos. O modelo de negócio exige o máximo de eficiência? Neste caso o compartilhamento de recursos será o caminho. O gerenciamento individualizado por cliente é fundamental? Neste caso, busca-se o isolamento. Encontrar o meio-termo é bastante complexo, como podemos ver nesta análise dos problemas de desempenho que podem ser causados pela fragmentação do pool de conexões.

Como você vê esta questão? Quais outros modelos podem ser adotados, para diferentes cenários? Continue a conversa nos comentários abaixo ou pelo Twitter.