Erros de desenvolvimento de banco de dados cometidos por desenvolvedores de aplicativos [fechado]

566

Quais são os erros comuns de desenvolvimento de banco de dados cometidos pelos desenvolvedores de aplicativos?

Charles Faiga
fonte
Duplicação quase duplicada de stackoverflow.com/questions/346659/…
dkretz

Respostas:

1002

1. Não usar índices apropriados

É relativamente fácil, mas ainda acontece o tempo todo. Chaves estrangeiras devem ter índices nelas. Se você estiver usando um campo em um WHERE(provavelmente) deverá ter um índice nele. Esses índices costumam cobrir várias colunas com base nas consultas que você precisa executar.

2. Não impondo integridade referencial

Seu banco de dados pode variar aqui, mas se seu banco de dados suportar integridade referencial - o que significa que todas as chaves estrangeiras devem apontar para uma entidade existente - você deve usá-lo.

É bastante comum ver essa falha nos bancos de dados MySQL. Não acredito que o MyISAM o suporte. InnoDB faz. Você encontrará pessoas que estão usando o MyISAM ou aquelas que estão usando o InnoDB, mas não o estão usando de qualquer maneira.

Mais aqui:

3. Usando chaves primárias naturais em vez de substitutas (técnicas)

Chaves naturais são chaves baseadas em dados externos significativos (ostensivamente) exclusivos. Exemplos comuns são códigos de produto, códigos de estado de duas letras (EUA), números de previdência social e assim por diante. Chaves primárias técnicas ou substitutas são aquelas que não têm absolutamente nenhum significado fora do sistema. Eles são inventados exclusivamente para identificar a entidade e normalmente são campos de incremento automático (SQL Server, MySQL, outros) ou sequências (principalmente o Oracle).

Na minha opinião, você sempre deve usar chaves substitutas. Esse problema surgiu nestas perguntas:

Este é um tópico um tanto controverso sobre o qual você não terá um acordo universal. Embora você possa encontrar algumas pessoas que pensam que as chaves naturais estão bem em algumas situações, você não encontrará nenhuma crítica às chaves substitutas além de indiscutivelmente desnecessárias. Essa é uma pequena desvantagem, se você me perguntar.

Lembre-se, mesmo países podem deixar de existir (por exemplo, Iugoslávia).

4. Escrevendo consultas que requerem DISTINCTtrabalho

Você costuma ver isso nas consultas geradas pelo ORM. Observe a saída de log do Hibernate e você verá todas as consultas começando com:

SELECT DISTINCT ...

Este é um atalho para garantir que você não retorne linhas duplicadas e, assim, obtenha objetos duplicados. Às vezes, você também vê pessoas fazendo isso. Se você vê demais, é uma verdadeira bandeira vermelha. Não DISTINCTé ruim ou não possui aplicativos válidos. Sim (em ambos os aspectos), mas não é um substituto ou um paliativo para escrever consultas corretas.

De porque eu odeio DISTINCT :

Onde as coisas começam a azedar, na minha opinião, é quando um desenvolvedor está criando consultas substanciais, unindo tabelas e, de repente, percebe que parece que está obtendo linhas duplicadas (ou mais) e sua resposta imediata ... sua "solução" para esse "problema" é usar a palavra-chave DISTINCT e POOF todos os seus problemas desaparecem.

5. Favorecendo a agregação sobre junções

Outro erro comum dos desenvolvedores de aplicativos de banco de dados é não perceber o quanto a agregação mais cara (ou seja, a GROUP BYcláusula) pode ser comparada às junções.

Para ter uma idéia de como isso é generalizado, escrevi sobre esse tópico várias vezes aqui e fui muito criticado por isso. Por exemplo:

Da instrução SQL - "ingressar" vs "agrupar por e ter" :

Primeira consulta:

SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3

Tempo da consulta: 0.312 s

Segunda consulta:

SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1

Tempo da consulta: 0.016 s

Está certo. A versão de junção que propus é vinte vezes mais rápida que a versão agregada.

6. Não simplificando consultas complexas por meio de visualizações

Nem todos os fornecedores de banco de dados oferecem suporte a visualizações, mas para aqueles que o fazem, eles podem simplificar bastante as consultas, se usadas criteriosamente. Por exemplo, em um projeto, usei um modelo genérico de Party para CRM. Essa é uma técnica de modelagem extremamente poderosa e flexível, mas pode levar a muitas junções. Nesse modelo, havia:

  • Partido : pessoas e organizações;
  • Função da Parte : coisas que essas partes fizeram, por exemplo Empregado e Empregador;
  • Relacionamento com as partes : como essas funções se relacionam.

Exemplo:

  • Ted é uma Pessoa, sendo um subtipo de Partido;
  • Ted tem muitos papéis, um dos quais é Funcionário;
  • A Intel é uma organização, sendo um subtipo de uma Parte;
  • A Intel tem muitas funções, uma das quais é empregadora;
  • A Intel emprega Ted, o que significa que há um relacionamento entre seus respectivos papéis.

Portanto, há cinco tabelas unidas para vincular Ted ao seu empregador. Você assume que todos os funcionários são Pessoas (não organizações) e fornece esta visão auxiliar:

CREATE VIEW vw_employee AS
SELECT p.title, p.given_names, p.surname, p.date_of_birth, p2.party_name employer_name
FROM person p
JOIN party py ON py.id = p.id
JOIN party_role child ON p.id = child.party_id
JOIN party_role_relationship prr ON child.id = prr.child_id AND prr.type = 'EMPLOYMENT'
JOIN party_role parent ON parent.id = prr.parent_id = parent.id
JOIN party p2 ON parent.party_id = p2.id

E de repente você tem uma visão muito simples dos dados que deseja, mas em um modelo de dados altamente flexível.

7. Entrada não higienizante

