Errores de desarrollo de bases de datos cometidos por desarrolladores de aplicaciones [cerrado]


566

¿Cuáles son los errores comunes de desarrollo de bases de datos cometidos por los desarrolladores de aplicaciones?


Respuestas:


1002

1. No usar índices apropiados

Esta es relativamente fácil pero aún sucede todo el tiempo. Las claves foráneas deben tener índices en ellas. Si está utilizando un campo en un WHERE(probablemente) debería tener un índice en él. Dichos índices a menudo deberían cubrir varias columnas en función de las consultas que necesita ejecutar.

2. No hacer cumplir la integridad referencial

Su base de datos puede variar aquí, pero si su base de datos admite integridad referencial, lo que significa que se garantiza que todas las claves externas apuntan a una entidad que existe, debería usarla.

Es bastante común ver esta falla en las bases de datos MySQL. No creo que MyISAM lo admita. InnoDB lo hace. Encontrará personas que usan MyISAM o aquellas que usan InnoDB pero que no lo están usando de todos modos.

Más aquí:

3. Uso de claves primarias naturales en lugar de sustitutas (técnicas)

Las claves naturales son claves basadas en datos significativos externos que son (aparentemente) únicos. Ejemplos comunes son códigos de productos, códigos de estado de dos letras (EE. UU.), Números de seguridad social, etc. Las claves primarias sustitutas o técnicas son aquellas que no tienen absolutamente ningún significado fuera del sistema. Se inventan exclusivamente para identificar la entidad y, por lo general, son campos de incremento automático (SQL Server, MySQL, otros) o secuencias (especialmente Oracle).

En mi opinión, siempre debes usar claves sustitutas. Este problema ha surgido en estas preguntas:

Este es un tema algo controvertido sobre el que no obtendrá un acuerdo universal. Si bien puede encontrar algunas personas, que piensan que las claves naturales están bien en algunas situaciones, no encontrará ninguna crítica a las claves sustitutivas que no sean posiblemente innecesarias. Eso es un pequeño inconveniente si me preguntas.

Recuerde, incluso los países pueden dejar de existir (por ejemplo, Yugoslavia).

4. Escribir consultas que requieren DISTINCTtrabajar

A menudo ve esto en consultas generadas por ORM. Mire la salida del registro de Hibernate y verá que todas las consultas comienzan con:

SELECT DISTINCT ...

Este es un atajo para garantizar que no devuelva filas duplicadas y, por lo tanto, obtenga objetos duplicados. A veces verás gente haciendo esto también. Si lo ves demasiado, es una verdadera bandera roja. No DISTINCTes que sea ​​malo o no tenga aplicaciones válidas. Lo hace (en ambos casos) pero no es un sustituto o un recurso provisional para escribir consultas correctas.

Por qué odio DISTINCT :

En mi opinión, cuando las cosas comienzan a empeorar es cuando un desarrollador está creando consultas sustanciales, uniendo tablas, y de repente se da cuenta de que parece que está obteniendo filas duplicadas (o incluso más) y su respuesta inmediata ... su "solución" a este "problema" es lanzar la palabra clave DISTINCT y POOF todos sus problemas desaparecen.

5. Favorecer la agregación sobre las uniones

Otro error común de los desarrolladores de aplicaciones de bases de datos es no darse cuenta de cuánto GROUP BYse puede comparar la agregación más costosa (es decir, la cláusula) con las uniones.

Para darle una idea de cuán extendido está esto, he escrito sobre este tema varias veces aquí y me han rechazado mucho. Por ejemplo:

De la instrucción SQL: "unirse" frente a "agrupar por y que tiene" :

Primera consulta:

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

Tiempo de 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

Tiempo de consulta: 0.016 s

Así es. La versión de unión que propuse es veinte veces más rápida que la versión agregada.

6. No simplificar consultas complejas a través de vistas

No todos los proveedores de bases de datos admiten vistas, pero para aquellos que lo hacen, pueden simplificar enormemente las consultas si se usan con prudencia. Por ejemplo, en un proyecto utilicé un modelo genérico de Party para CRM. Esta es una técnica de modelado extremadamente potente y flexible, pero puede conducir a muchas uniones. En este modelo había:

  • Partido : personas y organizaciones;
  • Rol de la fiesta : cosas que hicieron esas partes, por ejemplo, Empleado y Empleador;
  • Relación de roles de partido : cómo esos roles se relacionan entre sí.

Ejemplo:

  • Ted es una Persona, siendo un subtipo de Partido;
  • Ted tiene muchos roles, uno de los cuales es Empleado;
  • Intel es una organización, que es un subtipo de una Parte;
  • Intel tiene muchos roles, uno de los cuales es Empleador;
  • Intel emplea a Ted, lo que significa que existe una relación entre sus respectivos roles.

Entonces, hay cinco tablas unidas para vincular a Ted con su empleador. Asume que todos los empleados son Personas (no organizaciones) y proporciona esta vista 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

Y de repente tiene una vista muy simple de los datos que desea, pero en un modelo de datos altamente flexible.

7. No desinfección de entrada

Este es uno enorme. Ahora me gusta PHP, pero si no sabes lo que estás haciendo, es muy fácil crear sitios vulnerables a los ataques. Nada lo resume mejor que la historia de las pequeñas Bobby Tables .

Los datos proporcionados por el usuario a través de URL, datos de formulario y cookies siempre deben tratarse como hostiles y desinfectados. Asegúrese de obtener lo que espera.

8. No usar declaraciones preparadas

