La optimización de consultas para el elemento siguiente y anterior

votos
28

Busco la mejor manera de recuperar los registros anterior y siguiente de un registro sin ejecutar una consulta completa. Tengo una solución totalmente implementado en su lugar, y me gustaría saber si hay algún mejores enfoques para hacer esto por ahí.

Digamos que estamos construyendo un sitio web para una frutería ficticia. Además de sus páginas HTML, todas las semanas, quiere publicar una lista de ofertas especiales en su sitio. Él quiere que esas ofertas a residir en una tabla de base de datos real, y los usuarios tienen que ser capaces de ordenar las ofertas de tres maneras.

Cada elemento también tiene que tener una página con más información detallada, textual sobre la oferta y los botones anterior y siguiente. El anterior y los botones siguiente tienen que apuntar a las entradas vecinos en función de la clasificación del usuario hubiera elegido para la lista .

alt text http://www.pekkagaiser.com/stuff/Sort.gif?

Obviamente, el botón siguiente para Tomates, Clase I tiene que ser Manzanas, clase 1 en el primer ejemplo, Peras, clase I en la segunda, y ninguno en el tercero.

La tarea en la vista de detalle es para determinar los artículos siguientes y anteriores sin ejecutar una consulta cada vez que , con el orden de la lista como la única información disponible (Digamos que tenemos que a través de un parámetro GET ?sort=offeroftheweek_price, e ignorar las implicaciones de seguridad) .

Obviamente, sólo tiene que pasar los ID de los elementos siguientes y anteriores como parámetro es la primera solución que viene a la mente. Después de todo, ya sabemos la década de identificación en este punto. Sin embargo, esto no es una opción aquí - que funcionaría en este ejemplo simplificado, pero no en muchas de mis casos de uso del mundo real.

Mi enfoque actual en mi CMS está utilizando algo que he llamado clasificación de caché. Cuando se carga una lista, almaceno las posiciones de los elementos en los registros de una tabla llamada sortingcache.

name (VARCHAR)             items (TEXT)

offeroftheweek_unsorted    Lettuce; Tomatoes; Apples I; Apples II; Pears
offeroftheweek_price       Tomatoes;Pears;Apples I; Apples II; Lettuce
offeroftheweek_class_asc   Apples II;Lettuce;Apples;Pears;Tomatoes

Obviamente, la itemscolumna está muy poblada con identificadores numéricos.

En la página de detalles, ahora acceder al adecuado sortingcacheregistro, ir a buscar la itemscolumna, explotarlo, buscar el ID de elemento actual, y devolver el vecino anterior y siguiente.

array(current   => Tomatoes,
      next      => Pears,
      previous  => null
      );

Esto es obviamente caro, funciona para un número limitado de sólo registra y crea los datos redundantes, pero vamos a suponer que en el mundo real, la consulta para crear las listas es muy caro (es), corriendo en todas las vistas de detalle está fuera de la pregunta, y algunos se necesita almacenamiento en caché.

Mis preguntas:

  • ¿Usted piensa que esto es una buena práctica para averiguar los registros de vecinos para variar las órdenes de consulta?

  • ¿Conoce mejores prácticas en términos de rendimiento y simplicidad? ¿Sabe usted algo que hace que esta completamente obsoleto?

  • En teoría de la programación, hay un nombre para este problema?

  • Es el nombre de caché Clasificación es apropiada y comprensible para esta técnica?

  • ¿Hay alguna reconocidos, patrones comunes para resolver este problema? ¿Cómo se llaman?

Nota: Mi pregunta no es sobre la construcción de la lista, o cómo mostrar la vista de detalle. Estos son sólo ejemplos. Mi pregunta es la funcionalidad básica de la determinación de los vecinos de un registro cuando un re-consulta es imposible, y la manera más rápida y barata para llegar allí.

Si algo no está claro, por favor deje un comentario y voy a aclarar.

A partir de una recompensa - tal vez hay algo más de información sobre este por ahí.

Publicado el 22/02/2010 a las 12:06
fuente por usuario
En otros idiomas...                            


11 respuestas

votos
-3