Este é enorme. Agora eu gosto de PHP, mas se você não sabe o que está fazendo, é muito fácil criar sites vulneráveis ​​a ataques. Nada resume melhor do que a história das pequenas mesas de Bobby .

Os dados fornecidos pelo usuário por meio de URLs, dados de formulário e cookies sempre devem ser tratados como hostis e higienizados. Verifique se está obtendo o que espera.

8. Não usar declarações preparadas

As instruções preparadas são quando você compila uma consulta menos os dados usados ​​em inserções, atualizações e WHEREcláusulas e fornece essas informações posteriormente. Por exemplo:

SELECT * FROM users WHERE username = 'bob'

vs

SELECT * FROM users WHERE username = ?

ou

SELECT * FROM users WHERE username = :username

dependendo da sua plataforma.

Eu vi bancos de dados trazidos de joelhos ao fazer isso. Basicamente, sempre que um banco de dados moderno encontra uma nova consulta, ele precisa compilá-lo. Se encontrar uma consulta vista anteriormente, você estará dando ao banco de dados a oportunidade de armazenar em cache a consulta compilada e o plano de execução. Ao fazer muita consulta, você está dando ao banco de dados a oportunidade de descobrir isso e otimizar adequadamente (por exemplo, fixando a consulta compilada na memória).

O uso de instruções preparadas também fornecerá estatísticas significativas sobre a frequência com que determinadas consultas são usadas.

Instruções preparadas também o protegerão melhor contra ataques de injeção de SQL.

9. Não normalizando o suficiente

A normalização do banco de dados é basicamente o processo de otimizar o design do banco de dados ou como você organiza seus dados em tabelas.

Apenas nesta semana, deparei com algum código em que alguém havia implodido uma matriz e a inserido em um único campo em um banco de dados. Normalizar isso seria tratar o elemento dessa matriz como uma linha separada em uma tabela filha (isto é, um relacionamento de um para muitos).

Isso também surgiu no melhor método para armazenar uma lista de IDs de usuário :

Já vi em outros sistemas que a lista é armazenada em uma matriz PHP serializada.

Mas a falta de normalização ocorre de várias formas.

Mais:

10. Normalizando demais

Isso pode parecer uma contradição com o ponto anterior, mas a normalização, como muitas coisas, é uma ferramenta. É um meio para um fim e não um fim em si mesmo. Eu acho que muitos desenvolvedores esquecem isso e começam a tratar um "meio" como um "fim". O teste de unidade é um excelente exemplo disso.

Certa vez, trabalhei em um sistema que tinha uma hierarquia enorme para clientes que eram algo como:

Licensee ->  Dealer Group -> Company -> Practice -> ...

de modo que você teve que juntar cerca de 11 tabelas antes de poder obter dados significativos. Foi um bom exemplo de normalização levado longe demais.

Mais exatamente, a desnormalização cuidadosa e considerada pode trazer enormes benefícios de desempenho, mas é preciso ter muito cuidado ao fazer isso.

Mais:

11. Usando arcos exclusivos

Um arco exclusivo é um erro comum em que uma tabela é criada com duas ou mais chaves estrangeiras, onde uma e apenas uma delas pode ser não nula. Grande erro. Por um lado, fica muito mais difícil manter a integridade dos dados. Afinal, mesmo com integridade referencial, nada impede que duas ou mais dessas chaves estrangeiras sejam definidas (apesar de restrições de verificação complexas).

De um guia prático para o design de banco de dados relacional :

Aconselhamos vivamente a construção de arco exclusivo sempre que possível, pelo bom motivo de que podem ser difíceis de escrever código e apresentar mais dificuldades de manutenção.

12. Não realizando análise de desempenho em consultas

O pragmatismo reina supremo, particularmente no mundo dos bancos de dados. Se você segue os princípios a ponto de se tornarem um dogma, provavelmente cometeu erros. Veja o exemplo das consultas agregadas acima. A versão agregada pode parecer "agradável", mas seu desempenho é lamentável. Uma comparação de desempenho deveria ter encerrado o debate (mas não terminou), mas mais ao ponto: divulgar essas opiniões mal informadas é ignorante e até perigoso.

13. Confiança excessiva nos construtos UNION ALL e particularmente UNION

Uma UNION em termos SQL apenas concatena conjuntos de dados congruentes, o que significa que eles têm o mesmo tipo e número de colunas. A diferença entre eles é que UNION ALL é uma concatenação simples e deve ser preferida sempre que possível, enquanto um UNION implicitamente fará um DISTINCT para remover tuplas duplicadas.

UNIÕES, como DISTINCT, têm seu lugar. Existem aplicativos válidos. Mas se você se encontra fazendo muitas delas, principalmente em subconsultas, provavelmente está fazendo algo errado. Pode ser um caso de construção de consulta ruim ou um modelo de dados mal projetado forçando você a fazer essas coisas.

UNIONs, principalmente quando usados ​​em junções ou subconsultas dependentes, podem prejudicar um banco de dados. Tente evitá-los sempre que possível.

14. Usando condições OR em consultas

Isso pode parecer inofensivo. Afinal, ANDs estão OK. OU deve ficar bem também, certo? Errado. Basicamente, uma condição AND restringe o conjunto de dados, enquanto uma condição OR o aumenta , mas não de uma maneira que se presta à otimização. Especialmente quando as diferentes condições OR podem se cruzar, forçando o otimizador a efetivamente executar uma operação DISTINCT no resultado.

Ruim:

... WHERE a = 2 OR a = 5 OR a = 11

Melhor:

... WHERE a IN (2, 5, 11)

Agora, o seu otimizador SQL pode transformar efetivamente a primeira consulta na segunda. Mas talvez não. Apenas não faça isso.

15. Não projetar seu modelo de dados para oferecer soluções de alto desempenho