Las declaraciones preparadas son cuando compila una consulta menos los datos utilizados en inserciones, actualizaciones y WHEREcláusulas y luego la proporciona más tarde. Por ejemplo:

SELECT * FROM users WHERE username = 'bob'

vs

SELECT * FROM users WHERE username = ?

o

SELECT * FROM users WHERE username = :username

Dependiendo de su plataforma.

He visto bases de datos arrodilladas al hacer esto. Básicamente, cada vez que una base de datos moderna encuentra una nueva consulta, tiene que compilarla. Si encuentra una consulta que se ha visto antes, le está dando a la base de datos la oportunidad de almacenar en caché la consulta compilada y el plan de ejecución. Al hacer mucho la consulta, le está dando a la base de datos la oportunidad de resolverlo y optimizarlo en consecuencia (por ejemplo, al anclar la consulta compilada en la memoria).

El uso de declaraciones preparadas también le dará estadísticas significativas sobre la frecuencia con la que se usan ciertas consultas.

Las declaraciones preparadas también lo protegerán mejor contra los ataques de inyección SQL.

9. No se normaliza lo suficiente

La normalización de la base de datos es básicamente el proceso de optimizar el diseño de la base de datos o cómo organizar sus datos en tablas.

Justo esta semana me encontré con un código en el que alguien había implosionado una matriz y la había insertado en un solo campo en una base de datos. Normalizar eso sería tratar el elemento de esa matriz como una fila separada en una tabla secundaria (es decir, una relación de uno a muchos).

Esto también apareció en el mejor método para almacenar una lista de ID de usuario :

He visto en otros sistemas que la lista se almacena en una matriz PHP serializada.

Pero la falta de normalización viene en muchas formas.

Más:

10. Normalizando demasiado

Esto puede parecer una contradicción con el punto anterior, pero la normalización, como muchas cosas, es una herramienta. Es un medio para un fin y no un fin en sí mismo. Creo que muchos desarrolladores olvidan esto y comienzan a tratar un "medio" como un "fin". Las pruebas unitarias son un excelente ejemplo de esto.

Una vez trabajé en un sistema que tenía una gran jerarquía para clientes que era algo así como:

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

de modo que tenía que unir unas 11 tablas antes de poder obtener datos significativos. Fue un buen ejemplo de normalización llevado demasiado lejos.

Más concretamente, la desnormalización cuidadosa y considerada puede tener enormes beneficios de rendimiento, pero debe tener mucho cuidado al hacer esto.

Más:

11. Usando arcos exclusivos

Un arco exclusivo es un error común cuando se crea una tabla con dos o más claves foráneas donde una y solo una de ellas puede ser no nula. Gran error. Por un lado, se vuelve mucho más difícil mantener la integridad de los datos. Después de todo, incluso con integridad referencial, nada impide que se configuren dos o más de estas claves foráneas (a pesar de las complejas restricciones de verificación).

De una guía práctica para el diseño de bases de datos relacionales :

Hemos desaconsejado encarecidamente la construcción de arcos exclusivos siempre que sea posible, por la buena razón de que pueden ser incómodos escribir código y plantear más dificultades de mantenimiento.

12. No hacer análisis de rendimiento en consultas en absoluto

El pragmatismo reina supremamente, particularmente en el mundo de las bases de datos. Si te apegas a los principios hasta el punto de que se han convertido en un dogma, entonces probablemente hayas cometido errores. Tome el ejemplo de las consultas agregadas de arriba. La versión agregada puede parecer "agradable" pero su rendimiento es lamentable. Una comparación de rendimiento debería haber terminado el debate (pero no lo hizo), pero más importante: decir esas opiniones mal informadas en primer lugar es ignorante, incluso peligroso.

13. Dependencia excesiva de UNION ALL y particularmente de las construcciones UNION

Una UNIÓN en términos de SQL simplemente concatena conjuntos de datos congruentes, lo que significa que tienen el mismo tipo y número de columnas. La diferencia entre ellos es que UNION ALL es una concatenación simple y debe preferirse siempre que sea posible, mientras que UNION hará una DISTINCIÓN implícita para eliminar las tuplas duplicadas.

Los SINDICATOS, como DISTINCT, tienen su lugar. Hay aplicaciones válidas Pero si te encuentras haciendo muchas de ellas, particularmente en subconsultas, entonces probablemente estés haciendo algo mal. Ese podría ser un caso de construcción de consulta deficiente o un modelo de datos mal diseñado que lo obligue a hacer tales cosas.

Los UNION, particularmente cuando se usan en uniones o subconsultas dependientes, pueden paralizar una base de datos. Intenta evitarlos siempre que sea posible.

14. Uso de condiciones OR en consultas

Esto puede parecer inofensivo. Después de todo, los AND están bien. O debería estar bien también ¿verdad? Incorrecto. Básicamente, una condición AND restringe el conjunto de datos mientras que una condición OR lo hace crecer , pero no de una manera que se preste a la optimización. Particularmente cuando las diferentes condiciones OR pueden cruzarse, forzando al optimizador a una operación DISTINCT con eficacia en el resultado.

Malo:

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

Mejor:

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

Ahora su optimizador SQL puede convertir efectivamente la primera consulta en la segunda. Pero puede que no. Solo no lo hagas.

15. No diseñar su modelo de datos para prestarse a soluciones de alto rendimiento

Este es un punto difícil de cuantificar. Normalmente se observa por su efecto. Si se encuentra escribiendo consultas retorcidas para tareas relativamente simples o si las consultas para encontrar información relativamente sencilla no son eficientes, entonces probablemente tenga un modelo de datos deficiente.

