¿Qué califica "demasiadas solicitudes de base de datos" en el código?

17

Esta es una discusión que yo y algunos de mis colegas están teniendo y pensé que vendría aquí y vería si hay un consenso general al respecto.

Básicamente se reduce a las siguientes 2 opiniones sobre las llamadas a la base de datos: 1. Haga una llamada grande para obtener todo lo que pueda necesitar para reducir la cantidad de llamadas a la base de datos 2. Haga llamadas separadas más pequeñas en función de lo que se solicita para reducir el tamaño de la base de datos. DB llama

Donde esto entra especialmente en juego es en el código común. Usaremos el ejemplo de una clase de Empleado, ya que es bastante sencillo.

Digamos que su clase de Empleado tiene 10 atributos de valor (nombre, apellido, fecha de contratación, etc.) y luego 2 atributos de clase ... 1 apuntando a una clase de Departamento y luego 1 supervisor que señala de nuevo a otro objeto Empleado.

En la mentalidad n. ° 1, haría una llamada que devuelva los datos del Empleado, así como los campos necesarios para completar los atributos del Departamento y del Supervisor ... o al menos los campos que se usan con más frecuencia de esos subobjetos.

En la mentalidad n. ° 2, solo llenaría el objeto Empleado al principio y luego solo los objetos Departamento y Supervisor si realmente se solicitan.

La postura de 2 es bastante directa ... minimice el tamaño de las solicitudes y cuántos objetos de la base de datos deben golpearse cada vez que se realiza una de esas solicitudes. La postura de # 1 es que incluso si se pudiera implementar correctamente, el simple hecho de que el código tendría que hacer múltiples conexiones va a causar más tensión en la conexión entre el servidor web y la base de datos en lugar de reducirlo.

La fuerza impulsora detrás de investigar esto es que la cantidad de tráfico entre nuestro servidor web y el servidor de base de datos se está descontrolando.

usuario107775
fuente
77
En mi experiencia no hay una "respuesta correcta" a esto. Hay un equilibrio entre latencia y rendimiento. La baja latencia puede tolerar muchas pequeñas solicitudes o incluso una grande; sin embargo, los enlaces de alta latencia tienden a ser mejores moviendo muchos datos a la vez. Sin embargo, si el rendimiento es bajo en una configuración de alta latencia, es mejor buscar fragmentos más pequeños para ser más receptivos.
3
Probablemente relacionado con el problema n + 1 stackoverflow.com/questions/97197/…
Valera Kolupaev
@Valera: para mayor comodidad, aquí está el enlace publicado en esa pregunta: realsolve.co.uk/site/tech/hib-tip-pitfall.php?name=n1selects
rwong
44
"La cantidad de tráfico entre nuestro servidor web y el servidor de bases de datos se está descontrolando". Qué significa eso? ¿Puedes ser específico sobre cuál es el verdadero problema? ¿Tienes problemas de rendimiento? ¿Has hecho perfiles y mediciones? Proporcione los resultados reales de las mediciones reales como parte de la pregunta. De lo contrario, solo estamos adivinando.
S.Lott

Respuestas:

8

Si la fuerza impulsora detrás de esta pregunta es demasiado tráfico, ¿ha investigado el almacenamiento en caché de los objetos de uso frecuente? Por ejemplo: después de obtener los objetos Empleado y Departamento y Supervisor, tal vez sería una buena idea agregarles un caché para que, si se vuelven a solicitar en un futuro próximo, ya estén en el caché y no es necesario recuperarlos. de nuevo. Por supuesto, la memoria caché deberá permitir que los objetos raramente utilizados expiren, y también debe poder eliminar los objetos que la aplicación ha modificado y guardado en la base de datos.

Según el idioma y los marcos que esté utilizando, es posible que ya exista un marco de almacenamiento en caché que pueda hacer algo (o la mayoría) de lo que necesita. Si usa Java, podría buscar en Apache Commons-Cache (no lo he usado por un tiempo, y aunque parece inactivo, todavía está disponible para usar y fue bastante decente la última vez que lo usé).

FrustratedWithFormsDesigner
fuente
3