Por lo que tiene dos tareas:

  1. construir lista ordenada de elementos (selecciona con diferentes ORDER BY)
  2. mostrar información sobre cada elemento (SELECT detalles de la base de datos con posibilidad de almacenamiento en caché).

¿Cuál es el problema?

PD: si la lista ordenada puede ser demasiado grande sólo tiene funcionalidad PAGINADOR implementado. Podría haber diferentes implementaciones, por ejemplo, es posible que desee agregar "LIMIT 5" en consulta y proporcionar "Mostrar el próximo 5" botón. Cuando se pulsa este botón, "donde el precio <0,89 LÍMITE 5" se añade condiciones similares.

Respondida el 22/02/2010 a las 15:04
fuente por usuario

votos
16

He aquí una idea. Se podría descargar las costosas operaciones a una actualización cuando los insertos tendero / actualizaciones nuevas ofertas en lugar de cuando el usuario final selecciona los datos para ver. Esto puede parecer una manera no dinámico para manejar los datos de clasificación, pero puede aumentar la velocidad. Y, como sabemos, no es siempre una solución de compromiso entre rendimiento y otros factores de codificación.

Crear una tabla para guardar siguiente y anterior para cada oferta y cada opción de clasificación. (Como alternativa, puede almacenar esta en la tabla de oferta si usted siempre tendrá tres opciones de clasificación - la velocidad de consulta es una buena razón para eliminar la normalización de base de datos)

Por lo que tendría estas columnas:

  • Tipo de orden (Sin clasificar, precio, clase y Precio Desc)
  • ID de la oferta
  • Anterior ID
  • Siguiente ID

Cuando la información de detalle de la página de detalles de la oferta se realiza una consulta de la base de datos, la NextID y PrevID serían parte de los resultados. Por lo que sólo se necesita una consulta para cada página de detalles.

Cada vez que se inserta una oferta, actualiza o se elimina, se tendría que ejecutar un proceso que valida la integridad / exactitud de la tabla SortType.

Respondida el 22/02/2010 a las 20:20
fuente por usuario

votos
1

No estoy seguro de si he entendido bien, así que si no es así, simplemente me digo;)

Digamos, que los dados son la consulta de la lista ordenada y el desplazamiento en la lista actual, es decir, tenemos una $queryy una $n.

Una solución obvia para reducir al mínimo las consultas, sería la de obtener todos los datos a la vez:

list($prev, $current, $next) = DB::q($query . ' LIMIT ?i, 3', $n - 1)->fetchAll(PDO::FETCH_NUM);

Esa declaración obtiene el anterior, el actual y los siguientes elementos de la base de datos en el orden de clasificación actual y pone la información asociada a las variables correspondientes.

Pero ya que esta solución es demasiado simple, supongo que no he entendido algo.

Respondida el 07/02/2011 a las 20:31
fuente por usuario

votos
2

He tenido pesadillas con éste a su vez. Su enfoque actual parece ser la mejor solución, incluso para las listas de artículos 10k. El almacenamiento en caché los identificadores de la vista de lista en la sesión HTTP y luego usar ese para mostrar la (personalizado al usuario actual) anterior / siguiente. Esto funciona bien sobre todo cuando hay demasiadas formas de filtrar y ordenar la lista inicial de elementos en lugar de sólo 3.
Además, mediante el almacenamiento de toda la lista de identificadores se llega a mostrar un "you are at X out of Y"texto usabilidad mejora.
de JIRA anterior / siguiente

Por cierto, esto es lo que JIRA también lo hace.

Para responder directamente a sus preguntas:

  • Sí, es una buena práctica, ya que las escalas sin ninguna complejidad del código añadido cuando su filtro / clasificación y tipos de elementos cantan más complejo. Lo estoy usando en un sistema de producción con 250 mil artículos con variaciones filtro / ordenar "infinitos". Recortar los ID cacheables a 1000 es también una posibilidad ya que el usuario es muy probable que nunca haga clic en Anterior o siguiente más de 500 veces (Él va a más probable es volver atrás y refinar la búsqueda o paginar).
  • No sé de una mejor manera. Pero si las clases donde limitado y esto era un sitio público (sin sesión HTTP) entonces tendría muy probablemente Desnormalizar.
  • No sé.
  • Sí, la clasificación caché suena bien. En mi proyecto lo llamo "anterior / siguiente en los resultados de búsqueda" o "la navegación en los resultados de búsqueda".
  • No sé.