Este é um ponto difícil de quantificar. É normalmente observado por seu efeito. Se você se escreve escrevendo consultas genéricas para tarefas relativamente simples ou que consultas para descobrir informações relativamente diretas não são eficientes, provavelmente você tem um modelo de dados ruim.

De certa forma, este ponto resume todos os anteriores, mas é mais uma advertência que fazer coisas como otimização de consultas geralmente é feito primeiro quando deve ser feito depois. Em primeiro lugar, você deve garantir um bom modelo de dados antes de tentar otimizar o desempenho. Como Knuth disse:

Otimização prematura é a raiz de todo o mal

16. Uso incorreto de transações de banco de dados

Todas as alterações de dados para um processo específico devem ser atômicas. Ou seja, se a operação for bem-sucedida, ela será totalmente executada. Se falhar, os dados permanecerão inalterados. - Não deve haver possibilidade de alterações "pela metade".

Idealmente, a maneira mais simples de conseguir isso é que todo o design do sistema se esforce para oferecer suporte a todas as alterações de dados por meio de instruções INSERT / UPDATE / DELETE únicas. Nesse caso, nenhuma manipulação de transação especial é necessária, pois o mecanismo do banco de dados deve fazê-lo automaticamente.

No entanto, se algum processo exigir que várias instruções sejam executadas como uma unidade para manter os dados em um estado consistente, será necessário o Controle de Transação apropriado.

  • Inicie uma transação antes da primeira instrução.
  • Confirme a transação após a última declaração.
  • Em qualquer erro, reverter a transação. E muito NB! Não se esqueça de pular / abortar todas as instruções que se seguem após o erro.

Também é recomendável prestar muita atenção às subtelidades de como a camada de conectividade do banco de dados e o mecanismo de banco de dados interagem nesse sentido.

17. Não entender o paradigma 'baseado em conjunto'

A linguagem SQL segue um paradigma específico adequado a tipos específicos de problemas. Não obstante, várias extensões específicas de fornecedor, a linguagem luta para lidar com problemas triviais em idiomas como Java, C #, Delphi etc.

Essa falta de entendimento se manifesta de algumas maneiras.

  • Imposição inadequada de muita lógica processual ou imperativa no banco de dados.
  • Uso inadequado ou excessivo de cursores. Especialmente quando uma única consulta seria suficiente.
  • Supondo incorretamente que os gatilhos sejam acionados uma vez por linha afetados nas atualizações de várias linhas.

Determine a clara divisão de responsabilidades e tente usar a ferramenta apropriada para resolver cada problema.

cletus
fonte
9
Nas declarações do MySQL sobre chaves estrangeiras, você está certo que o MyISAM não as suporta, mas implica que apenas o uso do MyISAM é um design ruim. Um motivo pelo qual eu usei o MyISAM é que o InnoDB não suporta pesquisas FullText, e não acho isso irracional.
Derek H
1
Eu tenho que perguntar sobre # 6. Usar visualizações como essa é uma das minhas coisas favoritas, mas aprendi recentemente, para meu horror, que os índices do MySQL nas tabelas subjacentes só serão respeitados se a estrutura da visualização permitir o uso do algoritmo de mesclagem. Caso contrário, uma tabela temporária será usada e todos os seus índices serão inúteis. É ainda mais alarmante quando você percebe que várias operações causam esse comportamento. É uma ótima maneira de transformar uma consulta de 0,01 segundo em uma de 100 segundos. Mais alguém aqui tem experiência com isso? Verifique os links no meu próximo comentário.
Peter Bailey
5
Não concordo totalmente com o nº 3. Sim, os países podem deixar de existir, mas o código do país continuará representando a mesma coisa. Mesmo com códigos de moeda ou estados dos EUA. É estúpido usar uma chave substituta nesses casos e cria mais sobrecarga nas suas consultas, pois você deve incluir uma associação extra. Eu diria que é mais seguro dizer que você provavelmente deve usar um substituto para dados específicos do usuário (portanto, não países, moedas e estados dos EUA).
Thomas
1
RE: # 11 A restrição de verificação necessária para reforçar a integridade dos dados é trivial. Existem outras razões para evitar esse design, mas a necessidade de restrição de verificação "complexa" não é uma delas.
Thomas
2
Com o # 3 você não está sendo honesto. Existem mais desvantagens na chave artificial do que "talvez você não precise dela". Especificamente, o uso de uma chave natural permitirá controlar a ordem em que os dados da sua tabela são gravados no disco. Se você souber como sua tabela será consultada, poderá indexá-la para que as linhas acessadas simultaneamente terminem na mesma página. Além disso, você pode impor a integridade dos dados usando um índice composto exclusivo. Se você precisar disso, precisará adicioná-lo além do seu índice de chave artificial. Se o índice composto é o seu pkey, são 2 pássaros mortos com uma pedra.
Shane H
110

Principais erros de design e programação de banco de dados cometidos pelos desenvolvedores

  • Design e uso de bancos de dados egoístas. Os desenvolvedores geralmente tratam o banco de dados como seu armazenamento de objetos persistente pessoal sem considerar as necessidades de outras partes interessadas nos dados. Isso também se aplica a arquitetos de aplicativos. O design inadequado do banco de dados e a integridade dos dados dificultam o trabalho dos terceiros com os dados e podem aumentar substancialmente os custos do ciclo de vida do sistema. Relatórios e MIS tendem a ser um primo ruim no design de aplicativos e são feitos apenas como uma reflexão tardia.

  • Abusando de dados desnormalizados. Exagerar nos dados desnormalizados e tentar mantê-los no aplicativo é uma receita para problemas de integridade dos dados. Use desnormalização com moderação. Não querer adicionar uma junção a uma consulta não é desculpa para desnormalizar.

  • Com medo de escrever SQL. SQL não é ciência do foguete e é realmente muito bom em fazer seu trabalho. As camadas de mapeamento de O / R são muito boas para fazer 95% das consultas simples e que se encaixam bem nesse modelo. Às vezes, o SQL é a melhor maneira de fazer o trabalho.

  • Políticas dogmáticas de 'Sem procedimentos armazenados'. Independentemente de você acreditar que os procedimentos armazenados são maus, esse tipo de atitude dogmática não tem lugar em um projeto de software.

  • Não entendo o design do banco de dados. A normalização é sua amiga e não é ciência de foguetes. União e cardinalidade são conceitos bastante simples - se você estiver envolvido no desenvolvimento de aplicativos de banco de dados, não há realmente desculpa para não entendê-los.

