SQL Azure Federations na prática (pt-PT)

SQL Azure Federations na prática (pt-PT)


No artigo anterior introduzimos de forma teórica as SQL Azure Federations, vamos neste artigo realizar um pequeno exemplo prático e analisar algumas dicas de utilização das Federations nas nossas aplicações.

As SQL Azure Federations são a implementação do padrão de sharding.Sharding é um padrão que permite aumentar a escalabilidade e a performance de grandes bases de dados. Aplicar o padrão a uma base de dados significa “partir” essa base de dados em pedaços mais pequenos e distribui-los por vários servidores de modo a obter escalabilidade. A cada pedaço resultante chamamos de shard.

A base de dados raiz (root) poderá ter várias federações. Cada federação poderá ter vários membros (shards) e cada membro poderá ter várias unidades atómicas. A escalabilidade máxima é atingida quando todas as unidades atómicas estão contidas em apenas um membro da federação, ou seja, cada membro de uma federação tem apenas uma unidade atómica.

Cada membro da federação é suportado por uma instância de base de dados SQL Azure. Uma unidade atómica é a unidade mínima indivisível que poderemos ter dentro dos membros e representa uma instância da federation key (chave da federação).

Exemplo

Vamos assumir que o leitor já dispõe de uma instância de servidor SQL Azure. Caso não tenha pode muito facilmente aceder ao portal de Windows Azure em https://windows.azure.com/default.aspx e criar um novo servidor na sua subscrição. Não se esqueça de adicionar o seu endereço de IP actual nas regras da firewall.

Vamos agora estabelecer ligação à base de dados master do servidor para podemos criar a nossa base de dados de demonstração. Todos os servidores de SQL Azure dispõem de uma base de dados com o nome master. Esta base de dados é responsável, entre outras coisas, por armazenar os logins e permissões e é apenas sobre ela que podemos executar os comandos de CREATE, ALTER ou DROP de bases de dados e utilizadores. Como vamos criar uma nova base de dados para executar os exemplos deste artigo teremos que efectuar uma ligação a essa base de dados.

Neste exemplo estamos a utilizar um servidor de base de dados vazio pelo que a ligação irá ser estabelecida por omissão sobre a base de dados master. Caso o utilizador tenha já alguma base de dados criada no servidor deverá nas opções do SQL Server Management Studio (SSMS) indicar que pretende ligar à base de dados master sob a pena de a ligação ser criada sobre uma base de dados já existente e não conseguirá criar a nova.

Criar esta base de dados no portal seria muito mais fácil, bastava adicioná-la ao servidor pretendido mas, de forma a dar mais alguma experiência aos nossos leitores relativamente a ligações a base de dados em SQL Azure optou-se por explicar estes detalhes.

Para os menos experientes, alerta-se ainda para o facto de que o login terá que estar no formato <login>@<server> como pode verificar na imagem seguinte:

Feita a ligação vamos começar por a base de dados onde vamos criar as nossas federations através do comando CREATE DATABASE Demo. Após este passo, torna-se necessário mudar a ligação da base de dados master para a base de dados Demo que acabámos de criar.

Note que o comando USE DEMO irá produzir o erro USE statement is not supported to switch between databases. Use a new connection to connect to a different Database.” Sendo necessário desligar a ligação e voltar a ligar escolhendo como base de dados Demo ou simplesmente mudando a ligação através da lista de bases de dados disponíveis do SSMS.

Vamos agora criar o esquema.

No artigo de introdução falámos de dados centralizados. Dados centralizados são dados sobre os quais não existem muitas operações de leitura e escrita e que estão disponíveis e que apenas estão disponíveis numa única localização.

Neste exemplo vamos criar uma tabela Loja que irá ficar alojada apenas na nossa base de dados raiz sendo por isso considerado dados centralizados.

CREATE TABLE Loja(
LojaID INT PRIMARY KEY,
Nome NVARCHAR(100))