Respondida el 07/02/2011 a las 21:04
fuente por usuario

votos
2

En general, me Desnormalizar los datos de los índices. Pueden ser almacenados en las mismas filas, pero casi siempre se recuperan mis identificaciones de resultados, a continuación, hacer un viaje por separado para los datos. Esto hace que el almacenamiento en caché los datos muy simple. No es tan importante en PHP, donde la latencia es baja y el gran ancho de banda, pero esta estrategia es muy útil cuando se tiene una alta latencia, la aplicación de bajo ancho de banda, tales como una página web AJAX donde gran parte del sitio se representa en JavaScript.

Siempre almacenar en caché los listados de resultados, y los mismos resultados por separado. Si hay algo que afecta a los resultados de una consulta de lista, el caché de los resultados de la lista se actualiza. Si hay algo que afecta a los propios resultados, los resultados particulares que se actualicen. Esto me permite actualizar cualquiera de ellos sin tener que regenerar todo, lo que resulta en el almacenamiento en caché eficaz.

Desde mis listas de resultados rara vez cambian, genero todas las listas al mismo tiempo. Esto puede hacer que la respuesta inicial ligeramente más lento, pero simplifica la renovación de antememoria (todas las listas se almacenan en una sola entrada de caché).

Porque tengo toda la caché lista, es trivial para encontrar los artículos vecinos sin revisar la base de datos. Con suerte, también se almacenan en caché los datos de esos artículos. Esto es especialmente útil cuando la ordenación de datos en JavaScript. Si ya tengo una copia en caché en el cliente, puedo recurrir al instante.

Para responder a sus preguntas en concreto:

  • Sí, es una idea fantástica para averiguar los vecinos antes de tiempo, o la información que el cliente es probable que acceder a la siguiente, especialmente si el costo es bajo y el costo ahora para volver a calcular es alta. Entonces es simplemente una solución de compromiso de pre-cálculo y de almacenamiento frente a la velocidad extra.
  • En términos de rendimiento y simplicidad, evitar las cosas Enlazando que son lógicamente diferentes cosas. Índices y datos son diferentes, es probable que ser cambiado en diferentes momentos (por ejemplo, la adición de un nuevo punto de referencia afectará a los índices, pero no los datos existentes), y por lo tanto deben ser accedidos por separado. Esto puede ser un poco menos eficiente desde el punto de vista de un solo subproceso, pero cada vez se ata algo juntos, se pierde el almacenamiento en caché eficacia y asychronosity (la clave de la escala es asychronosity).
  • El plazo para la obtención de datos antes de tiempo es la búsqueda previa. La búsqueda previa puede ocurrir en el momento de acceso o en el fondo, pero antes de que realmente se necesitan los datos precargados. Lo mismo sucede con pre-cálculo. Es una compensación de costo ahora, los costes de almacenamiento, y el costo de obtener cuando sea necesario.
  • "Clasificación de caché" es un nombre adecuado.
  • No lo sé.

Además, cuando se hace una caché cosas, ellos almacenar en caché en el nivel más genérico posible. Algunas cosas podrían ser específicos de usuario (como los resultados de una consulta de búsqueda), en los que otros podrían ser agnóstico de usuario, tales como navegar por un catálogo. Ambos pueden beneficiarse del almacenamiento en caché. La consulta de catálogo puede ser frecuente y ahorrar un poco cada vez, y la consulta de búsqueda puede ser costoso y ahorrar mucho un par de veces.

Respondida el 09/02/2011 a las 08:00
fuente por usuario

votos
0

Hay tantas maneras de hacer esto en cuanto a la piel del gato proverbial. Así que aquí hay un par de la mía.

Si su búsqueda original es caro, lo que usted dice que es, a continuación, crear otra mesa, posiblemente, una tabla de memoria poblarlo con los resultados de su cara y rara vez ejecutar la consulta principal.