ConcernedOfTunbridgeWells
fonte
2
Pode-se argumentar que as transações devem ser feitas no banco de dados transacional e nos relatórios e o MIS deve ser feito em um banco de dados de análise separado. Portanto, você obtém o melhor dos dois mundos e todo mundo fica feliz (exceto pela má caneca que precisa escrever o script de transformação de dados para criar o último a partir do primeiro).
21720 Chris Simpson
Não apenas a má caneca que escreve o ETL - qualquer um que use dados do sistema, os dados de baixa qualidade no aplicativo MIS que estão incluídos porque vários relacionamentos-chave não são realmente registrados na fonte, qualquer um envolvido nos infindáveis ​​problemas de reconciliação que se seguem da baixa qualidade dos dados.
ConcernedOfTunbridgeWells
Eu não poderia discordar mais do ponto um. Os bancos de dados são para persistência, não para comunicação entre processos. Quase sempre existem soluções melhores para esse problema. A menos que exista um requisito explícito para isso, você absolutamente DEVE tratar o banco de dados como se ninguém, exceto o seu aplicativo, jamais o usasse. Mesmo que exista um requisito explícito, faça uma análise da história do usuário e da causa raiz, e muitas vezes você descobrirá uma maneira muito melhor de preencher a intenção do solicitante. Então, novamente, eu trabalho em uma empresa onde a frase CQRS é um pouco comum
George Mauer
3
Exemplo trivial: Eu tenho um sistema de administração de apólices de seguro e preciso carregar o estado de 5 milhões de reclamações em um sistema de resseguro cedido para calcular possíveis recuperações. Os sistemas são pacotes COTS cliente-servidor mais antigos, projetados para fazer interface com sistemas de mainframe ainda mais antigos. Ambos devem ser reconciliados para fins de controle financeiro. Este trabalho é realizado uma vez por mês. Pela sua lógica, eu escreveria uma série de histórias de usuários definindo os requisitos e pediria aos fornecedores que citassem a adição de um wrapper de serviço da Web aos seus produtos existentes.
ConcernedOfTunbridgeWells
2
Então o seu DBA é preguiçoso ou incompetente.
ConcernedOfTunbridgeWells
80
  1. Não usando o controle de versão no esquema do banco de dados
  2. Trabalhando diretamente em um banco de dados ativo
  3. Não ler e entender conceitos mais avançados do banco de dados (índices, índices agrupados, restrições, visualizações materializadas, etc.)
  4. Falha ao testar a escalabilidade ... dados de teste de apenas 3 ou 4 linhas nunca fornecerão a imagem real do desempenho real ao vivo
Rad
fonte
1
Eu segundo, pesadamente, # 1 e # 2. Sempre que faço uma alteração no banco de dados, despejo seu esquema e o faço a versão; Eu tenho três bancos de dados configurados, um desenvolvedor, um intermediário e um ativo - nada é "testado" no banco de dados ativo !!
Ixmatus
Aqui no Red Gate, tomamos medidas para melhorar seu primeiro ponto com o SQL Source Control! Das conversas que tive durante minha pesquisa, acho que as pessoas não estão mais desenvolvendo contra bancos de dados de produção, mas geralmente são feitas correções de "emergência" que geralmente encontram o caminho de volta aos ambientes de desenvolvimento, o que é outro problema.
David Atkinson
46

Uso excessivo e / ou dependência de procedimentos armazenados.

Alguns desenvolvedores de aplicativos veem os procedimentos armazenados como uma extensão direta do código da camada intermediária / front-end. Esse parece ser um traço comum nos desenvolvedores de pilha da Microsoft (sou um deles, mas cresci fora dele) e produz muitos procedimentos armazenados que executam lógica de negócios complexa e processamento de fluxo de trabalho. Isso é muito melhor feito em outro lugar.

Os procedimentos armazenados são úteis quando, atualmente, foi comprovado que algum fator técnico real requer seu uso (por exemplo, desempenho e segurança). Por exemplo, manter a agregação / filtragem de grandes conjuntos de dados "próximos aos dados".

Recentemente, tive que ajudar a manter e aprimorar um grande aplicativo de desktop Delphi, no qual 70% da lógica e das regras de negócios foram implementadas nos procedimentos armazenados do 1400 SQL Server (o restante nos manipuladores de eventos da interface do usuário). Isso foi um pesadelo, principalmente devido à dificuldade de introduzir testes de unidade eficazes no TSQL, falta de encapsulamento e ferramentas ruins (Debuggers, editores).

Trabalhando com uma equipe Java no passado, rapidamente descobri que muitas vezes o oposto completo se aplica naquele ambiente. Um arquiteto Java me disse uma vez: "O banco de dados é para dados, não para código".

Hoje em dia, acho um erro não considerar os procs armazenados, mas eles devem ser usados ​​com moderação (não por padrão) em situações em que oferecem benefícios úteis (consulte as outras respostas).