Até aqui nada de novo, vamos então dar inicio à criação da nossa primeira federação. Para tal iremos executar o seguinte comando:

CREATE FEDERATION ProdutosFed (ID BIGINT RANGE)

Como vimos no artigo de introdução, será criada uma nova base de dados para alojar esta federação e inicialmente todos os seus membros. Após a execução do comando anterior poderemos logo verificar que a nova base de dados já foi criada.

Antes de criar o schema da nossa federação teremos que mudar a ligação para a nova base de dados. Essa mudança de ligação faz-se através do comando:

USE FEDERATION ProdutosFed(ID = 0) WITH RESET, FILTERING=OFF

O comando USE FEDERATION é próprio do SQL Azure mas fornece ao utilizador uma experiência de utilização muito semelhante ao comando USE do SQL Server.

Caso o leitor se esteja a questionar o porquê do ID=0 , neste momento a federação tem apenas um membro pelo que qualquer ID será válido para realizar as operações pretendidas.

 Vamos agora criar o esquema das tabelas da federação:

CREATE TABLE Categoria(
CategoriaID INT PRIMARY KEY,
Categoria NVARCHAR(100))
 
CREATE TABLE Produto(
      ProdutoID BIGINT PRIMARY KEY,
      Nome NVARCHAR(200),
      Categoria INT REFERENCES Categoria(CategoriaID),
) FEDERATED ON (ID=ProdutoID)

A primeira tabela será uma tabela de dados de referência, que será replicada por todos os eventuais membros da federação, porque não tem o atributo FEDERATED ON. Em oposição, os dados da tabela Produto serão considerados dados particionados porque já dispõe do atributo FEDERATED ON.

Realizando algumas inserções nas tabelas poderemos verificar que neste momento ambas as tabelas contêm todos os dados.

INSERT INTO Categoria VALUES (1,'Frescos')
INSERT INTO Categoria VALUES (2,'Secos')
INSERT INTO Categoria VALUES (3,'Líquidos')
INSERT INTO Produto VALUES (10,'Iogurte',1)
INSERT INTO Produto VALUES (20,'Leite',1)
INSERT INTO Produto VALUES (110,'Massa',2)
INSERT INTO Produto VALUES (120,'Cerveja',3)
 
SELECT * FROM Categoria;
SELECT * FROM Produto;



Estamos agora em condições de realize o primeiro particionamento dos dados e iremos realizar essa operação através dos comandos:

USE FEDERATION Root WITH RESET
GO
ALTER FEDERATION ProdutosFed SPLIT AT(ID=100)

O primeiro comando serve para mudarmos a ligação para a base de dados raiz (Demo). Os comandos de SPLIT e DROP são sempre executados sobre a base de dados raiz porque é esta a base de dados que contém os metadados que suportam as operações.

O segundo comando é o comando que efectivamente ordena o particionamento dos dados.

Com vimos no artigo de introdução, o divisão de uma federation em duas resulta na criação de duas novas bases de dados e na cópia filtrada dos dados para as respectivas bases de dados novas. O tempo que esta operação demora é proporcional à dimensão dos dados a copiar mas a informação mais importante nesta fase será a de que não é possível iniciar novo particionamento dos dados sem que o anterior tenha terminado.

Para monitorizar a existência de algum processo de SPLIT a ocorrer podemos fazer uso dos metadados presentes na base de dados raiz:

select * from sys.dm_federation_operations

 

Após a operação de SPLIT estar completa termos agora os dados particionados em duas bases de dados. Para verificar essa situação vamos realizar algumas consultas e observar os resultados:

-- #1
USE FEDERATION ProdutosFed(ID = 10) WITH RESET, FILTERING=OFF
GO
SELECT * FROM Produto;
GO
 
-- #2
USE FEDERATION ProdutosFed(ID = 110) WITH RESET, FILTERING=OFF
GO
SELECT * FROM Produto;
GO
 
-- #3
USE FEDERATION ProdutosFed(ID = 110) WITH RESET, FILTERING=ON
GO
SELECT * FROM Produto;

 

