Cómo buscar rápidamente a través de una lista muy grande de cadenas / registros en una base de datos

32

Tengo el siguiente problema: tengo una base de datos que contiene más de 2 millones de registros. Cada registro tiene un campo de cadena X y quiero mostrar una lista de registros para los cuales el campo X contiene una cadena determinada. Cada registro tiene un tamaño de aproximadamente 500 bytes.

Para hacerlo más concreto: en la GUI de mi aplicación tengo un campo de texto donde puedo ingresar una cadena. Sobre el campo de texto tengo una tabla que muestra los (primeros N, por ejemplo, 100) registros que coinciden con la cadena en el campo de texto. Cuando escribo o elimino un carácter en el campo de texto, el contenido de la tabla debe actualizarse sobre la marcha.

Me pregunto si hay una manera eficiente de hacer esto usando estructuras de índice apropiadas y / o almacenamiento en caché. Como se explicó anteriormente, solo quiero mostrar los primeros N elementos que coinciden con la consulta. Por lo tanto, para N lo suficientemente pequeño, no debería ser un gran problema cargar los elementos coincidentes de la base de datos. Además, el almacenamiento en caché de elementos en la memoria principal puede acelerar la recuperación.

Creo que el problema principal es cómo encontrar los elementos coincidentes rápidamente, dada la cadena de patrón. ¿Puedo confiar en algunas instalaciones de DBMS o tengo que construir un índice en memoria yo mismo? ¿Algunas ideas?

EDITAR

He realizado un primer experimento. He dividido los registros en diferentes archivos de texto (como máximo 200 registros por archivo) y coloqué los archivos en diferentes directorios (utilicé el contenido de un campo de datos para determinar el árbol de directorios). Termino con aproximadamente 50000 archivos en aproximadamente 40000 directorios. Luego ejecuté Lucene para indexar los archivos. Buscar una cadena con el programa de demostración Lucene es bastante rápido. Dividir e indexar tomó unos minutos: esto es totalmente aceptable para mí porque es un conjunto de datos estáticos que quiero consultar.

El siguiente paso es integrar Lucene en el programa principal y usar los hits devueltos por Lucene para cargar los registros relevantes en la memoria principal.

Giorgio
fuente
2
2 millones de registros * 500 bytes = 1 GB de datos. Es una gran cantidad de datos para buscar, sea cual sea la forma en que lo haga: ¿es probable que cada valor de X sea único o tendrá muchos registros con el mismo valor de X?
1
Esa también sería una gran cantidad de datos para intentar almacenar en la memoria como caché para una recuperación rápida. Eso equivaldría a más de 1 GB por sesión de usuario.
maple_shaft
Mi comentario anterior supone una aplicación web. ¿Es esta una aplicación web?
maple_shaft
Es una aplicación de escritorio. Los valores en los registros no son necesariamente únicos. Además, estoy buscando una subcadena que no sea una coincidencia exacta.
Giorgio el
@maple_shaft: solo almacenaría en caché los registros a los que he accedido recientemente. Si cambio la cadena de consulta y un registro aún coincide, todavía está en el caché.
Giorgio

Respuestas:

20

En lugar de poner sus datos dentro de la base de datos, puede guardarlos como un conjunto de documentos (archivos de texto) por separado y mantener el enlace (ruta / url, etc.) en la base de datos.

Esto es esencial porque la consulta SQL por diseño será muy lenta tanto en la búsqueda de subcadenas como en la recuperación.

Ahora, su problema se formula como tener que buscar los archivos de texto que contienen el conjunto de cadenas. Hay dos posibilidades aquí.

  1. Coincidencia de subcadenas Si sus blobs de texto son una sola picadura o palabra (sin espacios en blanco) y necesita buscar subcadenas arbitrarias dentro de ella. En tales casos, debe analizar cada archivo para encontrar los mejores archivos posibles que coincidan. Uno usa algoritmos como el algoritmo Boyer Moor. Vea esto y esto para más detalles. Esto también es equivalente a grep, porque grep usa cosas similares en su interior. Pero aún puede hacer al menos más de 100 grep (el peor de los casos, 2 millones) antes de regresar.

  2. Búsqueda indexada Aquí está asumiendo que el texto contiene un conjunto de palabras y la búsqueda está limitada a longitudes de palabras fijas. En este caso, el documento se indexa sobre todas las posibles apariciones de palabras. Esto a menudo se llama "búsqueda de texto completo". Hay varios algoritmos para hacer esto y varios proyectos de código abierto que se pueden usar directamente. Muchos de ellos también admiten búsquedas con comodines, búsquedas aproximadas, etc., como se muestra a continuación:
    a. Apache Lucene: http://lucene.apache.org/java/docs/index.html
    b. OpenFTS: http://openfts.sourceforge.net/
    c. Sphinx http://sphinxsearch.com/