De alguna manera, este punto resume todos los anteriores, pero es más una advertencia que hacer cosas como la optimización de consultas a menudo se hace primero cuando debería hacerse en segundo lugar. En primer lugar, debe asegurarse de tener un buen modelo de datos antes de intentar optimizar el rendimiento. Como dijo Knuth:

La optimización temprana es la raíz de todo mal

16. Uso incorrecto de las transacciones de la base de datos

Todos los cambios de datos para un proceso específico deben ser atómicos. Es decir, si la operación tiene éxito, lo hace completamente. Si falla, los datos se dejan sin cambios. - No debería haber posibilidad de cambios 'a medio hacer'.

Idealmente, la forma más sencilla de lograr esto es que todo el diseño del sistema debe esforzarse por admitir todos los cambios de datos a través de declaraciones individuales INSERT / UPDATE / DELETE. En este caso, no se necesita un manejo de transacciones especial, ya que su motor de base de datos debería hacerlo automáticamente.

Sin embargo, si algún proceso requiere que se realicen múltiples declaraciones como una unidad para mantener los datos en un estado consistente, entonces es necesario el Control de Transacción apropiado.

  • Comience una transacción antes de la primera declaración.
  • Confirmar la transacción después de la última declaración.
  • En cualquier error, deshaga la transacción. Y muy NB! No olvide omitir / cancelar todas las declaraciones que siguen después del error.

También se recomienda prestar especial atención a las sutilezas de cómo la capa de conectividad de la base de datos y el motor de la base de datos interactúan a este respecto.

17. No entender el paradigma "basado en conjuntos"

El lenguaje SQL sigue un paradigma específico adecuado para tipos específicos de problemas. A pesar de varias extensiones específicas del proveedor, el lenguaje tiene dificultades para lidiar con problemas que son triviales en idiomas como Java, C #, Delphi, etc.

Esta falta de comprensión se manifiesta de varias maneras.

  • Imponer inadecuadamente demasiada lógica de procedimiento o imperativa en la base de datos.
  • Uso inapropiado o excesivo de cursores. Especialmente cuando una sola consulta sería suficiente.
  • Asumiendo incorrectamente que desencadena el fuego una vez por fila afectada en las actualizaciones de varias filas.

Determine una división clara de la responsabilidad y procure utilizar la herramienta adecuada para resolver cada problema.


99
En las declaraciones de MySQL sobre claves foráneas, tiene razón en que MyISAM no las admite, pero implica que simplemente usar MyISAM es un mal diseño. Una razón por la que he usado MyISAM es que InnoDB no admite búsquedas de texto completo, y no creo que sea irrazonable.
Derek H

1
Tengo que preguntar sobre el # 6. Usar vistas como esta es una de mis actividades favoritas, pero recientemente aprendí, para mi horror, que con los índices de MySQL en las tablas subyacentes solo se obedecen si la estructura de la vista permite el uso del algoritmo de fusión. De lo contrario, se usa una tabla temporal y todos sus índices son inútiles. Es aún más alarmante cuando te das cuenta de que un montón de operaciones causan este comportamiento. Es una excelente manera de convertir una consulta de 0.01 segundos en una de 100 segundos. ¿Alguien más aquí tiene experiencia con esto? Verifique los enlaces en mi próximo comentario.
Peter Bailey

55
Completamente en desacuerdo con el n. ° 3. Sí, los países pueden dejar de existir, pero el código del país seguirá representando lo mismo. Lo mismo con los códigos de moneda o los Estados Unidos. Es tonto usar una clave sustituta en estos casos y crea más sobrecarga en sus consultas, ya que debe incluir una combinación adicional. Diría que es más seguro decir que probablemente debería usar un sustituto para datos específicos del usuario (por lo tanto, no países, monedas y estados de EE. UU.).
Thomas

1
RE: # 11 La restricción de verificación necesaria para hacer cumplir la integridad de los datos es trivial. Hay otras razones para evitar ese diseño, pero la necesidad de una restricción de verificación "compleja" no es una de ellas.
Thomas

2
Con el # 3 no estás siendo honesto. Hay más desventajas en la clave artificial que "es posible que no la necesite". Específicamente, el uso de una clave natural le dará la capacidad de controlar el orden en que los datos de su tabla se escriben en el disco. Si sabe cómo se consultará su tabla, puede indexarla para que las filas a las que se accede simultáneamente terminen en la misma página. Además, puede exigir la integridad de los datos utilizando un índice compuesto único. Si necesita esto, deberá agregarlo además de su índice de clave artificial. Si dicho índice compuesto es su clave, son 2 pájaros muertos de un tiro.
Shane H

110