O primeiro conjunto de comandos efectua uma ligação à 1ª parte dos dados, ou seja, todos os produtos com ID inferior a 100.

O segundo comando efectua uma ligação à 2ª parte dos dados (produtos com ID igual ou superior a 100).

O terceiro comando usa a opção FILTERING = ON de modo a que a ligação seja filtrada e desta forma só se consiga aceder aos dados do ID que foi passado como parâmetro. As aplicações multitenant poderão tirar muito partido desta opção.

Cada uma das bases de dados particionadas terá uma cópia completa da tabela Categoria dado que se tratam de dados de referência. As consultas a esta tabela são ainda independentes do ID e do FILTERING. Caso se pretenda alterar os dados presentes nesta tabela terá que ter em atenção que terá que realizar essa alteração em todos os membros da federação (em todas as bases de dados).

Como seria de esperar, temos neste momento 3 bases de dados neste servidor, uma base de dados raiz que suporta os dados centralizados e duas base de dados que suportam, no nosso exemplo, metade dos dados federados cada uma.

Já falámos nos metadados que estão presentes na base de dados raiz. Através de vistas sobre esses metadados poderemos retirar algumas informações tais como as federações presentes, as distribuições de cada federação, os membros que cada federação tem e os respectivos limites, etc.

SELECT * FROM sys.federations
 
SELECT * FROM sys.federation_distributions
 
SELECT * FROM sys.federation_member_distributions



 

Todas estas operações podem ser realizadas através do SQL Azure Management Portal mas, como já referimos, é também objectivo dar mais alguma sensibilidade ao utilizador sobre o que realmente se passa “lá dentro”.


Regras

Vamos agora verificar quais as regras para a correcta construção de uma base de dados com federações.

Criação da Federação

CREATE FEDERATION nome_federacao (nome_distribuicao <tipo> RANGE)

Para criar uma federação usamos o comando CREATE FEDERATION e além do nome da federação passamos 3 parâmetros, nome da distribuição, tipo de dados e tipo de distribuição.

O nome da federação e tem que ser único dentro de uma base de dados SQL Azure.

O nome da distribuição também é o identificados que iremos utilizar para referir a chave da federação quando, por exemplo, utilizarmos o comando CREATE TABLE … FEDERATED ON(...) ou o comando USE FEDERATION.

O tipo de dados terá que ser INT, BIGINT, UNIQUEIDENTIFIER ou VARBINARY(n) onde o n poderá ir até 900.

Neste momento RANGE é a única opção de particionamento disponível.

Federated Tables

As tabelas particionadas (federated tables) são as tabelas que têm os seus dados divididos por vários membros. Estas tabelas são criadas dentro das federações através da adição do atributo FEDERATED ON aquando da criação das mesmas.

CREATE TABLE Produto(…)FEDERATED ON (ID=ProdutoID)

É necessário passar como parâmetro no atributo qual a coluna desta tabela que representa a chave da federação. Relativamente e esta coluna temos as seguintes regras:

  • O tipo de dados desta coluna tem que ser obrigatoriamente igual ao tipo de dados da chave da federação.
  • Não pode ter valor nulo.
  • Não pode ser actualizada para valores fora do range do membro actual.
  • Não pode ser uma computed column.
  • Tem que fazer parte de todas as unique e clustered indexes.
  • Tem que fazer parte de todas as chaves estrangeiras para outras federated tables.

Sendo necessário indicar uma coluna como sendo a coluna que representa a chave da federação podemos concluir que todas as tabelas particionadas têm que conter a chave da federação. Esta regra implica que, em algumas tabelas, tenhamos que proceder a uma desnormalização do esquema.

Relativamente a esquema, temos ainda as seguintes regras:

  • Todas as chaves estrangeiras entre federated tables têm que conter a chave da federação.
  • Não podem existir reference tables com chaves estrangeiras para uma federated table.
  • Uma federated table pode ter chaves estrangeiras para reference tables sem restrições.