Lo más probable es que si necesita "palabras fijas" como consultas, el enfoque dos será muy rápido y efectivo.

Dipan Mehta
fuente
2
Este es un concepto interesante, pero parece poco probable que un desarrollador pueda buscar fácilmente a través de 1 GB de datos textuales de manera más rápida y eficiente que un motor de base de datos. Mucha gente más inteligente que tú y yo hemos trabajado en los optimizadores de consultas para hacer precisamente eso y es un poco ingenuo pensar que de alguna manera puedes hacerlo de manera más eficiente.
maple_shaft
44
@maple_shaft Los ejemplos que he dado no son motores de base de datos RDBMS. Son más como "motores de búsqueda" si desea llamarlo. Hay una gran diferencia conceptual entre elegir una lista de un índice (o tabla hash) versus buscar a través de 1 GB de datos una y otra vez cada vez que se inicia una consulta. Entonces, lo que sugiero no es una modificación menor.
Dipan Mehta
Esta parece una idea interesante, pero me pregunto cómo funcionaría. Tendría más de 2 000 000 de archivos, cada uno de aproximadamente medio kilobyte de tamaño. ¿O sugieres tener más de un registro por archivo? ¿Cuál sería la diferencia con una base de datos?
Giorgio
No estoy convencido de que esto necesariamente funcione mejor que, digamos, el índice de texto completo de SQL.
Kirk Broadhurst
@Giorgio: sí, así es como funcionarían los motores de búsqueda de texto completo. La diferencia clave aquí es una página pre-indexada frente a la búsqueda en memoria (nuevamente cada vez que aparece una consulta).
Dipan Mehta
21

La tecnología que está buscando es la indexación de texto completo. La mayoría de los RDBMS tienen algún tipo de capacidades integradas que podrían funcionar aquí, o podría usar algo como Lucene si quisiera ser más elegante y / o simplemente ejecutarlo en la memoria.

Wyatt Barnett
fuente
1
En mi opinión, las opciones de texto completo en cualquier RDBMS es una solución alternativa para hacer que haga algo para lo que no está diseñado: "buscar en una pila de datos no estructurados no relacionados". Si está creando un motor de búsqueda, simplemente no usa un RDBMS. Puede funcionar para pequeños conjuntos de datos, pero lakcs cualquier tipo de escala. Buscar entre montones de datos no estructurados no es un clavo, así que no use un martillo. Use la herramienta adecuada para el trabajo.
Pieter B
8

¿Has considerado un trie ? Básicamente, usted construye un árbol utilizando prefijos comunes, por lo que todas las palabras que comienzan con las mismas letras son elementos secundarios del mismo nodo. Si vas a admitir la coincidencia en cualquier subcadena, entonces tendrás que generar algún tipo de índice permutado y construir tu trie a partir de eso. Sin embargo, eso puede terminar eliminando sus requisitos de almacenamiento.

TMN
fuente
1
¡SÍ! Estaba pensando en la estructura de un árbol y recordé que había algo similar que podría ser adecuado para mí, pero no recordaba los de Trie porque nunca los había usado. Con respecto al requisito de almacenamiento: recuerde que necesito recuperar solo las primeras N entradas (por ejemplo, N = 100) porque no tiene sentido llenar una tabla con 20000 visitas. Por lo tanto, cada nodo del trie apuntaría como máximo a N entradas. Además, olvidé mencionar que necesito un acceso rápido pero no necesito una actualización rápida, porque los datos solo se cargan una vez. ¡La idea de un índice permutado realmente podría funcionar!
Giorgio
1
Buena respuesta, pero como se nota, un trie es grande para que coincida con el inicio de sus palabras, pero obtendrá con rapidez compleja y muy grande si se emparejan con cualquier subcadena ...
Kirk Broadhurst
Como primer experimento, he intentado construir el conjunto de todas las subcadenas que aparecen en las cadenas que tengo que buscar que, si entiendo correctamente, corresponden a los caminos del trie. Obtuve una excepción de falta de memoria (con 256 M de almacenamiento dinámico para la JVM) en subcadenas de longitud 6. Por lo tanto, me temo que esta solución no es factible, a menos que esté haciendo algo mal.
Giorgio el
5