Errores clave de diseño y programación de bases de datos cometidos por desarrolladores

  • Diseño y uso de bases de datos egoístas. Los desarrolladores a menudo tratan la base de datos como su almacén personal de objetos persistentes sin tener en cuenta las necesidades de otros interesados ​​en los datos. Esto también se aplica a los arquitectos de aplicaciones. El diseño deficiente de la base de datos y la integridad de los datos dificultan que terceros trabajen con los datos y pueden aumentar sustancialmente los costos del ciclo de vida del sistema. Los informes y MIS tienden a ser un primo pobre en el diseño de aplicaciones y solo se hacen como una ocurrencia tardía.

  • Abusar de datos desnormalizados. Exagerar los datos desnormalizados e intentar mantenerlos dentro de la aplicación es una receta para los problemas de integridad de los datos. Use la desnormalización con moderación. No querer agregar una unión a una consulta no es una excusa para denormalizar.

  • Miedo de escribir SQL. SQL no es ciencia espacial y en realidad es bastante bueno para hacer su trabajo. Las capas de mapeo O / R son bastante buenas para hacer el 95% de las consultas que son simples y se ajustan bien a ese modelo. A veces, SQL es la mejor manera de hacer el trabajo.

  • Políticas dogmáticas de "procedimientos no almacenados". Independientemente de si crees que los procedimientos almacenados son malos, este tipo de actitud dogmática no tiene cabida en un proyecto de software.

  • No entiendo el diseño de la base de datos. La normalización es tu amiga y no es ciencia espacial. La unión y la cardinalidad son conceptos bastante simples: si está involucrado en el desarrollo de aplicaciones de bases de datos, realmente no hay excusa para no comprenderlos.


2
Se podría argumentar que las transacciones deben hacerse en una base de datos transaccional y en informes, y que el MIS debe hacerse en una base de datos de análisis separada. Por lo tanto, obtienes lo mejor de ambos mundos y todos están contentos (a excepción de la pobre taza que tiene que escribir el script de transformación de datos para construir el último a partir del primero).
Chris Simpson

No solo la mala taza que escribe el ETL: cualquiera que use datos del sistema, los datos de baja calidad en la aplicación MIS que se incluye en el recuadro porque varias relaciones clave no se registran realmente en la fuente, cualquier persona involucrada en los interminables concilios de reconciliación que resultan de la mala calidad de los datos.
ConcernedOfTunbridgeWells

No podría estar más en desacuerdo con el punto uno. Las bases de datos son para persistencia, no para comunicación entre procesos. Casi siempre hay mejores soluciones para ese problema. A menos que exista un requisito explícito para ello, DEBE tratar la base de datos como si nadie, excepto su aplicación, la utilizara. Incluso si existe un requisito explícito, haga un análisis de la historia del usuario y la causa raíz y con frecuencia descubrirá una forma mucho mejor de cumplir con la intención del solicitante. Por otra parte, trabajo en una compañía donde la frase CQRS es algo común
George Mauer,

3
Ejemplo trivial: tengo un sistema de administración de pólizas de seguro y necesito cargar el estado de 5 millones de reclamos en un sistema de reaseguro cedido para calcular posibles recuperaciones. Los sistemas son paquetes COTS cliente-servidor más antiguos, diseñados para interactuar con sistemas mainframe incluso más antiguos. Ambos tienen que ser reconciliados para fines de control financiero. Este trabajo se realiza una vez al mes. Según su lógica, escribiría una serie de historias de usuarios que definen los requisitos y pediría a los proveedores que coticen sobre la adición de un contenedor de servicios web a sus productos existentes.
Preocupado por

2
Entonces su DBA es perezoso o incompetente.
Preocupado por

80
  1. No usar el control de versiones en el esquema de la base de datos
  2. Trabajando directamente contra una base de datos en vivo
  3. No leer y comprender conceptos de bases de datos más avanzados (índices, índices agrupados, restricciones, vistas materializadas, etc.)
  4. No probar la escalabilidad ... los datos de prueba de solo 3 o 4 filas nunca le darán una imagen real del rendimiento real en vivo

1
Yo segundo, fuertemente, # 1 y # 2. Cada vez que hago un cambio en la base de datos, vuelco su esquema y lo versiono; Tengo tres bases de datos configuradas, una de desarrollo, una de preparación y una en vivo - ¡NADA se "prueba" en el DB en vivo!
Ixmatus

¡Aquí en Red Gate hemos tomado medidas para mejorar su primer punto con SQL Source Control! De las conversaciones que he tenido durante mi investigación, creo que la gente ya no se está desarrollando contra bases de datos de producción, pero a menudo se hacen arreglos de "emergencia" que generalmente encuentran su camino de regreso a los entornos de desarrollo, lo cual es otro problema.
David Atkinson

46

Uso excesivo y / o dependencia de procedimientos almacenados.

Algunos desarrolladores de aplicaciones ven los procedimientos almacenados como una extensión directa del código de nivel medio / front-end. Esto parece ser un rasgo común en los desarrolladores de pila de Microsoft (soy uno, pero he superado) y produce muchos procedimientos almacenados que realizan una lógica empresarial compleja y un procesamiento de flujo de trabajo. Esto se hace mucho mejor en otro lugar.

Los procedimientos almacenados son útiles cuando se ha comprobado que algún factor técnico real necesita su uso (por ejemplo, rendimiento y seguridad). Por ejemplo, mantener la agregación / filtrado de grandes conjuntos de datos "cerca de los datos".

Recientemente tuve que ayudar a mantener y mejorar una gran aplicación de escritorio Delphi, de la cual el 70% de la lógica y las reglas del negocio se implementaron en 1400 procedimientos almacenados de SQL Server (el resto en los controladores de eventos de la interfaz de usuario). Esto fue una pesadilla, principalmente debido a la dificultad de introducir pruebas unitarias efectivas en TSQL, falta de encapsulación y herramientas deficientes (depuradores, editores).

Trabajando con un equipo de Java en el pasado, descubrí rápidamente que a menudo ocurre todo lo contrario en ese entorno. Un arquitecto de Java me dijo una vez: "La base de datos es para datos, no para código".