Siempre busca legibilidad y claridad la primera vez que escribes algo. Luego puede refactorizar si y cuando lo necesite. Realice pruebas de carga para encontrar los cuellos de botella, en muchos casos no es la cantidad de llamadas que causan el problema sino las mal escritas.

En cuanto a lo que clasifica como demasiados, eso depende de la aplicación. Para la mayoría de las aplicaciones web, cualquier cosa menor a 30 segundos es casi aceptable. Hablaría con sus usuarios sobre sus expectativas.

Tom Squires
fuente
¿Qué constituye una llamada db mal escrita?
nu everest
3

Su pregunta parece basarse en el supuesto de que tiene que adivinar qué datos se necesitarán para una página determinada. Ese no es el caso. No es tan fácil como el enfoque ingenuo, pero puede diseñar su código para saber si va a necesitar atributos del departamento o del supervisor antes de realizar llamadas a la base de datos.

Karl Bielefeldt
fuente
3

Estas son las reglas que uso, tal vez te sean útiles.

  1. ¡Mide primero! Ni siquiera miraré el código que "podría ser lento" a menos que realmente pueda ver el tráfico que fluye hacia ese recurso y ese recurso está respondiendo lentamente.
  2. 1 Solicitud = K consultas. El número de veces que hablo con la base de datos está completamente determinado por el tipo de recurso solicitado; y nunca por la naturaleza de la solicitud o el estado de ese recurso; En su ejemplo, es probable que haya un máximo de 3 consultas: 1 para empleados, 1 para departamentos y 1 para supervisores; No importa cuántos de cada uno haya.
  3. No preguntes lo que no usarás . Si estamos hablando de HTTP, no tiene sentido consultar datos para más adelante; no hay más tarde; cada solicitud comienza desde una pizarra limpia. A veces necesito la mayoría de las columnas de una tabla, pero en ocasiones solo necesito una o dos; cuando sepa exactamente los campos que necesito, solo pediré eso.
  4. Lanza hardware al problema. Los servidores son baratos; A veces puede obtener suficiente rendimiento simplemente moviendo la base de datos a una caja más robusta; o enviando algunas consultas a una réplica de solo lectura.
  5. Primero invalide el caché, luego implemente el almacenamiento en caché. La necesidad de colocar datos de uso frecuente o difíciles de consultar en un caché es fuerte; pero con demasiada frecuencia, se pasa por alto el desalojo de datos no utilizados o el vencimiento de datos reemplazados. Si sabe cómo sacar datos del caché; entonces estás seguro poniéndolo en el caché; Si resulta que es más costoso invalidar la memoria caché que simplemente hacer la consulta; entonces no necesitabas un caché.
SingleNegationElimination
fuente
2

Ambas estrategias aquí son perfectamente válidas. Hay ventajas y desventajas para cada uno:

Una llamada para los 3 objetos:

  • funcionará más rápido
  • le dará exactamente lo que necesita en el caso en que lo necesite
  • probablemente solo sea utilizable en un caso (aunque puede ser un caso muy común)
  • será más difícil de mantener
  • tendrá que mantenerse con más frecuencia (ya que cambiará si alguno de los esquemas de los 3 objetos o los datos necesarios cambian)

Una llamada por objeto (3 llamadas en total)

  • Le ofrece una llamada de propósito general para llenar una única instancia de cada tipo de objeto; entonces pueden usarse independientemente
  • Será más fácil de mantener ya que la estructura de consulta será más simple.
  • Será más lento (no necesariamente 3 veces más lento, pero la sobrecarga aumenta para los mismos datos)
  • Puede causar problemas con la recuperación de datos innecesarios (extraer un registro completo cuando necesita un campo es un desperdicio)
  • Puede causar problemas de N + 1 cuando existe una relación de muchos a uno, si la consulta de registro único se envía N veces, una por registro en la colección.
