Aproveitando a deixa de ontem, segue aqui a recomendação de mais um guia interessante - SQL Server 2008 White Paper: Analysis Services Performance Guide. O guia pode ser encontrado no endereço http://www.microsoft.com/downloads/details.aspx?FamilyID=3be0488d-e7aa-4078-a050-ae39912d2e43&DisplayLang=en e para aqueles que trabalham com o SSAS, é leitura obrigatória!
Nota: diferente do guia de atualização, esse somente possui 80 páginas, então pode ser lido rapidinho! :-)
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Para o SQL Server 2005 nós tivemos acesso a um guia EXCELENTE sobre atualização de versões e, para nos ajudar, este guia foi atualizado para o SQL Server 2008:
SQL Server 2008 Upgrade Technical Reference Guide
http://www.microsoft.com/downloads/details.aspx?FamilyID=66d3e6f5-6902-4fdd-af75-9975aea5bea7&displaylang=en
Show de bola, não é? Aí você que chegou até aqui, vai acessar o link, baixar o material, abrir o documento no Word e, quando vê que ele possui 486 páginas, vai colocá-lo em uma pasta e, provavelmente, vai esquecer que ele existe ... tsc tsc tsc!
Existe muita gente que vê uma atualização da versão do banco de dados como uma dor, sempre, mas eu digo que na maioria das vezes isso está relacionado com uma falta de preparação adequada. Se você imagina que somente fazer um detach e um attach de um banco de dados no servidor atualizado, é uma migração de sucesso, pense de novo. É necessário analisar seu banco de dados e suas aplicações, realizar testes de carga e de funcionalidade, para verificar se algo vai quebrar ou não. Feito isso, se prepare e escreva (com carinho) para seu ambiente um guia de atualização, com tudo o que têm que fazer no dia D, quais são as verificações pós-upgrade, etc.
Dito isso, você não precisa ler o guia inteiro, mas sim ler aqueles trechos que lhe dizem respeito e varrer os outros tópicos, procurando alguma coisa que você não sabia que poderia impactar seu ambiente. É claro, que esses problemas não previstos, poderiam em sua maioria ser notados através de uma bateria de testes eficientes.
Com isso você vai ficar 100% livre de qualquer problema? Pode ser que não! Alguns cenários de estresse mais complexos e outras variáveis que podem surgir em produção, eventualmente trazem alguns imprevistos, mas com uma preparação adequada para a atualização, você estará diminuindo (e muito!) a chance de passar algumas madrugadas acordado e com o seu chefe no pé. :-)
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Muito se têm falado do Entity Framework e aqui em Brasília não é diferente, onde eu tenho trabalhado com alguns clientes, discutindo sobre a melhor forma se utilizar o EF nos projetos. Dentro das restrições de tempo (algo que está mais do que apertado ultimamente) eu juntei algumas idéias que tive e gostaria de compartilhar com vocês, um pouquinho de cada vez.
Definição do problema: a empresa R2D2 vai utilizar o Entity Framework em um novo projeto (ponto para ela!) e está montando uma arquitetura de referência. Dentre os requisitos, eles querem tentar garantir ao máximo que os desenvolvedores programem de acordo com os padrões definidos e utilizem métodos de uma interface específica (para validação de negócio) junto com o Entity Framework.
Pensando nisso, podemos criar diversas maneiras para tentar garantir ao máximo que essas regras serão seguidas, como utilização de templates, geradores de código, modificação do gerador do Entity Framework, etc. Eu resolvi usar uma abordagem mais direta, com o uso de interfaces e, alguns subterfúgios, para que sejam feitas algumas validações das entidades.
Quer ver no que deu? Continue lendo o artigo e baixe o projeto de referência, anexado a esta postagem.
Solução 01 do brainstorm
(feedbacks são mais do que bem vindos!!)
Devemos criar uma biblioteca de classes “EFUtil” com classes utilitárias que ofereçam serviços interessantes para o desenvolvedor. Dessa forma ele ficará compelido a usar os recursos disponibilizados e nós poderemos fazer alguns controles através desses métodos.
Detalhamento do projeto EFUtil
Classe PoliticasEF - Oferece métodos responsáveis pela verificação das políticas de desenvolvimento definidas pelo time de arquitetos, como por exemplo: toda entidade deve implementar a interface IRegraNegocio.
Interface IRegraNegocio - Interface que faz parte do EFUtil e define o método ValidaRegraNegocio, que deve ser implementado por toda entidade definida no modelo conceitual. Na versão 1.0 do EF precisamos usar uma interface, pois por padrão toda entidade já deriva de EntityObject. Quem sabe no futuro vamos usar uma classe abstrata e fazer com que as entidade derivem desta…
Classe ServicoContexto – Responsável por manter os métodos relacionados com os contextos do EF, como por exemplo, o método SalvarAlteracoes. Este seria responsável por chamar o método SaveChanges, fazer a validação das políticas, logging e, em caso de problemas de concorrência, aplicar a política definida para o projeto.
Partindo do que foi dito acima, para essa proposta todo desenvolvedor seria orientado para fazer o seguinte:
- Implementar a interface IRegraNegocio em uma classe parcial para todas as entidades definidas no seu modelo conceitual.
- Criar uma classe parcial para o contexto e implementar o método parcial OnContextCreated().
No exemplo que tenho, no projeto de acesso a dados “EFDataAccess”, criei um modelo edmx baseado no banco de dados SimpleDB (que somente possui a tabela funcionário) e implementei as regras definidas acima…
public partial class SimpleDBEntities
{
partial void OnContextCreated()
{
EFUtil.PoliticasEF.VerificaEntidades(this);
}
}
public partial class Funcionario: EFUtil.IRegraNegocio
{
#region IRegraNegocio Members
public void ValidaRegraNegocio()
{
throw new Exception("Um erro qualquer de validação de negócio...");
}
#endregion
}
Vamos ver agora como implementei as verificações e validações para tentar evitar que o desenvolvedor fugisse à regra…
O método VerificaEntidades analisa o espaço conceitual do modelo informado pesquisando todos os tipos “entidade” e, como estamos falando da entidade conceitual, não temos informação das interfaces que as classes parciais implementam, então precisamos usar reflexão. Dessa forma, para cada entidade recuperada, verificamos se o tipo recuperado pelo método GetType implementa a interface EFUtil.IRegraNegocio. Utilizo uma coleção de dicionário para verificar quais contextos já foram verificados, fazendo este controle através do hashcode do objeto.
public static void VerificaEntidades(ObjectContext contexto)
{
string nomeAssembly, nomeEntidade;
Type tipoEntidade;
Assembly AssemblyDA = Assembly.GetCallingAssembly();
// Mostra o nome do Assembly - EFDataAccess neste caso
nomeAssembly = AssemblyDA.GetName().Name;
var entidades = from e in contexto.MetadataWorkspace.GetItems(System.Data.Metadata.Edm.DataSpace.CSpace)
where e.BuiltInTypeKind == System.Data.Metadata.Edm.BuiltInTypeKind.EntityType
select e;
foreach (var e in entidades)
{
// Recupera o nome da entidade listada, ignorando o namespace do modelo conceitual, que normalmente é
// diferente do namespace definido no assembly
nomeEntidade = e.ToString().Split('.')[1];
tipoEntidade = AssemblyDA.GetType(nomeAssembly + "." + nomeEntidade, true);
if (tipoEntidade.GetInterface("EFUtil.IRegraNegocio") == null)
throw new Exception("Entidade '" + nomeEntidade + "' não está implementando a interface EFUtil.IRegraNegocio");
}
objetosVerificados.Add(contexto.GetHashCode(), DateTime.Now);
}
Obs 1: É necessário compormos o nome do tipo conforme exibido, porque o namespace do modelo conceitual é diferente do namespace primitivo do assembly, que por padrão é o nome do projeto. Por simplicidade eu estou considerando namespaces no formato namespace.entidade, mas é claro que isso pode ser diferente e deverá ser tratado de forma genérica, recuperando o último nome após o “.”.
Obs 2: Como estamos chamando o método VerificaEntidades através de uma classe parcial, é obrigatório que essa classe esteja na mesma DLL que o modelo, então eu posso assumir que o GetCallingAssembly está sempre se referindo ao módulo correto.
Para tentar aumentar a segurança e evitar que um desenvolvedor tente usufruir dos serviços disponibilizados pela classe ServicoContexto sem chamar o VerificaEntidades, no início do método SalvarAlteracoes() é feito uma validação, que pesquisa no dicionário se o objeto deste contexto já foi verificado.
internal static void JaFoiVerificado(ObjectContext contexto)
{
if (!objetosVerificados.ContainsKey(contexto.GetHashCode()))
throw new Exception("A validação do contexto não foi feita, verifique se existe uma classe parcial " +
"do contexto implementando o seguinte trecho de código: \n" +
"partial void OnContextCreated() { \n" +
"EFUtil.PoliticasEF.VerificaEntidades(this); \n" +
"}");
}
É também no SalvarAlteracoes que as regras de negócio de cada entidade são invocadas, de acordo com o código abaixo (note que por simplicidade eu somente estou verificando os itens que foram adicionados):
private static void ValidaRegras(ObjectContext contexto)
{
foreach (ObjectStateEntry item in contexto.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
{
IRegraNegocio entidade = (IRegraNegocio)item.Entity;
entidade.ValidaRegraNegocio();
}
}
Conclusão e considerações
Com o exemplo acima eu não espero ter resolvido essa questão, muito pelo contrário, espero que essa postagem seja o início de uma discussão produtiva. Gostaria de fazer algumas considerações sobre o que foi codificado:
- Se o desenvolvedor utilizar o método SalvarAlteracoes sem ter implementado corretamente o método parcial OnContextCreated, o JaFoiVerificado pega o problema.
- Se o desenvolvedor implementar corretamente a classe parcial do contexto, mas esquecer de implementar em alguma entidade a interface IRegraNegocio, o problema será detectado pelo VerificaEntidades.
- Se o desenvolvedor não implementar a interface e chamar o SaveChanges do contexto diretamente, não podemos fazer nada (com essa abordagem).
- Comentando a interface e o método que chama o VerificaEntidades, vocês podem simular os cenário acima.
- Fazer a validação da regras auxiliares (e até poder chamar outros métodos) em toda instância pode parecer um desperdício, mas talvez seja possível parametrizar algumas coisas e até criar novas sobrecargas para os construtores do contexto, que pode trazer uma flexibilidade interessante se pensarmos na configuração do contexto em subsistemas específicos.
Pessoal, notem que eu não quero com esse post entrar em detalhes qual a melhor arquitetura para sua aplicação, aonde é melhor colocar sua regra de negócio ou outra afirmação do tipo. Estou apenas mostrando algumas maneiras de como podemos utilizar o Entity Framework, que espero eu, possa ajudá-los no dia-a-dia.
Em anexo está tudo o que foi codificado, além de um outro modelo do northwind com mais entidades. Espero que vocês testem o projeto e tudo funcione corretamente, pois estou esperando o feedback de vocês. :-)
Em breve voltarei com adendos e este projeto, como controle de concorrência e logging…
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Attachment(s): EFBrainstorm.zip
PessoAll, tudo bem?
Recebi diversos e-mails relatando problemas com as gravações dos meus últimos webcasts sobre Entity Framework e Filestream. As gravações realmente estão com problema e eu estou vendo internamente se existe uma maneira de corrigirmos os vídeos. Caso a resposta seja negativa, eu pretendo gravar até o fim do ano pequenos vídeos com as demonstrações e disponibilizá-las no media center do MSDN Brasil.
Obrigado pelo interesse de todos nas gravações e me desculpe pelo transtorno.
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Muita gente fica confusa quando começa a ver a quantidade de opções com hotfixes para o SQL Server, sem entender o que deve aplicar e quando fazer. Para facilitar o entendimento do processo de criação e publicação dos fixes, a Microsoft escreveu um KB muito interessante: An Incremental Servicing Model is available from the SQL Server team to deliver hotfixes for reported problems (http://support.microsoft.com/kb/935897). O KB explica como os fixes são organizados e como as atualizações acumulativas em intervalos definidos ajudam o profissional a ter uma maior previsibilidade para aplicação das correções em seus ambientes. Dito isso, não fique esperando só pelos service packs, aplique os pacotes cumulativos com frequência.
Você pode acompanhar as novidades dos pacotes para o SQL Server através do blog: http://blogs.msdn.com/sqlreleaseservices
Em tempo, já está disponível o CTP do SP3 para o SQL Server 2005: http://www.microsoft.com/downloads/details.aspx?FamilyId=D22317E1-BC64-4936-A14B-7A632B50A4CA&displaylang=en.
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Guia
Aproveitando que eu estou brincando um pouco com o Sharepoint, vou deixar aqui uma boa referência vinda do grupo de patterns & practices: Sharepoint Guide (http://msdn.microsoft.com/en-us/library/dd203468.aspx).
Column lookup
Depois de montar minha VPC com o MOSS 2007, estava criando umas listagens para testes, principalmente para ver questões de relacionamento entre listas, exibições, etc. Partindo do básico que toda listagem já possui uma coluna ID (que não é exibida por padrão), criei novas colunas referenciando outras listas, pois assim conseguia criar um novo item relacionando uma atividade existente (vinda de outra tabela) com um outro dado qualquer, por exemplo.
A dúvida surgiu quando vi que era possível fazer para o campo nomeAtividade (antigo Title), duas ligações: nomeAtividade e nomeAtividade (link to item). Qual seria a diferença? Será que está na garantia da integridade relacional? Comecei a fazer alguns testes, confiando no meu intuito, mas não conseguia ver nenhuma diferença clara. Recorri então ao Live e como resultado da pesquisa encontrei este link: http://www.sharepointkings.com/2008/07/sharepoint-column-lookup-information.html.
Pelo que vi, em duas referências distintas, a diferença entre as opções está na navegabilidade do site.
- Quando você coloca a opção link to item e navega para o item em questão (uma atividade específica, por exemplo) partindo da listagem corrente, quando você clica em close é remetido para a listagem de onde partiu.
- Quando você coloca a opção SEM o link to item e navega para o item em questão (uma atividade específica, por exemplo) partindo da listagem corrente, quando você clica em close é exibida a página de atividades (RootFolder = *), pois você estava vendo o detalhamento de uma atividade.
Será que é só isso a diferença? Pesquisei mais um pouquinho e não achei nada diferente. Alguém sabe de outra diferença para nos contar? :-)
Se essa opção somente existir para isso, acho que é muita confusão para pouco benefício e, quem sabe, deveria ser feito de outra forma.
De qualquer maneira, fica uma dica. Acredito que o Sharepoint é uma ótima aposta para quem está trabalhando com nossa plataforma e, cada vez mais, outros produtos estão sendo construídos para “rodarem” sobre ele. Com essa complexidade inerente do agrupamento de tecnologias, muitos problemas podem surgir (p.ex.: quem está causando este gargalo?), aí alguém que sabia fazer um troubleshooting bem feito e conheça a arquitetura do produto, vai estar bem cotado no mercado… Concorda?
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Recebi um questionamento de um cliente e minha resposta acabou tendo um desdobramento curioso que compartilho com vocês.
Problema e solução
A pergunta era mais ou menos assim: "Eu tenho um serviço que expõe um tipo complexo e quero alterar o minOccurs de um elemento para ser minOccurs=1. Porém o WSDL não está gerando o que eu estou pedindo, mesmo utilizando o atributo XMLElement na definição da propriedade".
Para ajudá-lo eu montei um projeto simples que expõe um serviço e defini um método que recebe como parâmetro o tipo complexo InfoPapel, que possui algumas propriedades, entre elas a seguinte (mesmos atributos informados pelo cliente, a classe eu inventei):
[XmlElement(IsNullable = false)]
[DataMember]
public string NomePapel
{
get { return nomePapel; }
set { nomePapel = value; }
}
Essa definição irá gerar a seguinte definição de esquema:
<xs:complexType name="InfoPapel">
<xs:sequence>
<xs:element minOccurs="0" name="NomePapel" nillable="true" type="xs:string" />
<xs:element name="Empresa" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
O problema aqui está na utilização do XmlElement ao invés de utilizar a propriedade IsRequired do atributo DataMember. Para entendimento, o WCF possui três serializadores: XMLSerializer, DataContractSerializer, e NetDataContractSerializer (http://msdn.microsoft.com/en-us/magazine/cc163569.aspx), sendo que por padrão é utilizado o DataContractSerializer. Utilizando o padrão, o serializador vai ignorar o atributo XmlElement, não gerando um WSDL diferente, como esperado.
Se alterarmos nosso código para utilizar as propriedade do DataMember, poderemos controlar efetivamente o esquema gerado, conforme vemos abaixo:
[DataMember(IsRequired = true, Order = 1)]
public string NomePapel
{
get { return nomePapel; }
set { nomePapel = value; }
}
<xs:complexType name="InfoPapel">
<xs:sequence>
<xs:element name="NomePapel" nillable="true" type="xs:string" />
<xs:element name="Empresa" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
Veja que não temos mais o minOccurs="0" na definição do esquema e, sabendo que por padrão o minOccurs é igual a 1 (especificação do XSD), conseguimos o resultado esperado, que era um minOccurs="1".
XmlSerializerFormat
Se você quer utilizar o serializador XML (utilizado pelo ASP.NET web services), precisa colocar junto ao atributo ServiceContract , definido na interface, outro atributo: XmlSerializerFormat. Utilizando esse atributo, quando o WCF for gerar o WSDL e XSDs relacionados, ele irá levar em conta o atributo XmlElement e ignorar o DataMember.
O que me chamou a atenção foi a ausência da propriedade IsNullable no atributo DataMember e a ausência da propriedade IsRequired no atributo XmlElement.
minOccurs vs. nillable
Se dermos uma olhada na especificação do XML Schema Definition (XSD) em http://www.w3.org/TR/xmlschema-0/, temos uma explicação clara das diferenças: em uma definição de esquema, um elemento pode aparecer de X a Y vezes, sendo controlado pelo minOccurs e maxOccurs, enquanto o nillable define um elemento que aparece no XML mas não possui nenhum valor, utilizando o atributo xsi:nil="true" no elemento. Note que dependendo do objetivo do XML que você está manipulando, pode existir diferenças semânticas na ausência de um elemento (minOccurs = 0) e no aparecimento de um elemento marcado como nulo. E aí?
Quando utilizamos o XmlElement e XmlSerializerFormat, conseguimos dois formatos de saída:
Quando “IsNullable = false” o atributo nillable desaparece e minOccurs = 0.
<xs:element minOccurs="0" maxOccurs="1" name="NomePapel" type="xs:string" />
Quando “IsNullable = true” o atributo nillable aparece e minOccurs = 1!
<xs:element minOccurs="1" maxOccurs="1" name="NomePapel" nillable="true" type="xs:string" />
No primeiro caso ou o elemento não aparece ou ele aparece com um valor definido. Note porém que no segundo caso, como o nulo deverá ser informado no XML como um atributo do elemento, o XSD está definindo o elemento com minOccurs=1, isto é, se existe a possibilidade dele ser nulo, o elemento têm que estar presente.
Já na utiliação do DataContractSerializer com o DataMember, conseguimos obter duas definições diferentes utilizando ou não o IsRequired:
<xs:element minOccurs="0" name="NomePapel" nillable="true" type="xs:string" /> (IsRequired = false)
<xs:element name="NomePapel" nillable="true" type="xs:string" /> (IsRequired = true)
Note que aqui não temos a definição do elemento tipo string para não aceitar nulos, por se tratar de um tipo por referência. Por outro lado, conseguimos ter um minOccurs="0" com nillable="true", o que não consegui através do XmlElement.
Não vi uma maneira fácil (nem sei é possível e se faz sentido) do DataMember definir um elemento string como nillable="false". Alguém sabe?
Finalizando...
Além do lado geek do post e da explicação dos serializers e atributos, fica uma pergunta: porque eu mencionei essa história do minOccurs e nillable? Esse post surgiu num contexto de interoperabilidade e o preciosismo pode até parecer inútil, mas eu acredito que quanto mais conhecermos os detalhes do que está acontecendo com nossos serviços, mais fácil será de resolvermos um eventual problema de interop. Vai que a gente cruza com um gerador de proxies mais enjoadinho...
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Se você quer saber mais sobre o Visual Studio Team System 2008 e Application Lifecycle Management (ALM), participe de treinamentos disponíveis em várias cidades pelo brasil. Alguns dos assuntos abordados são:
Situação da Indústria de Software Mundial Desafios do Desenvolvimento e Manutenção de Software Colaboração entre testadores, desenvolvedores, arquitetos e analistas de requisito Visão Geral sobre o Visual Studio 2008 Team System Qualidade e Controle do Processo de Desenvolvimento Demonstração prática dos Visual Studio Team System Gostou? Então acesse http://www.msdnbrasil.com.br/conhecavsts/ e veja o calendário de eventos para a sua cidade.
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
No último post nós conversamos sobre a utilização do recurso de Filestream no SQL Server de acordo com o tamanho dos arquivos. Agora eu vou demonstrar para vocês o que pode acontecer quando vocês estão manipulando um arquivo grande e o acessam através de instruções T-SQL, ao invés de usar a API do Win32 para fazer essa manipulação.
1 - Criando o banco de dados e o arquivo
Para nossa experiência, vamos criar um banco de dados chamado FileStreamDB e adicionar um arquivo, que deve ficar com aproximandamente 256 MB após as atualizações.
(Listagem 0)
CREATE DATABASE FileStreamDB ON PRIMARY
( NAME = FileStreamDB_data,
FILENAME = N'C:\Temp\Filestream\DBs\FileStreamDB_data.mdf',
SIZE = 10MB,
MAXSIZE = 50MB,
FILEGROWTH = 15%),
FILEGROUP FileStreamGroup1 CONTAINS FILESTREAM
( NAME = FileStreamDB_CVs,
FILENAME = N'C:\Temp\Filestream\DBs\CVs')
LOG ON
( NAME = 'FileStreamDB_log',
FILENAME = N'C:\Temp\Filestream\DBs\FileStreamDB_log.ldf',
SIZE = 5MB,
MAXSIZE = 25MB,
FILEGROWTH = 5MB);
USE FileStreamDB
go
IF EXISTS (SELECT * FROM sys.objects WHERE name = N'CurriculumVitae')
DROP TABLE CurriculumVitae
GO
CREATE TABLE CurriculumVitae
(Codigo uniqueidentifier NOT NULL PRIMARY KEY DEFAULT NEWSEQUENTIALID() ROWGUIDCOL,
Nome VARCHAR(255) NOT NULL,
CV VARBINARY(MAX) FILESTREAM)
GO
INSERT INTO CurriculumVitae (Nome, CV) VALUES ('Luciano Caixeta Moreira',
CAST(REPLICATE('BLABLABLA BLABLABLABLA BLABLA BLA BLABLABLA', 1000) AS VARBINARY(MAX)))
DECLARE @i INT = 0
WHILE @i < 15
BEGIN
UPDATE CurriculumVitae
SET CV = CV + CV
SET @i = @i + 1
END
Com o circo montado, vamos para a nossa análise.
2 - Analisando a utilização de memória
Primeiramente vamos reiniciar a instância do SQL Server, pois queremos que o buffer pool não tenha muita memória comitada, somente reservada. Após a re-inicialização, limpamos os buffers de dados e vemos a situação dos objetos em memória (listagem 1).
(Listagem 1)
DBCC DROPCLEANBUFFERS
GO
select * from sys.dm_os_buffer_descriptors
/*
database_id file_id page_id page_level allocation_unit_id page_type row_count free_space_in_bytes is_modified numa_node
----------- ----------- ----------- ----------- -------------------- ------------------------------------------------------------ ----------- ------------------- ----------- -----------
2 1 150 0 844424932360192 INDEX_PAGE 57 5574 1 0
2 1 132 0 562949957025792 INDEX_PAGE 322 1978 1 0
6 1 31 0 196608 DATA_PAGE 102 1568 0 0
6 1 252 1 327680 INDEX_PAGE 3 8045 0 0
2 1 101 0 281474978611200 DATA_PAGE 131 4035 1 0
6 1 237 0 72057594039042048 INDEX_PAGE 1 8042 0 0
2 1 157 1 562949956108288 INDEX_PAGE 5 7916 1 0
6 1 7433 0 281474978349056 DATA_PAGE 3 7841 1 0
... (Removido por simplicidade) ...
6 1 235 0 72057594038976512 DATA_PAGE 1 7972 0 0
2 1 167 0 562949956960256 INDEX_PAGE 165 764 1 0
2 1 155 0 562949956108288 IAM_PAGE 2 6 1 0
2 1 78 0 562949956108288 INDEX_PAGE 121 3876 1 0
2 1 29 0 281474978283520 DATA_PAGE 2 7226 1 0
6 1 25 0 281474977955840 DATA_PAGE 2 8038 0 0
(73 row(s) affected)
*/
Notamos aqui que somente temos 73 entradas, vindas de diferentes páginas espalhadas pelos bancos de dados.
Para monitorar a utilização da memória distribuída pelos memory clerks, utilizo o código da listagem 2 que nos permitirá consultar a mudança nas alocações durante a execução de um procedimento específico. Neste caso nós somente queremos analisar um tipo, mas fica aqui o script como referência.
(Listagem 2)
SELECT
[Type],
SUM(single_pages_kb) AS sum_single_pages_kb,
SUM(multi_pages_kb) AS sum_multi_pages_kb,
MAX(virtual_memory_reserved_kb) AS max_virtual_memory_reserved_kb,
MAX(virtual_memory_committed_kb) AS max_virtual_memory_committed_kb,
GETDATE() as DataHora
INTO MemoryClerks
FROM sys.dm_os_memory_clerks
GROUP BY [type]
while 1=1
begin
INSERT INTO MemoryClerks
SELECT
[Type],
SUM(single_pages_kb) AS sum_single_pages_kb,
SUM(multi_pages_kb) AS sum_multi_pages_kb,
MAX(virtual_memory_reserved_kb) AS max_virtual_memory_reserved_kb,
MAX(virtual_memory_committed_kb) AS max_virtual_memory_committed_kb,
GETDATE() as DataHora
FROM sys.dm_os_memory_clerks
GROUP BY [type]
waitfor delay '00:00:05'
END
Após disparado o script da listagem 2, que ficará rodando até que seja interrompido por um comando de cancelamento da execução, em outra conexão execute a consulta (listagem 3) que vai trazer os dados do arquivo que foi criado.
(Listagem 3)
SELECT Codigo, Nome, CAST(CV as varchar(MAX)) AS CV, CV.PathName() AS Arquivo
FROM CurriculumVitae AS C
Você pode deixar os scripts rodando até que a consulta seja finalizada ou interrompê-los no meio da execução, desde que já tenha decorrido um tempo suficiente para o SQL Server começar a processar a consulta e capturar algumas variações nos memory clerks. Como resultado da execução completa da consulta (dados abaixo), podemos realizar novas pesquisas (listagem 4) para nos trazerem informações sobre o estado da memória e mudanças capturadas na tabela MemoryClerks.
(Listagem 4)
select * from sys.dm_os_buffer_descriptors
/*
33226 registros são retornados!
database_id file_id page_id page_level allocation_unit_id page_type row_count free_space_in_bytes is_modified numa_node
----------- ----------- ----------- ----------- -------------------- ------------------------------------------------------------ ----------- ------------------- ----------- -----------
2 1 26137 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 12684 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 847 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 20983 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 7533 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 29297 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 15870 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
4 1 1390 0 72057594057981952 INDEX_PAGE 1 7959 0 0
2 1 24193 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 10719 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 32500 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 19058 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 5593 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
2 1 14979 0 71916856550359040 TEXT_MIX_PAGE 1 40 0 0
... (Excluído por simplicidade) ...
*/
*/
DBCC TRACEON (3604)
DBCC PAGE (2,1,14979,2)
/*
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
PAGE: (1:14979)
BUFFER:
BUF @0x04097F5C
bpage = 0x0D19A000 bhash = 0x00000000 bpageno = (1:14979)
bdbid = 2 breferences = 0 bUse1 = 35893
bstat = 0xc00009 blog = 0x321bc bnext = 0x00000000
PAGE HEADER:
Page @0x0D19A000
m_pageId = (1:14979) m_headerVersion = 1 m_type = 3
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x8020
m_objId (AllocUnitId.idObj) = -2147483636 m_indexId (AllocUnitId.idInd) = 255
Metadata: AllocUnitId = 71916856550359040 Metadata: PartitionId = 0
Metadata: IndexId = -1 Metadata: ObjectId = 0 m_prevPage = (0:0)
m_nextPage = (0:0) pminlen = 0 m_slotCnt = 1
m_freeCnt = 40 m_freeData = 8150 m_reservedCnt = 0
m_lsn = (24:178:3) m_xactReserved = 0 m_xdesId = (0:0)
m_ghostRecCnt = 0 m_tornBits = 0
Allocation Status
GAM (1:2) = NOT ALLOCATED SGAM (1:3) = NOT ALLOCATED PFS (1:8088) = 0x4 100_PCT_FULL
DIFF (1:6) = NOT CHANGED ML (1:7) = NOT MIN_LOGGED
DATA:
Memory Dump @0x6432C000
6432C000: 01030000 2080ff00 00000000 00000000 †.... .ÿ.........
6432C010: 00000000 00000100 0c000080 2800d61f †............(.Ö.
6432C020: 833a0000 01000000 18000000 b2000000 †:..........²...
6432C030: 03000000 00000000 00000000 00000000 †................
6432C040: 01000000 00000000 00000000 00000000 †................
6432C050: 00000000 00000000 00000000 00000000 †................
6432C060: 0800761f 0000d107 00000000 03004142 †..v...Ñ.......AB
6432C070: 4c41424c 4120424c 41424c41 424c4142 †LABLA BLABLABLAB
6432C080: 4c412042 4c41424c 4120424c 4120424c †LA BLABLA BLA BL
6432C090: 41424c41 424c4142 4c41424c 41424c41 †ABLABLABLABLABLA
6432C0A0: 20424c41 424c4142 4c41424c 4120424c † BLABLABLABLA BL
6432C0B0: 41424c41 20424c41 20424c41 424c4142 †ABLA BLA BLABLAB
6432C0C0: 4c41424c 41424c41 424c4120 424c4142 †LABLABLABLA BLAB
6432C0D0: 4c41424c 41424c41 20424c41 424c4120 †LABLABLA BLABLA
6432C0E0: 424c4120 424c4142 4c41424c 41424c41 †BLA BLABLABLABLA
6432C0F0: 424c4142 4c412042 4c41424c 41424c41 †BLABLA BLABLABLA
6432C100: 424c4120 424c4142 4c412042 4c412042 †BLA BLABLA BLA B
...... (Excluído por simplicidade) .....
6432DF90: 4c41424c 4120424c 41424c41 20424c41 †LABLA BLABLA BLA
6432DFA0: 20424c41 424c4142 4c41424c 41424c41 † BLABLABLABLABLA
6432DFB0: 424c4120 424c4142 4c41424c 41424c41 †BLA BLABLABLABLA
6432DFC0: 20424c41 424c4120 424c4120 424c4142 † BLABLA BLA BLAB
6432DFD0: 4c41424c 41420000 00000000 00000000 †LABLAB..........
6432DFE0: 00000000 00000000 00000000 00000000 †................
6432DFF0: 00000000 00000000 00000000 00006000 †..............`.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
*/
SELECT
max_virtual_memory_reserved_kb,
max_virtual_memory_committed_kb,
DataHora
FROM MemoryClerks
WHERE [TYPE] = 'MEMORYCLERK_SQLBUFFERPOOL'
/*
max_virtual_memory_reserved_kb max_virtual_memory_committed_kb DataHora
------------------------------ ------------------------------- -----------------------
1654368 37480 2008-11-11 10:22:39.290
1654368 37480 2008-11-11 10:22:22.967
1654368 37480 2008-11-11 10:22:44.320
1654368 45216 2008-11-11 10:22:49.373
1654368 84228 2008-11-11 10:22:54.400
1654368 127652 2008-11-11 10:22:59.420
1654368 172100 2008-11-11 10:23:04.420
1654368 215012 2008-11-11 10:23:09.467
1654368 262020 2008-11-11 10:23:14.503
1654368 281656 2008-11-11 10:23:19.540
1654368 282168 2008-11-11 10:23:24.563
1654368 282168 2008-11-11 10:23:29.577
1654368 282168 2008-11-11 10:23:34.610
1654368 282168 2008-11-11 10:23:39.640
1654368 282168 2008-11-11 10:23:44.663
1654368 282168 2008-11-11 10:23:49.673
1654368 282168 2008-11-11 10:23:54.717
(17 row(s) affected)
*/
Sinistro! Mas intuitivo...
Podemos ver pela análise dos buffers descriptors que agora temos um grande volume de páginas do tipo TEXT_MIX_PAGE armazenadas na TempDB (database_id = 2). Isto é, o SQL Server está utilizando a TempDB para armazenar os dados vindos do nosso arquivo. Isso pode ser verificado quando analisamos uma página qualquer que foi listada (14979 no exemplo) e conseguimos ver uma série de "BLA BLA BLA"s.
Nota: "3 - text mix page. A text page that holds small chunks of LOB values plus internal parts of text tree. These can be shared between LOB values in the same partition of an index or heap" (http://www.sqlskills.com/blogs/paul/post/Inside-the-Storage-Engine-Anatomy-of-a-page.aspx).
E o que aconteceu com a memória que o SQL Server? Podemos notar pela análise do tipo MEMORYCLERK_SQLBUFFERPOOL que o SQL Server havia reservado os tradicionais 1.6GB (2GB - memtoleave e espaço para thread stack) e somente havia comitado 37.480 KB. Quando executamos nossa consulta para recuperar os dados do arquivo, notamos que o total de memória comitada começa a aumentar até estabilizar em 282.168 KB... Levando em conta que o arquivo possui aproximadamente 256 MB, o resultado não é mera coincidência! O SQL Server está lotando nossa memória com páginas vindas do arquivo, que estão sendo armazenadas na TempDB!
Então não importa se você está armazenando seus dados utilizando o filestream, se você está utilizando uma instrução T-SQL para recuperar o dado (seja no management studio ou na sua aplicação), a informação vai passar pela memória do SQL Server, concorrendo com outras páginas de dados e possivelmente colocando o lazy writer para trabalhar. Quando estiver manipulando esses arquivos grandes, use a classe SqlFileStream e deixe que os dados fluam através do streaming do NTFS.
Conclusão: entenda como o SQL Server trabalha com o Filestream e as diferenças ao acessar o arquivo através de T-SQL ou Win32.
PS: Em anexo está o script para vocês brincarem...
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Attachment(s): Filestream_MemoryUsage.zip
Antes de começar, vamos ao whitepaper... Enquanto eu estava escrevendo um artigo sobre Filestream (quando for publicado eu aviso a todos) o Paul Randal havia anunciado que em breve seria publicado um whitepaper sobre o assunto. Recentemente ele saiu do forno com o título: FILESTREAM Storage in SQL Server 2008 (http://msdn.microsoft.com/en-us/library/cc949109.aspx).
No meu último post sobre o recurso Filestream no SQL Server, eu recebi um comentário bem interessante. Ao invés de responder lá mesmo (onde quase ninguém vê), resolvi escrever um post sobre o assunto.
Comentário: "Fiquei muito animado quando descobri o recurso de filestream no SQL Server 2008, porém fiquei com a pulga atrás da orelha quando vi uma documentação do SQL Server (que a esta altura não vou lembrar onde foi) afirmando que para blobs muito grandes ainda seria mais vantajoso utilizar um campo blob do que um filestream.
Isso me deixou na dúvida de até onde vale a pena usar filestream ou não..."
Particularmente eu desconheço a afirmação sobre o filestream ser ruim para blobs muito grandes. Mas quando falamos sobre blobs bem pequenos, aí a observação faz todo sentido. Explico:
No eterno debate de quando armazenar o seu arquivo binário no banco de dados ou colocá-lo no sistema de arquivos e manter uma referência no banco de dados, os profissionais normalmente deveriam levar em conta aspectos como manutenção, implantação e desempenho. Para ajudar neste último quesito, um artigo do Jim Gray e Cia. servia de embasamento: To BLOB or Not To BLOB: Large Object Storage in a Database or a Filesystem? (http://research.microsoft.com/research/pubs/view.aspx?msr_tr_id=MSR-TR-2006-45). Em linhas gerais o artigo diz que para arquivos menores que 256 KB é mais eficiente armazená-los no banco de dados, enquanto arquivos maiores de 1 MB é melhor utilizar o sistema de arquivos. Arquivos entre esses valores caem em uma zona cinzenta e ainda existem outras considerações, como fragmentação dos dados, leitura e escrita.
Para comprovar que o artigo estava certo, podemos ver no whitepaper do Paul a figura 1, que mostra um ganho de performance do filestream com arquivos maiores de 1MB.
Então devemos levar em conta se usamos ou não o recurso do filestream com arquivos bem pequenos, lembrando que junto com o filestream você ainda têm outros ganhos de manutenção e implantação do seu banco de dados, que podem valer mais do que essa diferença de desempenho, certo?
Agora, se você estiver usando o recurso de Filestream e não acessar os seus dados da melhor maneira, pode pagar um preço alto na utilização da memória... Mas isso fica para um próximo post que já está saindo do forno!
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Bom dia pessoal.
Como muitos de vocês já devem saber, o MSDN Brasil oferece um programa de treinamento completo sobre o SQL Server e outras tecnologias, chamado MSDN Experience (http://www.microsoft.com/brasil/msdn/experience/default.mspx).
Após a publicação dos vídeos de SQL Server, ficamos com a pendência da publicação das perguntas sobre cada uma das sessões, que podem ser respondidas ao fim de cada módulo. As perguntas dos primeiros módulos já foram publicadas e as questões dos três módulos seguintes já estão em processo de publicação e estarão disponíveis muito em breve para todos.
Se você já tinha visto os vídeos, volte ao MSDN Experience e responda as questões.
Se você ainda não conhece esse programa e quer aprender sobre o SQL Server, acesse: http://www.msdnbrasil.com.br/experience/sqlserver/Home.aspx.
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Bom, já têm um tempinho que nós vemos um enfoque maior no Entity Framework que no Linq to SQL, mas o que será que os times de produto estavam pensando? Ontem tivemos um anúncio importante feito pelo Tim Mallalieu (http://blogs.msdn.com/adonet/archive/2008/10/29/update-on-linq-to-sql-and-linq-to-entities-roadmap.aspx). O trecho mais importante é:
"We’re making significant investments in the Entity Framework such that as of .NET 4.0 the Entity Framework will be our recommended data access solution for LINQ to relational scenarios. We are listening to customers regarding LINQ to SQL and will continue to evolve the product based on feedback we receive from the community as well."
Got it? :-)
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Quem trabalha com desenvolvimento utilizando tecnologias Microsoft sabe que os últimos anos não tem sido fáceis... O motivo? A empresa está oferecendo tantos, mas tantos recursos para nós, que é fácil nos perdermos em nossos estudos, querendo conhecer todas essas tecnologias... Eu não reclamo! É melhor ter diversos recursos disponíveis e poder escolher aquele que se adequa à necessidade do meu cliente (e não o inverso). Agora, que aumenta a pressão para nos mantermos atualizado, ah, isso aumenta.
E para completar, estamos vendo no PDC uma transformação (que já foi muito anunciada) na maneira em que nós desenvolvemos software. Temos que aprender a lidar com isso e entender essa tendência, que vai muito além da utilização da nuvem, está na maneira que concebemos nossa aplicação, na possibilidade de interação e garantir a experiência do usuário, em cada vez mais conseguir interagir com outros elementos do cotidiano ao nosso redor, em sermos penetrantes e, ao mesmo tempo, não intrusivos. É um grande desafio, que vêm acompanhado de uma janela de oportunidades.
Como fazer para não ser engolido por esse tsunami? Desde já fique atento ao que está acontecendo, veja as novidades que estão por vir e comece a entender as mudanças antes que elas aconteçam. Blogs e mais blogs já trazem a novidade (ontem meus RSSS feeds trouxeram 31 posts com a palavra PDC):
http://blogs.msdn.com/wcamb/archive/2008/10/28/plataforma-de-servi-os-azure-sql-data-services-sds.aspx
http://blogs.msdn.com/danielsf/archive/2008/10/28/pdc-2008-novidades-das-tecnologias-microsoft.aspx
http://blogs.msdn.com/jpclementi/archive/2008/10/28/windows-7-pre-beta-est-no-ar.aspx
http://blogs.msdn.com/otavio/archive/2008/10/28/explicando-um-pouco-mais-do-azure-e-pdc-hoje.aspx
http://blogs.technet.com/dbordini/archive/2008/10/28/pdc-2008-windows-azure.aspx
Azure, claro, na ponta da língua do pessoal!
Além dos keynotes que você pode assistir no site www.microsoftpdc.com, que tal ver outras sessões interessantíssimas que estão sendo gravadas por lá? Acesse http://channel9.msdn.com/Media/Videos/, procure aquelas com a imagem "view SESSION video" e não fique de fora do PDC.
Ontem eu assisti a sessão The Future of C# com o impressionante Anders Hejlsberg (http://channel9.msdn.com/pdc2008/TL16/). Muito interessante ver novas funcionalidades e a integração que teremos com aspectos dinâmicos no C#, o que no início me pareceu ser um pouco distorcido, acabou se mostrando como mais um potencial da linguagem que poderá ser bem explorado pelos programadores. Isso demonstra claramente o que Eric Lippert comentou no seu post de ontem (http://blogs.msdn.com/ericlippert/archive/2008/10/28/the-future-of-c-part-two.aspx) "Our goal is to make a compelling and practical language for general-purpose application development on our platforms, and thereby enable our customers to be successful." - Assino em baixo! E o futuro do futuro que o Anders mostra no fim da apresentação... Bem empolgante.
Dentro dessa leva de novidades, algumas boas notícias acabam ficando ofuscadas, mas claro que não podemos deixar de citá-las aqui.
http://blogs.msdn.com/velocity/archive/2008/10/28/announcing-ctp2-of-microsoft-project-code-name-velocity.aspx
http://blogs.msdn.com/sync/archive/2008/10/28/annoucing-sync-framework-v2-ctp1.aspx
Realmente, estamos presenciando uma época bem interessante...
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================
Já é conhecido do público que o Professional Developers Conference deste ano vai anunciar muitas novidades (já está fazendo isso) que poderemos esperar da Microsoft, principalmente quando falamos de cloud computing. Tudo isso começou ontem, com uma general session onde falaram muito do Windows Azure.
O Windows Azure é uma fundação baseada em serviços para a infra-estrutura e aplicações na nuvem. Um ambiente que abstrai a implantação e o gerenciamento de aplicações altamente escaláveis. Aconselho dar uma olhada nos posts do nosso amigo JP (http://blogs.msdn.com/jpclementi/archive/2008/10/27/lan-amento-do-windows-azure-no-pdc-2008.aspx) e Waldemir (http://blogs.msdn.com/wcamb/archive/2008/10/27/windows-azure-uma-plataforma-de-servi-os-na-nuvem.aspx).
Quem quiser saber mais novidades que serão anunciadas, acompanhe as sessões de keynote ao vivo: www.microsoftpdc.com. Lembrando que o horário da GS de hoje é 8:30 PST.
Dica: com o horário de verão, PST está 5 horas atrasado em relação a nós, então 8:30 = 13:30 em BSB.
Mais informações...
A First Look at WF 4.0, “Dublin”, and “Oslo”: Workflows, Services, and Models
http://msdn.microsoft.com/en-us/library/dd200919.aspx
“Oslo”:
http://msdn.microsoft.com/en-us/oslo/default.aspx
Windows Azure
http://www.microsoft.com/azure/default.mspx
[]s
Luciano Caixeta Moreira
luciano.moreira@microsoft.com
===============================================
This post is provided "AS IS" and confers no right
===============================================