En estos días creo que es un error no tener en cuenta los procedimientos almacenados, pero deberían usarse con moderación (no de forma predeterminada) en situaciones en las que proporcionan beneficios útiles (ver las otras respuestas).


44
Los procedimientos almacenados tienden a convertirse en una isla dañada en cualquier proyecto en el que se utilizan, por lo que algunos desarrolladores establecen la regla "No hay procedimientos almacenados". Entonces parece que hay un conflicto abierto entre ellos. Su respuesta es un buen caso para cuándo elegir realmente una forma u otra.
Warren P

Beneficios: seguridad: no tiene que dar a las aplicaciones la capacidad de "eliminar * de ..."; ajustes: los DBA pueden modificar las consultas sin tener que volver a compilar / implementar toda la aplicación; análisis: es fácil volver a compilar un conjunto de procedimientos después de un cambio en el modelo de datos para garantizar que sigan siendo válidos; y, finalmente, considerando que SQL es ejecutado por el motor de la base de datos (no por su aplicación), entonces el concepto de "base de datos es para datos, no para código" simplemente se retrasa.
NotMe

Entonces, ¿enredaría su lógica de negocios en la interfaz de usuario, donde se divorció de los datos que se manipulan? Esto no parece una buena idea, particularmente porque la manipulación de datos es más eficiente cuando la realiza el servidor de la base de datos en lugar de los viajes de ida y vuelta desde la interfaz de usuario. Eso también significa que es más difícil controlar la aplicación porque no puede confiar en que la base de datos controle sus datos y potencialmente tenga diferentes versiones de una interfaz de usuario con diferentes manipulaciones de datos. No está bien. No dejo que nada toque mis datos, excepto a través de un procedimiento almacenado.
David T. Macknet

Si es necesario separar la lógica empresarial de la interfaz de usuario, se pueden utilizar arquitecturas de niveles múltiples. O bien, una biblioteca con objetos comerciales y lógica, utilizada por diferentes aplicaciones / UI. Los procedimientos almacenados bloquean sus datos / lógica de negocios en una base de datos específica, por lo que cambiar una base de datos en este caso es muy costoso. Y el gran costo es malo.
también el

@too: Cambiar una base de datos en la mayoría de los casos es muy costoso. No importa la idea de perder el rendimiento y las características de seguridad que proporciona un DBMS en particular. Además, los niveles adicionales agregan complejidad y disminuyen el rendimiento y las capas adicionales están vinculadas a su idioma particular. Finalmente, es más probable que cambie el idioma que se usa que un servidor de base de datos.
NotMe

41

Problema número uno? Solo prueban en bases de datos de juguetes. Por lo tanto, no tienen idea de que su SQL se arrastrará cuando la base de datos crezca, y alguien tiene que venir y arreglarlo más tarde (ese sonido que puedes escuchar es el rechinar de mis dientes).


2
El tamaño de la base de datos es relevante, pero un problema mayor es la carga: incluso si prueba en un conjunto de datos real, no está probando el rendimiento de sus consultas cuando la base de datos está bajo una carga de producción, lo que puede ser una verdadera revelación.
davidcl

Yo diría que el tamaño de la base de datos es un problema mayor que la carga. He visto muchas veces que faltaban índices cruciales, nunca hubo problemas de rendimiento durante las pruebas, porque toda la base de datos se ajustaba a la memoria
Danubian Sailor


28

Bajo rendimiento causado por subconsultas correlacionadas

La mayoría de las veces desea evitar subconsultas correlacionadas. Una subconsulta está correlacionada si, dentro de la subconsulta, hay una referencia a una columna de la consulta externa. Cuando esto sucede, la subconsulta se ejecuta al menos una vez por cada fila devuelta y podría ejecutarse más veces si se aplican otras condiciones después de aplicar la condición que contiene la subconsulta correlacionada.

Perdone el ejemplo artificial y la sintaxis de Oracle, pero supongamos que desea encontrar a todos los empleados que han sido contratados en cualquiera de sus tiendas desde la última vez que la tienda realizó menos de $ 10,000 de ventas en un día.

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)

La subconsulta en este ejemplo está correlacionada con la consulta externa por store_id y se ejecutará para cada empleado en su sistema. Una forma de optimizar esta consulta es mover la subconsulta a una vista en línea.

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

En este ejemplo, la consulta en la cláusula from ahora es una vista en línea (nuevamente una sintaxis específica de Oracle) y solo se ejecuta una vez. Dependiendo de su modelo de datos, esta consulta probablemente se ejecutará mucho más rápido. Funcionaría mejor que la primera consulta a medida que aumentaba el número de empleados. La primera consulta podría funcionar mejor si hubiera pocos empleados y muchas tiendas (y tal vez muchas de las tiendas no tenían empleados) y la tabla daily_sales se indexó en store_id. Este no es un escenario probable, pero muestra cómo una consulta correlacionada podría funcionar mejor que una alternativa.

He visto a desarrolladores junior correlacionar subconsultas muchas veces y generalmente ha tenido un impacto severo en el rendimiento. Sin embargo, al eliminar una subconsulta correlacionada, asegúrese de mirar el plan de explicación antes y después para asegurarse de que no está empeorando el rendimiento.


1
Gran punto, y para enfatizar uno de sus puntos relacionados: pruebe sus cambios. Aprenda a usar los planes de explicación (y vea lo que la base de datos está haciendo realmente para ejecutar su consulta y lo que cuesta), haga sus pruebas en un gran conjunto de datos y no haga que su SQL sea demasiado complejo e ilegible / imposible de mantener para una optimización eso en realidad no mejora el rendimiento real.
Rob Whelan