Os membros da federação não suportam:

  • Index Views
  • Colunas Identity
  • Tipo de dados timestamp e rowversion

Note ainda que não é obrigatório que os membros da federação tenham todos o mesmo esquema, cada membro pode ter o “seu” esquema desde que repeite as regras. Só poderão ser realizadas alteração ao esquema de um membro através de ligações sem filtro (FILTERING=OFF).

Reference Tables

São tabelas sobre os quais apenas existem operações de leitura (as escritas são possíveis mas raras) e que por questões de performance estão disponíveis em todos os membros da federação. Os seus dados são copiados integralmente durante as operações de SPLIT.

Estas tabelas são criadas dentro dos membros da federação através de CREATE TABLE sem o atributo FEDERATED ON. No exemplo anterior a tabela Categoria é uma reference table.

Em relação a restrições tenha em atenção que não podem existir chaves estrangeiras para tabelas particionadas (federated tables) e que as limitações relativamente a identity e timestamp se mantêm também para reference tables.

Só poderão ser realizadas alteração a dados ou ao esquema de uma referece table através de ligações sem filtro (FILTERING=OFF).

Central Tables

As tabelas centrais são tabelas que existem apenas na base de dados raiz. São tabelas sobre as quais não existem muitas operações de leitura ou escrita, normalmente são tabelas de metadados ou configurações.

Como não são tabelas que fazem parte das operações de SPLIT não se aplicam as restrições que vimos para os outros tipos de tabelas. Note apenas que os dados destas tabelas não estão visíveis quando estamos conectados a um membro de uma federação. Só poderemos aceder a estes dados através de uma ligação directa à base de dados raiz.

Utilização nas aplicações

Podemos dividir a utilização de SQL Azure Federations em dois casos.

O primeiro e mais simples, acontece quando a carga de trabalho incide sobre uma unidade atómica da federação. Neste caso basta apenas executar o comando USE FEDERATION para estabelecer a ligação ao membro correcto e a partir daí podemos fazer uma utilização como se de uma base de dados tradicional se tratasse.

USE FEDERATION ProdutosFed(ID = 1) WITH RESET, FILTERING=ON

No caso em que é necessária a participação de dados de mais do que um membro da federação o comportamento já terá que ser diferente. Neste momento as fan out queries não são suportadas pelo que teremos que realizar estas consultas individualmente a todos os membros da federação e agregar os resultados na aplicação.

Mas mesmo neste caso a resolução do problema não é muito complicada. Podemos tirar partido da vista sys.federation_member_distributions para obter a lista de membros da federação e quais os seus limites inferiores e superiores.

Note que o limite máximo superior é representado por NULL. Tenha em conta ainda que é apenas na tabela raiz que consegue obter todos os membros e as suas distribuições. Em cada membro da federação esta vista também está disponível mas tem apenas resultados para o próprio membro.

LINQ

Como vimos anteriormente terá que ser emitido um USE FEDERATION antes da primeira consulta à base de dados. Em LINQ para SQL existe um pormenor, temos que abrir a ligação antes de emitir o USE FEDERATION.

using (DataClasses1DataContext db = new DataClasses1DataContext())
{
   db.Connection.Open();
   db.ExecuteCommand("USE FEDERATION ProdutosFed(ID = 110) WITH RESET, FILTERING=OFF");
   var lista = from p in db.Produtos select new { ID = p.ProdutoID, Produto = p.Nome };
   dataGridView1.DataSource = lista;
}

 

Entity Framework

A utilização em Entity Framework tem o mesmo requisito, teremos que abrir a ligação antes de emitir o USE FEDERATION.

using (DemoEntities db = new DemoEntities())
{
   db.Connection.Open();
   string federationCmdText = @"USE FEDERATION ProdutosFed(ID = 110) WITH RESET, FILTERING=OFF";
   db.ExecuteStoreCommand(federationCmdText);
 
   var lista = from p in db.Produto select new { ID = p.ProdutoID, Produto = p.Nome };
   dataGridView1.DataSource = lista;
}