Ashley Henderson
fonte
4
Os procedimentos armazenados tendem a se tornar uma ilha de dano em qualquer projeto em que são usados; portanto, alguns desenvolvedores fazem a regra "Nenhum procedimento armazenado". Parece que há um conflito aberto entre eles. Sua resposta é um bom argumento para quando realmente escolher uma maneira ou outra.
Warren P
Benefícios: segurança - você não precisa dar aos aplicativos a capacidade de "excluir * de ..."; ajustes - os DBAs podem ajustar as consultas sem precisar recompilar / implantar o aplicativo inteiro; análise - é fácil recompilar vários procs após uma alteração no modelo de dados para garantir que eles ainda sejam válidos; e, finalmente, considerando que o SQL é executado pelo mecanismo de banco de dados (não seu aplicativo), o conceito de "banco de dados é para dados, não para código" é apenas retardado.
NotMe
Então, você enredaria sua lógica de negócios na interface do usuário, onde ela estava separada dos dados sendo manipulados? Isso não parece uma boa idéia, principalmente porque a manipulação de dados é mais eficiente quando realizada pelo servidor de banco de dados, e não por viagens de ida e volta da interface do usuário. Isso também significa que é mais difícil controlar o aplicativo, porque você não pode confiar no banco de dados controlando seus dados e, potencialmente, ter diferentes versões de uma interface do usuário por aí com diferentes manipulações de dados. Não é bom. Não deixo nada tocar meus dados, exceto através de um procedimento armazenado.
David T. Macknet
Se for necessário separar a lógica de negócios da interface do usuário, arquiteturas de várias camadas podem ser usadas. Ou, uma biblioteca com objetos de negócios e lógica, usada por diferentes aplicativos / UIs. Os procedimentos armazenados bloqueiam sua lógica de dados / negócios em um banco de dados específico; nesse caso, alterar um banco de dados é muito caro. E um custo enorme é ruim.
também
@too: Alterar um banco de dados na maioria dos casos é muito caro. Não importa a idéia de perder os recursos de desempenho e segurança que um DBMS específico fornece. Além disso, camadas adicionais aumentam a complexidade e diminuem o desempenho, e camadas adicionais estão vinculadas ao seu idioma específico. Por fim, é mais provável que o idioma usado mude do que um servidor de banco de dados.
NotMe
41

Problema número um? Eles testam apenas em bancos de dados de brinquedos. Portanto, eles não têm idéia de que o SQL deles rastreará quando o banco de dados ficar grande, e alguém terá que aparecer e corrigi-lo mais tarde (esse som que você pode ouvir é meus dentes rangendo).

Bob Moore
fonte
2
O tamanho do banco de dados é relevante, mas um problema maior é a carga - mesmo se você testar em um conjunto de dados real, não estará testando o desempenho de suas consultas quando o banco de dados estiver sob uma carga de produção, o que pode ser realmente uma surpresa.
Davidcl
Eu diria que o tamanho do banco de dados é um problema maior que a carga. Eu já vi muitas vezes que faltavam índices cruciais - nunca houve problema de desempenho durante os testes, porque todo o banco de dados se encaixava na memória
Danubian Sailor
31

Não está usando índices.

Christophe Herreman
fonte
28

Baixo desempenho causado por subconsultas correlacionadas

Na maioria das vezes você deseja evitar subconsultas correlacionadas. Uma subconsulta é correlacionada se, dentro da subconsulta, houver uma referência a uma coluna da consulta externa. Quando isso acontece, a subconsulta é executada pelo menos uma vez para cada linha retornada e pode ser executada mais vezes se outras condições forem aplicadas após a condição que contém a subconsulta correlacionada.

Perdoe o exemplo artificial e a sintaxe Oracle, mas digamos que você queira encontrar todos os funcionários que foram contratados em qualquer uma de suas lojas desde a última vez em que a loja realizou menos de US $ 10.000 em vendas por dia.

select e.first_name, e.last_name
from employee e
where e.start_date > 
        (select max(ds.transaction_date)
         from daily_sales ds
         where ds.store_id = e.store_id and
               ds.total < 10000)

A subconsulta neste exemplo está correlacionada com a consulta externa pelo store_id e seria executada para todos os funcionários em seu sistema. Uma maneira de otimizar essa consulta é mover a subconsulta para uma visualização em linha.

select e.first_name, e.last_name
from employee e,
     (select ds.store_id,
             max(s.transaction_date) transaction_date
      from daily_sales ds
      where ds.total < 10000
      group by s.store_id) dsx
where e.store_id = dsx.store_id and
      e.start_date > dsx.transaction_date

Neste exemplo, a consulta na cláusula from agora é uma exibição em linha (novamente alguma sintaxe específica do Oracle) e é executada apenas uma vez. Dependendo do seu modelo de dados, essa consulta provavelmente será executada muito mais rapidamente. O desempenho seria melhor que a primeira consulta à medida que o número de funcionários aumentasse. A primeira consulta poderia ter um desempenho melhor se houvesse poucos funcionários e muitas lojas (e talvez muitas lojas não tivessem funcionários) e a tabela daily_sales estivesse indexada em store_id. Este não é um cenário provável, mas mostra como uma consulta correlata poderia ter um desempenho melhor do que uma alternativa.

Vi desenvolvedores juniores correlacionarem subconsultas muitas vezes e, geralmente, teve um impacto severo no desempenho. No entanto, ao remover uma subconsulta correlacionada, verifique o plano de explicação antes e depois para garantir que você não esteja piorando o desempenho.

adão
fonte
1
Ótimo ponto e para enfatizar um de seus pontos relacionados - teste suas alterações. Aprenda a usar os planos de explicação (e veja o que o banco de dados está realmente fazendo para executar sua consulta e quanto custa), faça seus testes em um grande conjunto de dados e não torne seu SQL excessivamente complexo e ilegível / impossível de manter para uma otimização isso não melhora realmente o desempenho real.
Rob Whelan
21

Na minha experiência:
Não me comunico com DBAs experientes.

Kb.
fonte
17

Usando o Access em vez de um banco de dados "real". Existem muitos grandes bancos de dados pequenos e até gratuitos, como SQL Express , MySQL e SQLite, que funcionarão e serão dimensionados muito melhor. Os aplicativos geralmente precisam ser dimensionados de maneiras inesperadas.