21

En mi experiencia:
no se comunica con DBA con experiencia.


17

Usar Access en lugar de una base de datos "real". Hay muchas bases de datos pequeñas e incluso gratuitas, como SQL Express , MySQL y SQLite, que funcionarán y escalarán mucho mejor. Las aplicaciones a menudo necesitan escalar de maneras inesperadas.


16

Olvidarse de establecer relaciones entre las tablas. Recuerdo haber tenido que limpiar esto cuando comencé a trabajar en mi empleador actual.


14

Uso de Excel para almacenar (grandes cantidades de) datos.

He visto compañías que tienen miles de filas y usan múltiples hojas de trabajo (debido al límite de filas de 65535 en versiones anteriores de Excel).


Excel es muy adecuado para informes, presentación de datos y otras tareas, pero no debe tratarse como una base de datos.


14

Me gustaría agregar: Favorecer el código "Elegante" sobre el código de alto rendimiento. El código que funciona mejor contra las bases de datos suele ser feo para el desarrollador de aplicaciones.

Creyendo esas tonterías sobre la optimización prematura. Las bases de datos deben considerar el rendimiento en el diseño original y en cualquier desarrollo posterior. El rendimiento es el 50% del diseño de la base de datos (el 40% es integridad de datos y el último 10% es seguridad) en mi opinión. Las bases de datos que no se crean de abajo hacia arriba funcionarán mal una vez que los usuarios reales y el tráfico real se coloquen en la base de datos. ¡La optimización prematura no significa que no haya optimización! No significa que deba escribir código que casi siempre funcionará mal porque le resulta más fácil (por ejemplo, cursores que nunca deberían permitirse en una base de datos de producción a menos que todo lo demás haya fallado). Significa que no necesita buscar exprimir ese último pequeño rendimiento hasta que lo necesite. Se sabe mucho sobre qué funcionará mejor en las bases de datos,


2
+1 - La programación de la base de datos implica optimizar el comportamiento de los componentes mecánicos. Sin embargo, tenga en cuenta que Knuth dice que la optimización prematura es la raíz de todo mal alrededor del 97% del tiempo (o palabras en ese sentido). El diseño de la base de datos es un área en la que realmente tiene que pensar en esto por adelantado.
Preocupado por

2
Ejem ... de lo que estás hablando es de una optimización que no es prematura. Se requiere cierta consideración del uso real desde el principio en el diseño de la base de datos (y el diseño de la aplicación también, realmente). La regla de Knuth en realidad no es trivial de seguir, porque tienes que decidir qué es prematuro y qué no, realmente se reduce a "no realizar optimizaciones sin datos". Las primeras decisiones relacionadas con el rendimiento de las que está hablando tienen datos: ciertos diseños establecerán límites inaceptables en el rendimiento futuro, y puede calcularlos.
Rob Whelan

13

No utiliza consultas parametrizadas. Son bastante útiles para detener la inyección de SQL .

Este es un ejemplo específico de no desinfectar los datos de entrada, mencionados en otra respuesta.


3
Excepto que la entrada de desinfección es incorrecta. Desinfectar implica colocarlo en un lugar donde pueda ser peligroso. Parametrizar significa mantenerlo fuera del camino del daño por completo.
Dustin

12

Odio cuando los desarrolladores usan sentencias select anidadas o incluso funciones que devuelven el resultado de una sentencia select dentro de la parte "SELECCIONAR" de una consulta.

Estoy realmente sorprendido de no ver esto en ningún otro lugar aquí, tal vez lo pasé por alto, aunque @adam tiene un problema similar indicado.

Ejemplo:

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

En este escenario, si MyTable devuelve 10000 filas, el resultado es como si la consulta acabara de ejecutar consultas 20001, ya que tenía que ejecutar la consulta inicial más la consulta de cada una de las otras tablas una vez para cada línea de resultado.

Los desarrolladores pueden salirse con la suya trabajando en un entorno de desarrollo en el que solo devuelven unas pocas filas de datos y las subtablas generalmente solo tienen una pequeña cantidad de datos, pero en un entorno de producción, este tipo de consulta puede volverse exponencialmente costoso a medida que aumenta los datos se agregan a las tablas.

Un ejemplo mejor (no necesariamente perfecto) sería 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

Esto permite que los optimizadores de bases de datos mezclen los datos juntos, en lugar de consultar en cada registro de la tabla principal y generalmente encuentro cuando tengo que arreglar el código donde se creó este problema, generalmente termino aumentando la velocidad de las consultas en un 100% o más mientras reduce simultáneamente el uso de CPU y memoria.


12

Para bases de datos basadas en SQL:

  1. No aprovechar los ÍNDICES AGRUPADOS ni elegir las columnas incorrectas para AGRUPAR.
  2. No usar un tipo de datos SERIAL (número automático) como CLAVE PRIMARIA para unirse a una CLAVE EXTRANJERA (INT) en una relación de tabla padre / hijo.
  3. No ACTUALIZAR ESTADÍSTICAS en una tabla cuando se han INSERTADO o BORRADO muchos registros.
  4. No reorganizar (es decir, descargar, dejar caer, volver a crear, cargar y volver a indexar) tablas cuando se han insertado o eliminado muchas filas (algunos motores mantienen físicamente las filas eliminadas en una tabla con un indicador de eliminación).
  5. No aprovechar FRAGMENTO DE EXPRESIÓN (si se admite) en tablas grandes que tienen altas tasas de transacción.
  6. ¡Elegir el tipo de datos incorrecto para una columna!
  7. No elegir un nombre de columna adecuado.
  8. No agregar nuevas columnas al final de la tabla.
  9. No se crean índices adecuados para admitir consultas de uso frecuente.
  10. creando índices en columnas con pocos valores posibles y creando índices innecesarios.
    ... más para agregar.