Me gustaría agregar, además de la respuesta de Wyatt Barnett, que una solución RDBMS con indexación de texto completo en la columna adecuada funcionará, pero si desea utilizar un caché local de registros recuperados previamente, entonces necesita un plan para utilizar estos registros en caché a su ventaja.

Una opción es recopilar los identificadores únicos de estos registros que EXPLÍCITAMENTE no desea recuperar de la consulta e incluirlos, posiblemente en a NOT INo a NOT EXISTS.

Sin embargo, una advertencia: usar NOT INo NOT EXISTStiende a no ser barato y PUEDE influir negativamente en el rendimiento de su consulta o en el plan de consulta, según el motor de base de datos que esté utilizando. Ejecute un plan de explicación en su consulta final para asegurarse de que se estén utilizando todos sus índices en las columnas afectadas.

Tampoco hace daño hacer una comparación de rendimiento entre los dos enfoques para ver cuál es más rápido. Es posible que se sorprenda al descubrir que mantener un caché local y filtrarlos de su consulta explícitamente puede tener un rendimiento peor que una consulta finamente ajustada que recupera todos los registros.

maple_shaft
fuente
maple_shaft y @Wyatt Barnett: Muchas gracias por las sugerencias. Tendré que leer un poco y probar diferentes soluciones. No todas las bases de datos admiten la indexación completa, MySQL (que estoy usando actualmente) sí lo hace ( dev.mysql.com/doc/refman/5.5/en/fulltext-search.html ). Intentaré hacer algunas pruebas y luego informar aquí.
Giorgio
2

Por si acaso te lo perdiste. Si utiliza Lucene para su base de datos en lugar de la búsqueda de texto compatible con DB, tendrá que ser extremadamente cuidadoso al modificar su DB. ¿Cómo se asegura de que puede tener atomicidad cuando tiene que hacer cambios tanto en la base de datos como en los recursos externos (Lucene)? Sí, se puede hacer, pero habrá mucho trabajo.

En resumen, está perdiendo el soporte transaccional de DB si coloca a Lucene en su esquema de datos.

InformadoA
fuente
1
El problema como se indicó no parece ser un buen ajuste para un RDMS de todos modos.
Pieter B
1

¿Has considerado la esfinge? http://sphinxsearch.com si puede usar una herramienta de terceros, esto sería ideal para lo que está tratando de lograr, es mucho más eficiente en la búsqueda de texto completo que cualquier RDBMS que yo haya usado personalmente.

ramita
fuente
3
y el voto negativo es para?
twigg
1

Es algo extraño que ninguna de las respuestas presentara el término "índice invertido" , la tecnología que subyace a todas las soluciones similares a Apache Lucene y otras.

El índice invertido es un mapeo de palabras a documentos ("índice invertido a nivel de registro") o incluso ubicaciones precisas de palabras dentro del documento ("índice invertido a nivel de palabra").

Las operaciones lógicas AND y OR son triviales de implementar. Si tiene ubicaciones precisas de palabras, es posible buscar palabras adyacentes, haciendo posible la búsqueda de frases.

Entonces, piense en un índice que contenga tuplas (palabra, archivo, ubicación). Cuando tiene, por ejemplo, ("invertido", "foo.txt", 123), simplemente verifica si ("índice", "foo.txt", 124) es parte del índice para buscar la frase completa "índice invertido" .

Si bien no le recomiendo que vuelva a implementar un motor de búsqueda de texto completo desde cero, es útil saber cómo funcionan tecnologías como Apache Lucene.

Por lo tanto, mi recomendación es aprender cómo funcionan los índices invertidos y elegir una tecnología que los use, como Apache Lucene. Entonces al menos tienes una sólida comprensión de lo que se puede hacer y lo que no se puede hacer.

juhist
fuente