Guia de Migração de Oracle para SQL Server e SQL Server para Oracle - Pt. 3

Publicados: 2022-03-11

A primeira e a segunda partes desta série discutiram as diferenças entre o Oracle Database e o Microsoft SQL Server em sua implementação de transações e as armadilhas de conversão resultantes, bem como alguns elementos de sintaxe comumente usados.

Esta última parte abordará a noção de consistência de leitura do Oracle e como converter a arquitetura, com base nessa noção, em uma versão do Microsoft SQL Server. Também abordará o uso de sinônimos (e como NÃO usá-los) e o papel do processo de controle de alterações no gerenciamento do ambiente de banco de dados.

Consistência de leitura do Oracle e seu equivalente no SQL Server

A consistência de leitura do Oracle é uma garantia de que todos os dados retornados por uma única instrução SQL venham do mesmo ponto singular no tempo.

Isso significa que, se você emitiu uma SELECT às 12:01:02.345 e ela foi executada por 5 minutos antes de retornar o conjunto de resultados, todos os dados (e apenas os dados) que foram confirmados no banco de dados a partir de 12:01:02.345 o tornarão em seu conjunto de retorno. Seu conjunto de retorno não terá novos dados adicionados durante os 5 minutos que o banco de dados levou para processar seu extrato, nem atualizações, e nenhuma exclusão será visível.

A arquitetura Oracle alcança consistência de leitura ao registrar internamente cada alteração nos dados e criar um conjunto de resultados de duas fontes: arquivos de dados permanentes e um segmento de desfazer (ou “segmento de reversão”, como era conhecido até a versão 10g).

Para apoiá-lo, as informações de desfazer devem ser preservadas. Se for substituído, resultará no infame erro ORA-01555: snapshot too old .

Deixando de lado o gerenciamento do segmento undo — e como navegar pelo erro ORA-01555: snapshot too old — vamos examinar as implicações da consistência de leitura em qualquer implementação prática no Oracle. Além disso, como ele deve ser espelhado no SQL Server, que – como é o caso de outras implementações de RDBMS, com a possível e qualificada exceção do PostgreSQL – não o suporta?

A chave é que as leituras e gravações do Oracle não bloqueiam umas às outras. Isso também significa que seu conjunto de retorno de consulta de longa duração pode não ter os dados mais recentes.

Leituras e gravações sem bloqueio são uma vantagem que o Oracle tem e afeta o escopo da transação.

Mas a consistência de leitura também significa que você não tem o estado mais recente dos dados. Quando em alguns cenários é perfeitamente bom (como produzir um relatório para um determinado momento), pode criar problemas significativos em outros.

Não ter os dados mais recentes – mesmo “sujos” ou não confirmados – pode ser crítico: o cenário clássico é um sistema de reserva de quartos de hotel.

Considere o seguinte caso de uso: Você tem dois agentes de atendimento ao cliente que estão aceitando simultaneamente pedidos de reserva de quarto. Como você pode garantir que os quartos não fiquem lotados?

No SQL Server, você pode iniciar uma transação explícita e SELECT um registro da lista (que pode ser uma tabela ou visualização) de salas disponíveis. Enquanto esta transação não for fechada (por COMMIT ou ROLLBACK ), ninguém poderá obter o mesmo registro de sala que você selecionou. Isso evita reservas duplas, mas também faz com que todos os outros agentes esperem um pelo outro para concluir as solicitações de reserva, uma de cada vez, sequencialmente.

No Oracle, você pode obter o mesmo resultado emitindo uma SELECT ... FOR UPDATE em relação aos registros que correspondem aos seus critérios de pesquisa.

Observação: existem soluções melhores, como definir um sinalizador temporário marcando uma sala “em consideração” em vez de bloquear cegamente o acesso a ela. Mas essas são soluções arquitetônicas, não opções de idioma.