Esta segunda tabla a continuación, podría ser consultada en todas las vistas y la clasificación es tan simple como establecer el orden de clasificación apropiado.

Como se requiere repoblar la segunda tabla con los resultados de la primera tabla, manteniendo así los datos fresco, pero minimizando el uso de la consulta caro.

Alternativamente, si se quiere evitar incluso la conexión a la base de datos entonces se podría almacenar todos los datos en una matriz de php y almacenarlo utilizando memcached. esto sería muy rápido y proporcionado sus listas no eran demasiado grandes sería eficiente de los recursos. y puede ser resuelto fácilmente.

corriente continua

Respondida el 11/02/2011 a las 05:19
fuente por usuario

votos
0

Supuestos básicos:

  • Especiales son semanales
  • Podemos esperar que el sitio para cambiar con frecuencia ... probablemente todos los días?
  • Podemos controlar las actualizaciones de la base de datos con una API éter o responder a través de disparadores

Si el sitio cambia a diario, le sugiero que todas las páginas se generan de forma estática durante la noche. Una consulta para cada iteración tipo de orden y hace a través de todas las páginas relacionadas. Incluso si hay elementos dinámicos, las probabilidades son que usted puede dirigirse a ellos mediante la inclusión de los elementos de la página estática. Esto proporcionaría servicio de la página óptima y sin carga de base de datos. De hecho, usted podría generar páginas separadas y los siguientes elementos prev / que se incluyen en las páginas. Esto puede ser más loco con 200 maneras de clasificar, pero con 3 Soy un gran fan de ella.

?sort=price
include(/sorts/$sort/tomatoes_class_1)
/*tomatoes_class_1 is probably a numeric id; sanitize your sort key... use numerics?*/

Si por alguna razón esto no es posible, yo recurro a la memorización. Memcache es popular para este tipo de cosas (juego de palabras!). Cuando algo es empujado a la base de datos, puede emitir un disparador para actualizar su caché con los valores correctos. Para ello, en la misma forma que lo haría si como si no existiera el elemento actualizado en 3 listas enlazadas - vincular en su caso (this.next.prev = this.prev, etc). A partir de ese, siempre y cuando su caché no llene en exceso, podrás tirar de valores simples de la memoria de una manera clave primaria.

Este método va a tomar algo de código adicional en los métodos de selección y actualización / inserción, pero debe ser bastante mínima. Al final, se le busca a [id of tomatoes class 1].price.next. Si esa clave está en la memoria caché, de oro. Si no, insertar en la memoria caché y la pantalla.

  • ¿Usted piensa que esto es una buena práctica para averiguar los registros de vecinos para variar las órdenes de consulta? Sí. Es una buena idea para llevar a cabo look-aheads sobre las solicitudes esperadas.
  • ¿Conoce mejores prácticas en términos de rendimiento y simplicidad? ¿Sabe usted algo que hace que esta completamente obsoleto? Con suerte lo anterior
  • En teoría de la programación, hay un nombre para este problema? ¿Mejoramiento?
  • Es el nombre de "caché Clasificación" es apropiada y comprensible para esta técnica? No estoy seguro de un nombre apropiado específico. Es el almacenamiento en caché, es una memoria caché de clase, pero no estoy seguro de que me dice que tiene un "caché de clasificación" transmitiría la comprensión instantánea.
  • ¿Hay alguna reconocidos, patrones comunes para resolver este problema? ¿Cómo se llaman? El almacenamiento en caché?

En este momento mis tizón respuestas son algo inútil, pero creo que mis soluciones narrativas deben ser bastante útil.

Respondida el 11/02/2011 a las 18:13
fuente por usuario

votos
0

Se podría guardar los números de fila de las listas ordenadas en puntos de vista , y que podría llegar a los puntos anteriores y siguientes en la lista bajo los números de fila (current_rownum + 1) (current_rownum-1) y.

Respondida el 12/02/2011 a las 14:01
fuente por usuario

votos
0

El problema / datastructur se llama gráfica bidireccional o se podría decir que tienes varias listas enlazadas.