Nathan Voxland
fonte
16

Esquecendo de estabelecer relacionamentos entre as tabelas. Lembro de ter que limpar isso quando comecei a trabalhar no meu atual empregador.

TheTXI
fonte
14

Usando o Excel para armazenar (grandes quantidades de) dados.

Vi empresas mantendo milhares de linhas e usando várias planilhas (devido ao limite de linhas de 65535 nas versões anteriores do Excel).


O Excel é adequado para relatórios, apresentação de dados e outras tarefas, mas não deve ser tratado como um banco de dados.

ML--
fonte
14

Gostaria de acrescentar: Favorecer o código "Elegante" ao código de alto desempenho. O código que funciona melhor em bancos de dados geralmente é feio para os olhos do desenvolvedor de aplicativos.

Acreditando que bobagem sobre otimização prematura. Os bancos de dados devem considerar o desempenho no design original e em qualquer desenvolvimento subsequente. O desempenho representa 50% do design do banco de dados (40% é a integridade dos dados e os últimos 10% são a segurança) na minha opinião. Os bancos de dados que não são criados de baixo para cima para executar terão um desempenho ruim quando usuários reais e tráfego real forem colocados no banco de dados. Otimização prematura não significa otimização! Isso não significa que você deve escrever código que quase sempre terá um desempenho ruim porque é mais fácil (cursores, por exemplo, que nunca devem ser permitidos em um banco de dados de produção, a menos que tudo falhe). Isso significa que você não precisa extrair esse último pouco de desempenho até precisar. Muito se sabe sobre o que terá melhor desempenho em bancos de dados,

HLGEM
fonte
2
+1 - A programação do banco de dados envolve a otimização do comportamento dos componentes mecânicos. Observe, no entanto, que Knuth diz que a otimização prematura é a raiz de todo mal cerca de 97% das vezes (ou palavras nesse sentido). O design do banco de dados é uma área em que você realmente precisa pensar sobre isso com antecedência.
ConcernedOfTunbridgeWells
2
Ahem ... o que você está falando é sobre otimização que não é prematura. Alguma consideração sobre o uso real é necessária desde o início no design do banco de dados (e também no design do aplicativo). Na verdade, a regra de Knuth não é trivial de seguir, porque você precisa decidir o que é prematuro e o que não é - realmente se resume a "não realizar otimizações sem dados". As decisões iniciais relacionadas ao desempenho das quais você está falando têm dados - determinados projetos definirão limites inaceitáveis ​​para o desempenho futuro e você poderá calculá-los.
Rob Whelan
13

Não está usando consultas parametrizadas. Eles são bastante úteis para interromper a injeção de SQL .

Este é um exemplo específico de não higienização dos dados de entrada, mencionado em outra resposta.

Cinza
fonte
3
Exceto que a entrada de higienização está errada. Higienizar implica colocá-lo em algum lugar onde possa ser perigoso. Parametrizar significa mantê-lo totalmente fora do caminho do mal.
Dustin
12

Eu odeio quando os desenvolvedores usam instruções de seleção aninhadas ou até funções, retornam o resultado de uma instrução de seleção dentro da parte "SELECT" de uma consulta.

Na verdade, estou surpreso por não ver isso em nenhum outro lugar aqui, talvez tenha esquecido, embora @adam tenha um problema semelhante indicado.

Exemplo:

SELECT
    (SELECT TOP 1 SomeValue FROM SomeTable WHERE SomeDate = c.Date ORDER BY SomeValue desc) As FirstVal
    ,(SELECT OtherValue FROM SomeOtherTable WHERE SomeOtherCriteria = c.Criteria) As SecondVal
FROM
    MyTable c

Nesse cenário, se MyTable retornar 10000 linhas, o resultado será como se a consulta tivesse executado apenas 20001 consultas, já que ela precisava executar a consulta inicial mais a consulta de cada uma das outras tabelas uma vez para cada linha de resultado.

Os desenvolvedores podem se dar bem com isso trabalhando em um ambiente de desenvolvimento em que estão retornando apenas algumas linhas de dados e as sub-tabelas geralmente possuem apenas uma pequena quantidade de dados, mas em um ambiente de produção, esse tipo de consulta pode se tornar exponencialmente caro, conforme mais dados são adicionados às tabelas.

Um exemplo melhor (não necessariamente perfeito) seria algo como:

SELECT
     s.SomeValue As FirstVal
    ,o.OtherValue As SecondVal
FROM
    MyTable c
    LEFT JOIN (
        SELECT SomeDate, MAX(SomeValue) as SomeValue
        FROM SomeTable 
        GROUP BY SomeDate
     ) s ON c.Date = s.SomeDate
    LEFT JOIN SomeOtherTable o ON c.Criteria = o.SomeOtherCriteria

Isso permite que os otimizadores de banco de dados embaralhem os dados juntos, em vez de requery em cada registro da tabela principal e, geralmente, quando preciso corrigir o código em que esse problema foi criado, geralmente acabo aumentando a velocidade das consultas em 100% ou enquanto reduz simultaneamente o uso da CPU e da memória.

CStroliaDavis
fonte
12

Para bancos de dados baseados em SQL:

  1. Não aproveitando os ÍNDICES CLUSTERED ou escolhendo a (s) coluna (s) incorreta (s) para CLUSTER.
  2. Não usando um tipo de dados SERIAL (número automático) como uma PRIMARY KEY para ingressar em uma FOREIGN KEY (INT) em um relacionamento de tabela pai / filho.
  3. Não atualizando estatísticas em uma tabela quando muitos registros foram inseridos ou excluídos.
  4. Não reorganizar (por exemplo, descarregar, eliminar, recriar, carregar e re-indexar) tabelas quando muitas linhas foram inseridas ou excluídas (alguns mecanismos mantêm fisicamente as linhas excluídas em uma tabela com um sinalizador de exclusão).
  5. Não aproveitar o FRAGMENT ON EXPRESSION (se suportado) em tabelas grandes com altas taxas de transação.
  6. Escolhendo o tipo de dados errado para uma coluna!
  7. Não escolhendo um nome de coluna adequado.
  8. Não adicionando novas colunas no final da tabela.
  9. Não criando índices adequados para dar suporte a consultas usadas com freqüência.
  10. criando índices em colunas com poucos valores possíveis e criando índices desnecessários.
    ... mais a ser adicionado.