Conclusão : A consistência de leitura do Oracle não é “totalmente boa” ou “totalmente ruim”, mas uma propriedade importante da plataforma que precisa ser bem compreendida e é fundamental para a migração de código entre plataformas.

Sinônimos públicos (e privados) em Oracle e Microsoft SQL Server

“Sinônimos públicos são maus.” Não é exatamente minha descoberta pessoal, mas eu a aceitei como evangelho até que meu dia, semana e ano foram salvos por sinônimos públicos.

Em muitos ambientes de banco de dados – eu diria que todos os ambientes Oracle com os quais tive a chance de trabalhar, mas nenhum que eu projetei – usar CREATE PUBLIC SYNONYM para cada objeto era rotina porque “sempre fizemos assim”.

Nesses ambientes, os sinônimos públicos tinham apenas uma função: permitir referência a um objeto sem especificar seu proprietário. E esta é uma razão mal pensada para fazer sinônimos públicos.

No entanto, os sinônimos públicos da Oracle podem ser extremamente úteis e proporcionar benefícios de produtividade da equipe que superam significativamente todas as suas desvantagens, se implementados e gerenciados corretamente e com uma razão. Sim, eu disse “produtividade da equipe”. Mas como? Para isso, precisamos entender como funciona a resolução de nomes no Oracle.

Quando o analisador Oracle encontra um nome (uma palavra-chave não reservada), ele tenta combiná-lo com um objeto de banco de dados existente na seguinte ordem:

Um fluxograma começando com my_object como entrada. O esquema atual da sessão de emissão tem um objeto chamado my_object? Se sim, terminamos. Se não, o esquema atual da sessão de emissão tem um sinônimo privado chamado my_object? Nesse caso, resolvemos o sinônimo em um objeto e terminamos. Se não, existe um sinônimo público chamado my_object? Em caso afirmativo, resolva-o e pronto. Caso contrário, procure um esquema com este nome. Se encontrarmos um, acabamos. Se não, gere um erro.

Nota: O erro gerado será ORA-00942: table or view does not exist para instruções DML, ou PLS-00201: identifier 'my_object' must be declared para procedimentos armazenados ou chamadas de função.

Nessa ordem de resolução de nomes, é fácil ver que, quando um desenvolvedor está trabalhando em seu próprio esquema, qualquer objeto local com o mesmo nome de um sinônimo público ocultará esse sinônimo público. (Observação: o Oracle 18c implementou o tipo de esquema “somente login” e esta discussão não se aplica a ele.)

Sinônimos públicos para equipes de dimensionamento: Oracle Change Control