1
Una objeción: 2) en realidad es una mala práctica. Veo a lo que te refieres: quieres un índice único en ese número automático y usarlo como una clave sustituta. Pero la clave principal no debe ser un número automático, ya que esa no es la clave principal: una clave primaria es "de qué se trata el registro", que (excepto para cosas como las transacciones de ventas) NO es el número automático, sino un bit único de información sobre la entidad que se está modelando.
David T. Macknet

La razón principal para utilizar el número automático para la clave primaria y externa es garantizar que una unión padre-hijo se pueda mantener independientemente de los cambios en cualquier otra columna. ¡Usar una clave primaria diferente, como el nombre del cliente u otros datos, puede ser riesgoso!
Frank R.

@David: ¡Estoy corregido! ... no es necesario usar el número automático como clave principal, todavía se puede tener una columna serial indexada en el padre, uniéndose al sustituto en el niño para garantizar que la relación no se rompa, mientras se tiene otra columna como un primario significativo para localizar la fila!
Frank R.

Es una cuestión de semántica, al final del día ... y Microsoft prefiere que las claves primarias no tengan sentido, en lugar de tener sentido. Los debates a su alrededor continúan, pero caigo en el campo "significativo". :)
David T. Macknet

9
  • No realizar una copia de seguridad antes de solucionar algún problema dentro de la base de datos de producción.

  • Usar comandos DDL en objetos almacenados (como tablas, vistas) en procedimientos almacenados.

  • Miedo a usar procesos almacenados o miedo a usar consultas ORM donde sea que sea más eficiente / apropiado de usar.

  • Ignorando el uso de un generador de perfiles de base de datos, que puede decirle exactamente en qué se está convirtiendo finalmente su consulta ORM y, por lo tanto, verificar la lógica o incluso la depuración cuando no se utiliza ORM.


8

No está haciendo el nivel correcto de normalización . Desea asegurarse de que los datos no estén duplicados y que los esté dividiendo en diferentes según sea necesario. También debe asegurarse de no seguir demasiado la normalización, ya que eso perjudicará el rendimiento.


¿Qué tan lejos es demasiado lejos? Si no hay datos duplicados, ¿cómo puede llevarlos más lejos?
finnw

La normalización es un equilibrio entre eliminar datos redundantes y aumentar la flexibilidad frente a un menor rendimiento y una mayor complejidad. Encontrar el equilibrio correcto requiere experiencia y cambia con el tiempo. Consulte en.wikipedia.org/wiki/Database_normalization para obtener información sobre cuándo desnormalizar
Nathan Voxland

8

Tratar la base de datos solo como un mecanismo de almacenamiento (es decir, una biblioteca de colecciones glorificada) y, por lo tanto, subordinada a su aplicación (ignorando otras aplicaciones que comparten los datos)


Un corolario de esto es descargar demasiado trabajo de consulta a la aplicación en lugar de mantenerlo en la base de datos a la que pertenece. LINQ es particularmente malo sobre esto.
3Dave

8
  • Descartar un ORM como Hibernate, por razones como "es demasiado mágico" o "no está en mi base de datos".
  • Confiar demasiado en un ORM como Hibernate e intentar calzarlo donde no sea apropiado.

8

1 - Usar innecesariamente una función en un valor en una cláusula where con el resultado de que ese índice no se utiliza.

Ejemplo:

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

en vez de

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

Y en menor medida: no agregar índices funcionales a los valores que los necesitan ...

2 - No agregar restricciones de verificación para garantizar la validez de los datos. El optimizador de consultas puede usar restricciones, y REALMENTE ayudan a garantizar que pueda confiar en sus invariantes. Simplemente no hay razón para no usarlos.

3 - Agregar columnas no normalizadas a las tablas por pura pereza o presión de tiempo. Las cosas generalmente no están diseñadas de esta manera, sino que evolucionan hacia esto. El resultado final, sin falta, es un montón de trabajo tratando de limpiar el desorden cuando te muerde la integridad de datos perdida en futuras evoluciones.

Piense en esto, una tabla sin datos es muy barata de rediseñar. Una tabla con un par de millones de registros sin integridad ... no es tan barata de rediseñar. Por lo tanto, hacer el diseño correcto al crear la columna o tabla se amortiza en espadas.

4 - no tanto sobre la base de datos per se sino realmente molesto. No me importa la calidad del código de SQL. El hecho de que su SQL se exprese en texto no hace que sea correcto ocultar la lógica en montones de algoritmos de manipulación de cadenas. Es perfectamente posible escribir SQL en texto de una manera que tu compañero programador pueda leer.


7

Esto se ha dicho antes, pero: índices, índices, índices . He visto muchos casos de aplicaciones web empresariales de bajo rendimiento que se corrigieron simplemente haciendo un pequeño perfil (para ver qué tablas se golpearon mucho) y luego agregando un índice en esas tablas. Esto ni siquiera requiere mucho conocimiento de escritura SQL, y la recompensa es enorme.