Si usted piensa en él como una lista enlazada, sólo podría añadir campos a la tabla de artículos para cada clasificación y prev siguiente, pulse /. Pero la persona que va a matar DB para eso, es como GOTO.

Si usted piensa en él como (bi) gráfica direccional, vas con la respuesta de Jessica. El principal problema no es que las actualizaciones orden son operaciones costosas.

 Item Next Prev
   A   B     -
   B   C     A
   C   D     B
   ...

Si cambia uno posicionar los temas que la nueva orden A, C, B, D, tendrá que actualizar 4 filas.

Respondida el 13/02/2011 a las 02:20
fuente por usuario

votos
4

Tengo una idea un tanto similar a la de Jessica. Sin embargo, en lugar de almacenar enlaces a los artículos siguientes y anteriores ordenar, almacenar el orden de clasificación para cada tipo de clasificación. Para encontrar el registro anterior o siguiente, acaba de obtener la fila con SortX = currentSort ++ o SortX = currentSort--.

Ejemplo:

Type     Class Price Sort1  Sort2 Sort3
Lettuce  2     0.89  0      4     0
Tomatoes 1     1.50  1      0     4
Apples   1     1.10  2      2     2
Apples   2     0.95  3      3     1
Pears    1     1.25  4      1     3

Esta solución produciría tiempos de consulta muy cortos, y que ocupen menos espacio en disco que la idea de Jessica. Sin embargo, como estoy seguro de que te das cuenta, el costo de la actualización de una fila de datos es notablemente más alta, ya que hay que volver a calcular y almacenar todos los criterios de ordenación. Pero aún así, dependiendo de su situación, si las actualizaciones de datos son poco frecuentes y, especialmente, si siempre ocurren en grandes cantidades, a continuación, esta solución podría ser la mejor.

es decir

once_per_day
  add/delete/update all records
  recalculate sort orders

Espero que esto es útil.

Respondida el 13/02/2011 a las 03:30
fuente por usuario

votos
0

Disculpas si me han entendido mal, pero creo que se desea conservar la lista ordenada entre el usuario accede al servidor. Si es así, su respuesta puede muy bien estar en su estrategia y tecnologías de almacenamiento en caché en lugar de la base de datos de optimización de consulta / esquema.

Mi enfoque sería para serializar () la matriz una vez que su primer recuperada, y luego almacenar en caché que en una zona de almacenamiento por separado; Si eso es MemCached / APC / unidad de disco duro / mongodb / etc y retener a sus detalles de la ubicación de caché para cada usuario de forma individual a través de sus datos de sesión. El backend de almacenamiento real sería naturalmente depende del tamaño de la matriz, que no entrar en mucho detalle acerca, pero las escalas memcached gran a través de múltiples servidores y mongo aún más a un costo ligeramente mayor latencia.

También no indica cuántas permutaciones tipo existen en el mundo real; por ejemplo, hacer lo necesario para almacenar en caché listas separadas por usuario, o puede que a nivel mundial caché por permutación de clase y luego filtrar lo que no es necesario a través de PHP ?. En el ejemplo que das, me gustaría simplemente cacheamos ambas permutaciones y tienda de cuál de los dos que necesitaba para serializar () en los datos de la sesión.

Cuando el usuario vuelve al sitio, comprobar el tiempo de vida del valor de los datos en caché y volver a utilizarlo si sigue siendo válido. También tendría un disparador que se ejecuta en INSERT IGNORE / actualizar / eliminar de las ofertas especiales que simplemente establece un campo de marca de tiempo en una tabla separada. Esto indicaría inmediatamente si la caché estaba duro y la consulta necesita ser re-carrera por un costo muy bajo consulta. Lo bueno de usar sólo el gatillo para establecer un campo único es que no hay necesidad de preocuparse por la poda de los viejos valores / redundantes de esa mesa.

Si este es adecuado dependerá del tamaño de los datos que se devuelve, la frecuencia con la que se modificó, y qué tecnologías de almacenamiento en caché están disponibles en el servidor.

Respondida el 13/02/2011 a las 15:47
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more