Vejamos agora uma equipe hipotética de 100 desenvolvedores trabalhando no mesmo banco de dados (algo que experimentei). Além disso, vamos supor que todos estejam trabalhando localmente em suas estações de trabalho pessoais e fazendo compilações independentes de bancos de dados, todos vinculados ao mesmo ambiente de desenvolvimento de banco de dados. A resolução da mesclagem de código em código que não é de banco de dados (seja C#, Java, C++, Python ou qualquer outra coisa) será feita no momento do check-in do controle de alterações e entrará em vigor na próxima compilação de código. Mas tabelas de banco de dados, código e dados precisam ser alterados várias vezes durante o desenvolvimento contínuo. Cada desenvolvedor faz isso de forma independente e entra em vigor imediatamente.

Para isso, todos os objetos de banco de dados são criados em um esquema de aplicação comum. Este é o esquema que o aplicativo faz referência. Cada desenvolvedor:

  • Conecta-se ao banco de dados com sua conta/esquema de usuário pessoal
  • Sempre começa com um esquema pessoal vazio
  • Referencia o esquema comum apenas por meio da resolução de nomes para um sinônimo público, conforme descrito acima

Quando um desenvolvedor precisa fazer alguma alteração no banco de dados - criar ou alterar uma tabela, alterar o código do procedimento ou até mesmo modificar um conjunto de dados para dar suporte a algum cenário de teste - ele cria uma cópia do objeto em seu esquema pessoal. Eles fazem isso obtendo o código DDL usando o comando DESCRIBE e executando-o localmente.

A partir deste momento, o código deste desenvolvedor verá a versão local do objeto e dos dados, que não serão visíveis (nem impactando) mais ninguém. Após a conclusão do desenvolvimento, o código do banco de dados modificado é verificado no controle de origem e os conflitos são resolvidos. Em seguida, o código final (e dados, se necessário) é implementado no esquema comum.

Depois disso, toda a equipe de desenvolvimento pode ver o mesmo banco de dados novamente. O desenvolvedor que acabou de entregar o código descarta todos os objetos de seu esquema pessoal e está pronto para uma nova atribuição.

Essa capacidade de facilitar o trabalho paralelo independente para vários desenvolvedores é o principal benefício dos sinônimos públicos - uma importância difícil de exagerar. No entanto, na prática, continuo vendo equipes criando sinônimos públicos em implementações Oracle “só porque sempre fazemos isso”. Por outro lado, em equipes que usam SQL Server, não vejo a criação de sinônimos públicos estabelecida como uma prática comum. A funcionalidade existe, mas não é usada com frequência.

No SQL Server, o esquema padrão atual para um usuário é definido na configuração do usuário e pode ser alterado a qualquer momento se você tiver privilégios de "alterar usuário". A mesma metodologia exata descrita acima para Oracle pode ser implementada. No entanto, se esse método não for usado, os sinônimos públicos não devem ser copiados.

Como o Microsoft SQL Server não associa uma nova conta de usuário com seu próprio esquema por padrão (como o Oracle faz), a associação deve fazer parte do seu script padrão de “criar usuário”.

Abaixo está um exemplo de um script que cria esquemas de usuário dedicados e atribui um a um usuário.

Primeiro, crie esquemas para novos usuários que precisam ser integrados ao banco de dados chamado DevelopmentDatabase (cada esquema deve ser criado em seu próprio lote):

 use DevelopmentDatabase; GO CREATE SCHEMA Dev1; GO CREATE SCHEMA Dev2; GO

Segundo, crie o primeiro usuário com seu esquema padrão atribuído:

 CREATE LOGIN DevLogin123 WITH PASSWORD = 'first_pass123'; CREATE USER Dev1 FOR LOGIN DevLogin123 WITH DEFAULT_SCHEMA = Dev1; GO

Neste ponto, o esquema padrão para o usuário Dev1 seria Dev1 .

Em seguida, crie o outro usuário sem esquema padrão:

 CREATE LOGIN DevLogin321 WITH PASSWORD = 'second_pass321'; CREATE USER Dev2 FOR LOGIN DevLogin321; GO

O esquema padrão para o usuário Dev2 é dbo .

Agora altere o usuário Dev2 para alterar seu esquema padrão para Dev2 :

 ALTER USER Dev2 WITH DEFAULT_SCHEMA = Dev2; GO

Agora, o esquema padrão para o usuário Dev2 é Dev2 .

Este script demonstra duas maneiras de atribuir e alterar um esquema padrão para um usuário em bancos de dados do Microsoft SQL Server. Como o SQL Server oferece suporte a vários métodos de autenticação de usuário (o mais comum é a autenticação do Windows) e a integração do usuário pode ser tratada por administradores de sistema em vez de DBAs, o método ALTER USER de atribuição/alteração do esquema padrão será mais útil.

Nota: Eu fiz o nome do esquema igual ao nome de um usuário. Não precisa ser assim no SQL Server, mas é minha preferência porque (1) combina com como é feito no Oracle e (2) simplifica o gerenciamento de usuários (resolvendo a maior objeção por parte de um DBA para fazer isso direito em primeiro lugar)—você sabe o nome de um usuário e sabe automaticamente o esquema padrão do usuário.

Conclusão : Os sinônimos públicos são uma ferramenta importante para construir um ambiente de desenvolvimento multiusuário estável e bem protegido. Infelizmente, em minha observação no setor, é mais frequentemente usado pelos motivos errados – deixando as equipes sofrendo a confusão e outras desvantagens de sinônimos públicos sem perceber seus benefícios. Alterar essa prática para obter benefícios reais de sinônimos públicos pode trazer benefícios reais ao fluxo de trabalho de desenvolvimento de uma equipe.

Processos de gerenciamento de acesso ao banco de dados e gerenciamento de mudanças

Como acabamos de falar sobre suporte para desenvolvimento paralelo por grandes equipes, vale a pena abordar um tópico separado e muitas vezes incompreendido: processos de controle de mudanças.

O gerenciamento de mudanças geralmente se torna uma forma de burocracia controlada por líderes de equipe e DBAs, desprezados por desenvolvedores rebeldes que querem entregar tudo, se não “ontem”, então “agora”.

Como DBA, sempre coloco barreiras de proteção no caminho para o "meu" banco de dados. E tenho uma boa razão para isso: um banco de dados é um recurso compartilhado.

Tweet

Em um contexto de controle de origem, o gerenciamento de alterações é geralmente aceito, pois permite que uma equipe reverta de um código novo, mas quebrado, para um código antigo, mas funcionando. Mas em um contexto de banco de dados, o gerenciamento de mudanças pode parecer um conjunto de barreiras e restrições irracionais colocadas pelos DBAs: é pura loucura que retarda o desenvolvimento desnecessariamente!

Deixemos de lado o desabafo deste desenvolvedor: sou DBA e não vou atirar pedras em mim mesmo! Como DBA, sempre coloco barreiras de proteção no caminho para o “meu” banco de dados. E tenho uma boa razão para isso: um banco de dados é um recurso compartilhado.

Cada equipe de desenvolvimento – e cada um de seus desenvolvedores – tem um objetivo definido muito especificamente e uma entrega muito específica. O único objetivo que está na mesa de um DBA todos os dias é a estabilidade do banco de dados como um recurso compartilhado. Um DBA tem a função exclusiva em uma organização de supervisionar todos os esforços de desenvolvimento em todas as equipes e controlar um banco de dados que todos os desenvolvedores acessam. É o DBA que garante que todos os projetos e todos os processos sejam executados sem interferir uns nos outros e que cada um tenha os recursos necessários para funcionar.

O problema é quando as equipes de desenvolvimento e DBA ficam trancadas em suas respectivas torres de marfim.

Os desenvolvedores não sabem, não têm acesso e nem se importam com o que acontece no banco de dados, desde que esteja funcionando bem para eles. (Não é sua entrega, nem afetará sua avaliação de desempenho.)

A equipe de DBA mantém o banco de dados próximo ao baú, protegendo-o de desenvolvedores que “não sabem nada” sobre ele, pois o objetivo de sua equipe é a estabilidade do banco de dados. E a melhor maneira de garantir a estabilidade é evitar mudanças destrutivas – muitas vezes resultando em uma atitude de proteger o banco de dados de quaisquer mudanças tanto quanto possível.

Essas atitudes conflitantes em relação a um banco de dados podem, como já vi, levar à animosidade entre as equipes de desenvolvimento e DBA e resultar em um ambiente inviável. Mas os DBAs e a equipe de desenvolvimento devem trabalhar juntos para alcançar um objetivo comum: entregar uma solução de negócios, que foi o que os uniu em primeiro lugar.

Tendo estado em ambos os lados da divisão desenvolvedor-DBA, sei que o problema é fácil de resolver quando os DBAs entendem melhor as tarefas e objetivos comuns das equipes de desenvolvimento. Por sua vez, os desenvolvedores precisam ver um banco de dados não como um conceito abstrato, mas como um recurso compartilhado – e aí, um DBA deve assumir o papel de um educador.

O erro mais comum que os DBAs não desenvolvedores cometem é restringir o acesso do desenvolvedor ao dicionário de dados e às ferramentas de otimização de código. O acesso a visualizações de catálogo Oracle DBA_ , visualizações V$ dinâmicas e tabelas SYS parecem para muitos DBAs como “DBA privilegiado” quando, na verdade, essas são ferramentas críticas de desenvolvimento.

O mesmo vale para o SQL Server, com uma complicação: o acesso a algumas exibições do sistema não pode ser concedido diretamente, mas é apenas parte da função de banco de dados SYSADMIN , e essa função nunca deve ser concedida fora da equipe de DBA. Isso pode ser resolvido (e deve ser resolvido no caso de migração de um projeto de Oracle para SQL Server) criando visualizações e procedimentos armazenados que são executados sob privilégios SYSADMIN , mas são acessíveis por usuários não DBA. Este é o trabalho do DBA de desenvolvimento quando um novo ambiente de desenvolvimento do SQL Server é configurado.

A proteção de dados é uma das principais responsabilidades de um DBA. Apesar disso, é bastante comum que as equipes de desenvolvimento tenham acesso total a dados de produção não filtrados para permitir a solução de problemas de tickets relacionados a dados. Esses são os mesmos desenvolvedores que têm acesso limitado à estrutura de dados – estrutura que foi criada por eles ou para eles em primeiro lugar.

Quando as relações de trabalho adequadas entre as equipes de desenvolvimento e DBA são estabelecidas, a criação de um bom processo de controle de mudanças se torna intuitiva. As especificidades e o desafio do gerenciamento de mudanças do lado do banco de dados é a rigidez e a fluidez de um banco de dados ao mesmo tempo - a estrutura é rígida, os dados são fluidos.

Muitas vezes acontece que o gerenciamento de mudanças na modificação da estrutura – ou seja, na linguagem de definição de dados, ou DDL – está bem estabelecido, enquanto as mudanças de dados têm pouco ou nenhum em termos de gerenciamento de mudanças. A justificativa é simples - os dados mudam o tempo todo.

Mas se olharmos mais de perto, veremos que, em qualquer sistema, todos os dados se enquadram em uma das duas categorias: dados do aplicativo e dados do usuário.

Dados de aplicativo é um dicionário de dados que define o comportamento de um aplicativo e é tão crítico para seus processos quanto qualquer código de aplicativo. As alterações nesses dados devem estar sob rigorosos processos de controle de alterações, assim como em qualquer outra alteração de aplicativo. Para criar transparência no processo de controle de alterações para alterações de dados do aplicativo, os dados do aplicativo e os dados do usuário devem ser separados explicitamente.

No Oracle, isso deve ser feito colocando os dados do aplicativo e do usuário, cada um em seu próprio esquema. No Microsoft SQL Server, isso deve ser feito colocando cada um em um esquema separado ou – muito melhor – em um banco de dados separado. Fazer essas escolhas deve fazer parte do planejamento da migração: o Oracle tem resolução de nome de dois níveis (esquema/proprietário – nome do objeto), enquanto o SQL Server tem resolução de nome de três níveis (banco de dados – esquema/proprietário – nome do objeto).

Uma fonte comum de confusão entre os mundos Oracle e SQL Server são – talvez surpreendentemente – os termos banco de dados e servidor :

Termo do SQL Server Termo Oracle Definição
servidor banco de dados (usado de forma intercambiável com servidor em linguagem comum, a menos que se refira especificamente a hardware de servidor, sistema operacional ou elementos de rede; pode haver um ou mais bancos de dados em um servidor físico/virtual) Uma instância em execução que pode "conversar" com outras instâncias por meio de portas de rede
banco de dados (parte de um servidor, contém vários esquemas/proprietários) esquema/proprietário O agrupamento de nível mais alto

Essa confusão de terminologia deve ser claramente compreendida em projetos de migração de plataforma cruzada porque a má interpretação do termo pode resultar em decisões de configuração incorretas que são difíceis de resolver retroativamente.

A separação correta dos dados do aplicativo e do usuário permite que uma equipe de DBA aborde sua segunda preocupação mais importante: a segurança dos dados do usuário. Como os dados do usuário residem separadamente, será muito simples implementar um procedimento de quebra de vidro para acesso aos dados do usuário conforme necessário.

Conclusão : Os processos de controle de mudanças são críticos em qualquer projeto. Na engenharia de software, o gerenciamento de mudanças no lado do banco de dados é frequentemente negligenciado porque os dados são vistos como “muito fluidos”. Mas é exatamente porque os dados são “fluidos” e “persistentes” ao mesmo tempo que um processo de controle de mudanças bem projetado deve ser a base da arquitetura adequada do ambiente de banco de dados.

Sobre o uso de ferramentas de migração de código

As ferramentas próprias padrão, Oracle Migration Workbench e SQL Server Migration Assistant, podem ser úteis nas migrações de código. Mas o que precisa ser levado em consideração é a regra 80/20: quando o código for migrado 80% corretamente, resolver os 20% restantes exigirá 80% do seu esforço de migração.

O maior risco no uso de ferramentas de migração é, de longe, a percepção de “bala de prata”. Pode-se ficar tentado a pensar: “Vai fazer o trabalho, e eu só preciso fazer um pouco de limpeza e arrumação”. Observei um projeto que falhou devido a tal atitude da equipe de conversão e sua liderança técnica.

Por outro lado, levei quatro dias úteis para realizar a conversão básica de um sistema Microsoft SQL Server 2008 de tamanho médio (cerca de 200 objetos) usando a funcionalidade de substituição em massa do Notepad++ como principal ferramenta de edição.

Nenhum dos elementos críticos de migração que abordei até agora pode ser resolvido por ferramentas de migração.

Claro, use ferramentas de assistência de migração, mas lembre-se de que elas fornecem apenas assistência de edição. O texto de saída resultante precisa ser revisado, modificado e, em alguns casos, reescrito para se tornar um código digno de produção.

O desenvolvimento de ferramentas de inteligência artificial pode resolver essas deficiências de ferramentas de migração no futuro, mas eu esperaria que as diferenças entre os bancos de dados se apagassem antes disso e qualquer processo de migração em si se tornaria desnecessário. Então, enquanto esses tipos de projetos forem necessários, precisaremos fazê-lo da maneira antiga, usando a antiquada inteligência humana.

Conclusão : O uso de ferramentas de assistência à migração é útil, mas não é uma solução mágica, e qualquer projeto de conversão ainda requer uma análise detalhada dos pontos acima.

Migrações do Oracle/SQL Server: Sempre dê uma olhada mais de perto

Oracle e Microsoft SQL Server são as duas plataformas RDBMS mais proliferadas no ambiente corporativo. Ambos têm conformidade básica com o padrão ANSI SQL, e pequenos segmentos de código podem ser movidos com muito pouca modificação, ou mesmo como estão.

Essa semelhança cria uma impressão enganosa de que a migração entre as duas plataformas é uma tarefa simples e direta e que o mesmo aplicativo pode ser facilmente adotado usando um back-end RDBMS para outro.

Na prática, essas migrações de plataforma estão longe de ser triviais e precisam levar em conta os detalhes do funcionamento interno de cada plataforma e, principalmente, a forma como implementam o suporte ao elemento mais crítico do gerenciamento de dados: as transações.

Embora eu tenha abordado duas plataformas RDBMS que estão no centro da minha experiência, o mesmo aviso – “parece igual não significa que funciona igual” – deve ser aplicado para mover código entre qualquer outro sistema de gerenciamento de banco de dados compatível com SQL. E em todos os casos, o primeiro ponto de atenção deve ser em como a implementação do gerenciamento de transações difere entre as plataformas de origem e de destino.