Frank Computer
fonte
1
Uma queixa: 2) é realmente uma prática ruim. Entendo o que você está obtendo - você deseja um índice exclusivo nessa numeração automática e para usá-lo como uma chave substituta. Mas a chave primária não deve ser uma numeração automática, pois não é isso que é uma chave primária: uma chave primária é "sobre o que é o registro", que (exceto para coisas como transações de vendas) NÃO é a numeração automática, mas um pouco único de informações sobre a entidade que está sendo modelada.
David T. Macknet
o principal motivo para usar a numeração automática para chave primária e estrangeira é garantir que uma junção pai-filho possa ser mantida independentemente das alterações em outras colunas. usar uma chave primária diferente, como nome do cliente ou outros dados, pode ser arriscado!
Frank R.
@ David: Eu estou corrigido! .. não é necessário usar a numeração automática como chave primária, ainda é possível ter uma coluna serial indexada no pai, juntando-se ao substituto no filho para garantir que a relação não será cortada, enquanto houver outra coluna como um primário significativo para localizar a linha!
Frank R.
É uma questão de semântica, no final das contas ... e a Microsoft prefere que as chaves primárias sejam sem sentido e não significativas. Os debates em torno dele continuam, mas eu caio no campo "significativo". :)
David T. Macknet
9
  • Não é necessário fazer um backup antes de corrigir algum problema no banco de dados de produção.

  • Usando comandos DDL em objetos armazenados (como tabelas, visualizações) em procedimentos armazenados.

  • Medo de usar proc armazenado ou medo de usar consultas ORM onde quer que seja mais eficiente / apropriado para uso.

  • Ignorando o uso de um criador de perfil de banco de dados, que pode dizer exatamente para o que sua consulta ORM está sendo convertida finalmente e, portanto, verifique a lógica ou até mesmo para depuração quando não estiver usando o ORM.

WhoIsNinja
fonte
8

Não está fazendo o nível correto de normalização . Você deseja garantir que os dados não sejam duplicados e que os dados sejam divididos em diferentes, conforme necessário. Você também precisa ter certeza de que não está seguindo a normalização muito longe, pois isso prejudicará o desempenho.

Nathan Voxland
fonte
Quão longe é longe demais? Se nenhum dado é duplicado, como você pode ir além?
7115 finnw
Normalização é um equilíbrio entre remover dados redundantes e aumentar a flexibilidade versus desempenho reduzido e complexidade aumentada. Encontrar o equilíbrio correto requer experiência e muda com o tempo. Veja en.wikipedia.org/wiki/Database_normalization para obter informações sobre quando desnormalizar
Nathan Voxland
8

Tratar o banco de dados apenas como um mecanismo de armazenamento (isto é, uma biblioteca de coleções glorificada) e, portanto, subordinado à sua aplicação (ignorando outras aplicações que compartilham os dados)

finnw
fonte
Um corolário disso é descarregar muito trabalho de consulta para o aplicativo, em vez de mantê-lo no banco de dados onde ele pertence. O LINQ é particularmente ruim nisso.
3Dave
8
  • Ignorar um ORM como o Hibernate fora de controle, por motivos como "é muito mágico" ou "não está no meu banco de dados".
  • Confiar demais em um ORM como o Hibernate e tentar calçá-lo onde não é apropriado.
Adam Jaskiewicz
fonte
8

1 - Desnecessariamente usar uma função em um valor em uma cláusula where com o resultado desse índice não sendo usado.

Exemplo:

where to_char(someDate,'YYYYMMDD') between :fromDate and :toDate

ao invés de

where someDate >= to_date(:fromDate,'YYYYMMDD') and someDate < to_date(:toDate,'YYYYMMDD')+1

E em menor grau: não adicionando índices funcionais aos valores que precisam deles ...

2 - Não adicionando restrições de verificação para garantir a validade dos dados. As restrições podem ser usadas pelo otimizador de consultas e REALMENTE ajudam a garantir que você possa confiar em seus invariantes. Não há razão para não usá-los.

3 - Adicionando colunas não normalizadas a tabelas por pura preguiça ou pressão do tempo. As coisas geralmente não são projetadas dessa maneira, mas evoluem para isso. O resultado final, sem falhas, é uma tonelada de trabalho tentando limpar a bagunça quando você é mordido pela integridade dos dados perdidos em futuras evoluções.

Pense nisso: uma tabela sem dados é muito barata para redesenhar. Uma mesa com alguns milhões de registros sem integridade ... não é tão barata para redesenhar. Assim, fazer o design correto ao criar a coluna ou tabela é amortizado em espadas.

4 - não tanto sobre o banco de dados em si, mas de fato irritante. Não se preocupando com a qualidade do código do SQL. O fato de seu SQL ser expresso em texto não permite ocultar a lógica em montes de algoritmos de manipulação de strings. É perfeitamente possível escrever SQL em texto de uma maneira que seja realmente legível pelo seu colega programador.

John Nilsson
fonte
7

Isso já foi dito antes, mas: índices, índices, índices . Eu já vi muitos casos de aplicativos da Web corporativos com baixo desempenho que foram corrigidos simplesmente fazendo um pequeno perfil (para ver quais tabelas estavam sendo muito atingidas) e adicionando um índice nessas tabelas. Isso nem exige muito em termos de conhecimento de escrita em SQL, e a recompensa é enorme.