Evite la duplicación de datos como la peste. Algunas personas defienden que una pequeña duplicación no perjudicará y mejorará el rendimiento. Oye, no digo que tengas que torturar tu esquema en la Tercera Forma Normal, hasta que sea tan abstracto que ni siquiera los DBA sepan lo que está sucediendo. Solo comprenda que siempre que duplique un conjunto de nombres, códigos postales o códigos de envío, las copias eventualmente no se sincronizarán entre sí. Pasará. Y luego te patearás mientras ejecutas el script de mantenimiento semanal.

Y por último: use una convención de nomenclatura clara, coherente e intuitiva. De la misma manera que un código bien escrito debería ser legible, un buen esquema o consulta SQL debería ser legible y prácticamente decirle lo que está haciendo, incluso sin comentarios. Te agradecerás en seis meses, cuando tengas que realizar tareas de mantenimiento en las mesas. "SELECT account_number, billing_date FROM national_accounts"es infinitamente más fácil trabajar con "SELECT ACCNTNBR, BILLDAT FROM NTNLACCTS".


Si los configura correctamente, no lo harán, pero esto implica el uso de desencadenantes a los que muchas personas son alérgicas.
HLGEM

6

¡No ejecuta una consulta SELECT correspondiente antes de ejecutar la consulta DELETE (particularmente en bases de datos de producción)!


5

El error más común que he visto en veinte años: no planificar con anticipación. Muchos desarrolladores crearán una base de datos y tablas, y luego modificarán y expandirán continuamente las tablas a medida que desarrollen las aplicaciones. El resultado final es a menudo un desastre e ineficiente y difícil de limpiar o simplificar más adelante.


1
Me puedo imaginar los horrores que se producen en estas situaciones ... Las bases de datos sin esquema son mucho más adecuadas para la creación rápida de prototipos y el desarrollo iterativo, pero como todo lo demás, esa flexibilidad viene con varias compensaciones.
Zsolt Török

4

a) Codificar valores de consulta en la cadena
b) Poner el código de consulta de la base de datos en la acción "OnButtonPress" en una aplicación de Windows Forms

He visto los dos.


44
"Poner el código de consulta DB en la acción" OnButtonPress "en una aplicación de Windows Form" ¿Cuál es el error de la base de datos aquí?
recursivo

@recursive: es una gran vulnerabilidad de inyección SQL. Cualquiera puede enviar SQL arbitrario a su servidor y se ejecutará textualmente.
Bill Karwin

De acuerdo con @recursive. Estos realmente no tienen nada que ver con problemas de DB.
p.campbell

b) es un error de arquitectura. Por supuesto, codificar consultas directamente en su aplicación es una mala idea, de todos modos.
3Dave

4

No prestar suficiente atención a la gestión de conexiones de bases de datos en su aplicación. Luego descubre que la aplicación, la computadora, el servidor y la red están obstruidos.


4
  1. Pensando que son DBA y modeladores / diseñadores de datos cuando no tienen adoctrinamiento formal de ningún tipo en esas áreas.

  2. Pensar que su proyecto no requiere un DBA porque todo eso es fácil / trivial.

  3. No discernir adecuadamente entre el trabajo que se debe hacer en la base de datos y el trabajo que se debe hacer en la aplicación.

  4. No validar copias de seguridad, o no hacer copias de seguridad.

  5. Incrustar SQL sin formato en su código.



3

No comprender el modelo de concurrencia de bases de datos y cómo esto afecta el desarrollo. Es fácil agregar índices y ajustar consultas después del hecho. Sin embargo, las aplicaciones diseñadas sin la debida consideración de los puntos críticos, la contención de recursos y el funcionamiento correcto (¡suponiendo que lo que acaba de leer sigue siendo válido!) Pueden requerir cambios significativos dentro de la base de datos y el nivel de aplicación para corregirlos más adelante.


3

No entiendo cómo funciona un DBMS bajo el capó.

No se puede conducir correctamente una palanca sin comprender cómo funciona un embrague. Y no puede entender cómo usar una Base de Datos sin comprender que realmente solo está escribiendo en un archivo en su disco duro.

Específicamente:

  1. ¿Sabes qué es un índice agrupado? ¿Lo pensaste cuando diseñaste tu esquema?

  2. ¿Sabes cómo usar los índices correctamente? ¿Cómo reutilizar un índice? ¿Sabes qué es un índice de cobertura?

  3. Muy bien, tienes índices. ¿Qué tan grande es 1 fila en su índice? ¿Qué tan grande será el índice cuando tenga muchos datos? ¿Encajará eso fácilmente en la memoria? Si no es así, es inútil como índice.

  4. ¿Alguna vez has usado EXPLAIN en MySQL? Excelente. Ahora sé honesto contigo mismo: ¿Entendiste incluso la mitad de lo que viste? No, probablemente no lo hiciste. Arregla eso.

  5. ¿Entiendes el caché de consultas? ¿Sabes qué hace que una consulta no se pueda almacenar en caché?

  6. ¿Estás usando MyISAM? Si NECESITA la búsqueda de texto completo, MyISAM es una mierda de todos modos. Usa la esfinge. Luego cambia a Inno.


2
Una mejor analogía podría ser que uno no puede solucionar adecuadamente una transmisión manual sin comprender un embrague. Mucha gente conduce correctamente un cambio de palanca sin saber cómo funciona un embrague.
Michael Easter

3
  1. Usando un ORM para hacer actualizaciones masivas
  2. Seleccionar más datos de los necesarios. Nuevamente, generalmente se hace cuando se usa un ORM
  3. Disparando sqls en un bucle.
  4. No tener buenos datos de prueba y notar una degradación del rendimiento solo en datos en vivo.
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.