KeithS
fuente
En respuesta a un par de sus inquietudes (# 3 y 5 en la segunda lista) ... ¿Qué pasa si el Supervisor y el Departamento solo se usan 1/3 (o menos) del tiempo? ¿Qué sucede si el código fue diseñado para obtener todos los elementos secundarios tan pronto como se hizo referencia por primera vez al objeto List <> codificado para contenerlos? ... ¿eso aliviaría la mayor parte de la cautela?
user107775
Si los objetos auxiliares son raramente necesarios, en el caso general esto funcionará más rápido (menos datos para recuperar) pero el peor de los casos será más lento (los mismos datos o más recuperados, usando tres veces la sobrecarga de comunicación de su computadora). En cuanto al problema de N + 1, simplemente necesita poder diseñar la consulta que recupera una lista de objetos para poder hacerlo en función de la clave externa del lado "uno" de la relación, y luego extraer varias filas fuera del resultado de la consulta. No puede usar una versión de la consulta que tenga que tener la clave primaria del registro.
KeithS
1

Para mí, demasiadas solicitudes de base de datos están haciendo más solicitudes de las que necesita para cargar los datos que necesita en un momento dado.

Por lo tanto, si no necesita los datos, no pierda la memoria para evitar un segundo viaje más tarde. Pero si necesita la cantidad de datos, debe minimizar las llamadas a la base de datos.

Entonces, tenga ambas opciones y use cada una donde la situación lo requiera.

EDITAR: tenga en cuenta que este curso también depende de su situación. Si es una aplicación web, por ejemplo, debe tener diferentes consideraciones que si se trata de una aplicación de escritorio que accede a la base de datos dentro de su red, a diferencia de la aplicación web WepApp.

AJC
fuente
¿Qué pasa si está escribiendo un código común y no está seguro de la forma en que se usará su código? Tal vez nunca imaginarías a alguien que no necesita el Supervisor, pero resulta que la aplicación en la que trabajas es la única que la necesita. Claro, podría escribir funciones separadas ... una para no incluirla y otra para incluirla, pero ¿en qué punto su código común comienza a requerir un conocimiento demasiado detallado para poder usarlo?
usuario107775
@ user107775 Normalmente escribo solo dos funciones para cada caso; uno que devuelve solo los valores de propiedad y otro que devuelve la clase con todas las clases relacionadas. Esto se debe a que la MAYORÍA de las veces, solo necesita las propiedades. De esta manera, no necesita conocimientos detallados, solo uno obtiene los conceptos básicos y el otro todo. Me parece un equilibrio razonable. (Sin embargo, algunos casos específicos requieren una mayor optimización, pero eso es caso por caso).
AJC
1

Conéctese a la base de datos, envíe la solicitud y haga que se analice, por lo general, lleva mucho tiempo en comparación con la recuperación de resultados, por lo que la tendencia general es concatenar tantas consultas como sea posible en una sola solicitud.

Aún así, hacer todo esto de una vez hará que el código no se pueda mantener. En cambio, generalmente se logra mediante una capa de abstracción adicional: el código programa varias solicitudes según sea necesario, luego el motor analiza esto como una gran solicitud (posiblemente usando caché en el camino) y luego se envían las respuestas según sea necesario.

Por supuesto, no siempre se pueden recuperar todos en una consulta: a menudo tendrá una consulta que proporciona los datos necesarios para crear la siguiente consulta, por lo que tendrá que repetirla. Aún asombrosos paquetes de consultas y realizar la mayor cantidad posible a la vez es mejor que cientos de pequeños disparos a la base de datos.

Por lo tanto, planifique lo que necesita, solicítelo y recupérelo, si necesita más, solicítelo y recupérelo nuevamente, y luego utilice los datos para generar contenido. Definitivamente evite el uso de solicitudes de bases de datos como la inicialización de variables locales dispersas por todo el código.

SF.
fuente
1

No sabemos lo suficiente sobre su aplicación para saber qué opción es culpable de optimizar demasiado pronto. ¿Con qué frecuencia se utilizan los datos del Supervisor? Parece que podría ser un desperdicio, pero no lo sabemos. Si los mantiene separados, es posible que pueda monitorear su sistema para ver con qué frecuencia terminan siendo utilizados juntos. Entonces puede tomar la decisión de combinarlos en una sola llamada. De lo contrario, si comienza a crear un cuello de botella con esta gran llamada, ¿dónde comienza a resolver problemas? Difícil de identificar lo que tiene sentido omitir. Se pueden agregar más campos de datos a este proceso.

Sería interesante saber cuánto de esto proviene de la memoria db frente al disco. No hay nada que me haga sentir que es más o menos probable que el departamento cambie en comparación con la dirección.

JeffO
fuente