Evite a duplicação de dados como uma praga. Algumas pessoas defendem que uma pequena duplicação não prejudicará e melhorará o desempenho. Ei, não estou dizendo que você precisa torturar seu esquema para a Terceira Forma Normal, até que seja tão abstrato que nem os DBAs saibam o que está acontecendo. Apenas entenda que sempre que você duplicar um conjunto de nomes, códigos postais ou códigos de remessa, as cópias ficarão fora de sincronia umas com as outras. Isso vai acontecer. E então você estará se movimentando ao executar o script de manutenção semanal.

E por último: use uma convenção de nomenclatura clara, consistente e intuitiva. Da mesma maneira que um código bem escrito deve ser legível, um bom esquema ou consulta SQL deve ser legível e praticamente informar o que está fazendo, mesmo sem comentários. Você se agradecerá em seis meses, quando precisar fazer manutenção nas mesas. "SELECT account_number, billing_date FROM national_accounts"é infinitamente mais fácil trabalhar com "SELECT ACCNTNBR, BILLDAT FROM NTNLACCTS".

pbailey19
fonte
Se você configurá-las corretamente, elas não serão, mas isso envolve o uso de gatilhos aos quais muitas pessoas são alérgicas.
HLGEM 20/10/09
6

Não executando uma consulta SELECT correspondente antes de executar a consulta DELETE (principalmente nos bancos de dados de produção)!

Jamol
fonte
5

O erro mais comum que já vi em vinte anos: não planejar com antecedência. Muitos desenvolvedores criarão um banco de dados e tabelas e, em seguida, modificarão e expandirão continuamente as tabelas à medida que desenvolvem os aplicativos. O resultado final costuma ser uma bagunça, ineficiente e difícil de limpar ou simplificar mais tarde.

Skatterbrainz
fonte
1
Eu posso imaginar os horrores que resultam nessas situações ... Os bancos de dados sem esquemas são muito melhores para prototipagem rápida e desenvolvimento iterativo, mas, como todo o resto, essa flexibilidade vem com várias vantagens e desvantagens.
Zsolt Török
4

a) Codificar valores de consulta na string
b) Colocar o código de consulta do banco de dados na ação "OnButtonPress" em um aplicativo Windows Forms

Eu vi os dois.

Benoit
fonte
4
"Colocando o código de consulta do banco de dados na ação" OnButtonPress "em um aplicativo Windows Form" Qual é o erro do banco de dados aqui?
recursivo
@ recursivo: é uma enorme vulnerabilidade à injeção de SQL. Qualquer um pode enviar SQL arbitrário para o seu servidor e ele será executado literalmente.
Bill Karwin
Concordou com @recursive. Isso realmente não tem nada a ver com problemas de banco de dados.
p.campbell
b) é um erro de arquitetura. Obviamente, codificar consultas diretamente no seu aplicativo é uma má ideia.
3Dave
4

Não prestando atenção suficiente no gerenciamento de conexões com o banco de dados em seu aplicativo. Então você descobre que o aplicativo, o computador, o servidor e a rede estão entupidos.

chefsmart
fonte
4
  1. Pensando que são DBAs e modeladores / projetistas de dados quando não têm doutrinação formal de nenhum tipo nessas áreas.

  2. Pensando que o projeto deles não requer um DBA, porque tudo isso é fácil / trivial.

  3. Falha em discernir adequadamente entre o trabalho que deve ser realizado no banco de dados e o trabalho que deve ser realizado no aplicativo.

  4. Não validando backups ou não fazendo backup.

  5. Incorporando SQL bruto em seu código.

jonesy
fonte
3

Não entender o modelo de concorrência de bancos de dados e como isso afeta o desenvolvimento. É fácil adicionar índices e ajustar consultas após o fato. No entanto, os aplicativos projetados sem a devida consideração para pontos de acesso, contenção de recursos e operação correta (supondo que o que você acabou de ler ainda seja válido!) Podem exigir alterações significativas no banco de dados e na camada do aplicativo para corrigir posteriormente.

Einstein
fonte
3

Não entendo como um DBMS funciona sob o capô.

Você não pode dirigir corretamente uma alavanca sem entender como uma embreagem funciona. E você não consegue entender como usar um banco de dados sem entender que realmente está apenas gravando em um arquivo no seu disco rígido.

Especificamente:

  1. Você sabe o que é um Índice de Cluster? Você pensou nisso quando projetou seu esquema?

  2. Você sabe como usar índices corretamente? Como reutilizar um índice? Você sabe o que é um índice de cobertura?

  3. Tão bom, você tem índices. Qual o tamanho de uma linha no seu índice? Qual será o tamanho do índice quando você tiver muitos dados? Isso caberá facilmente na memória? Caso contrário, é inútil como índice.

  4. Você já usou EXPLAIN no MySQL? Ótimo. Agora seja honesto consigo mesmo: você entendeu metade do que viu? Não, você provavelmente não. Conserte isso.

  5. Você entende o cache de consulta? Você sabe o que torna uma consulta incomensurável?

  6. Você está usando o MyISAM? Se você PRECISA de pesquisa de texto completo, o MyISAM é uma porcaria de qualquer maneira. Use Sphinx. Depois mude para Inno.

Shane H
fonte
2
Uma analogia melhor pode ser que não se pode solucionar adequadamente uma transmissão manual sem entender uma embreagem. Muitas pessoas dirigem corretamente uma troca de marchas sem saber como uma embreagem funciona.
Michael Easter
3
  1. Usando um ORM para fazer atualizações em massa
  2. Selecionando mais dados do que o necessário. Novamente, normalmente feito ao usar um ORM
  3. Disparando sqls em um loop.
  4. Não tendo bons dados de teste e observando a degradação do desempenho apenas em dados ativos.
Sriram
fonte