Tem ainda outro requisito, as SQL Azure Federations não suportam MARS (Multiple Active Result Sets). Devido a esta situação teremos que modificar a connection string com multipleactiveresultsets=False;

<add name="DemoEntities" connectionString="(…) multipleactiveresultsets=False;” (…) />

 A inexistência de suporte a MARS (Multiple Active Result Sets) é um assunto importante que iremos ver mais à frente.

Entity Framework Code First

Em Entity Framework Code First a abertura da ligação é feita de forma ligeiramente diferente porque ObjectContext utiliza um tipo de ligação diferente.

((IObjectContextAdapter)db).ObjectContext.Connection.Open();

 

Transacções

Relativamente a transacções temos que ter ainda em conta que o TransactionScope só deverá ser instanciado depois de emitido o USE FEDERATION de modo a que a transacção seja estabelecida sobre o membro da federação.

using (DemoEntities db = new DemoEntities())
{
   db.Connection.Open();
   string federationCmdText = @"USE FEDERATION ProdutosFed(ID = 110) WITH RESET, FILTERING=OFF";
   using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew))
   {
      //(...)
   }
}

 

Multiple Active Result Sets (MARS) 

Actualmente não existe suporte para Multiple Active Result Sets em SQL Azure Federations.

Isto significa que não poderemos usar algumas características que os ORMs (Object-relational mappers) nos fornecem actualmente e que são suportadas pelo MARS. São elas lazy loading, LoadProperty() e Load().

 

Em alternativa deveremos fazer eager loading. Lazy loading trata-se de apenas inicializar os objectos no momento em que estes são necessários. Eager loading é o contrário de lazy loading, ou seja, iniciar os objectos no momento em que são criados.

Em LINQ podemos fazer eager loading através do método DataLoadOptions.LoadWith<T>() de modo a que todos os resultados sejam obtidos da base de dados no momento da instanciação.

DataLoadOptions dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Produto>(c => c.Categoria);              
db.LoadOptions = dataLoadOptions;

Em Entity Framework, o eager loading é realizado através do método Include:

var produtos = from p in db.Produto.Include("Categoria1")  select p;

Outra opção é utilizar o método ToList() ou ToArray() antes de um ciclo de modo a que os resultados possam ser logo trazidos para memória no momento da construção. Neste caso teremos que ter a opção LazyLoading activa.

var produtos = (from p in db.Produto select p).ToList();
foreach (Produto produto in produtos)
{
   Categoria categoria = produto.Categoria1;
}

Conclusão

Esperamos que com este artigo o leitor tenha aprofundado mais os seus conhecimentos de SQL Azure Federations e que se sinta mais confiante na hora de abordar o uso desta tecnologia.

Referências

Your Data in the Cloud - http://blogs.msdn.com/b/cbiyikoglu/

Windows Azure Customer Advisory Team - http://windowsazurecat.com/

MSDN Library - http://msdn.microsoft.com/en-us/library/windowsazure/gg619386.aspx

joomla visitors
Leave a Comment
  • Please add 3 and 7 and type the answer here:
  • Post
Wiki - Revision Comment List(Revision Comment)
Sort by: Published Date | Most Recent | Most Useful
Comments
  • Fernando Lugão Veltem edited Revision 2. Comment: adicionado toc

  • Vitor Tomaz edited Original. Comment: Hidden statistics added

Page 1 of 1 (2 items)
Wikis - Comment List
Sort by: Published Date | Most Recent | Most Useful
Posting comments is temporarily disabled until 10:00am PST on Saturday, December 14th. Thank you for your patience.
Comments
  • Vitor Tomaz edited Original. Comment: Hidden statistics added

  • Fernando Lugão Veltem edited Revision 2. Comment: adicionado toc

Page 1 of 1 (2 items)