Método para indexar una base de datos de objetos

Estoy usando una base de datos de objetos (ZODB) para almacenar relaciones complejas entre muchos objetos, pero tengo problemas de rendimiento. Como resultado, comencé a construir índices para acelerar la recuperación e inserción de objetos. Aquí está mi historia y espero que puedas ayudar.

Inicialmente, cuando agregaría un objeto a la base de datos, lo insertaría en una twig dedicada a ese tipo de objeto. Para evitar que varios objetos representen la misma entidad, agregué un método que iteraría sobre los objetos existentes en la twig para encontrar duplicados. Esto funcionó al principio, pero a medida que la base de datos creció en tamaño, el tiempo que llevó cargar cada objeto en la memoria y comprobar los atributos creció de manera exponencial e inaceptable.

Para resolver ese problema, comencé a crear índices basados ​​en los atributos en el objeto para que cuando se agregara un objeto se guardara en la twig de tipo, así como dentro de una twig de índice de valor de atributo. Por ejemplo, digamos que estaba guardando un objeto de persona con atributos firstName = ‘John’ y lastName = ‘Smith’, el objeto se agregaría a la twig de tipo de objeto de persona y también se agregaría a las listas dentro de la twig de índice de atributo con claves ‘ John ‘y’ Smith ‘.

Esto ahorró mucho tiempo con la verificación de duplicados, ya que el nuevo objeto podría analizarse y solo sería necesario verificar el conjunto de objetos que se intersecan dentro de los índices de atributo.

Sin embargo, rápidamente me encontré con otro problema relacionado con el trato al actualizar objetos. Los índices deberían actualizarse para reflejar el hecho de que pueden dejar de ser precisos. Esto requiere recordar los valores antiguos para poder acceder a ellos directamente y eliminar el objeto o iterar sobre todos los valores de un tipo de atributo para encontrar y luego eliminar el objeto. De cualquier manera, el rendimiento comienza a degradarse rápidamente de nuevo y no puedo encontrar una manera de resolverlo.

¿Has tenido este tipo de problema antes? ¿Qué resolviste, o es solo algo con lo que tengo que lidiar cuando uso los OODBMS?

Gracias de antemano por la ayuda.

Sí, repoze.catalog es bueno y está bien documentado.

En resumen: ¡no haga que la indexación sea parte de la estructura de su sitio!

  1. Observe el uso de una jerarquía de elementos / contenedores para almacenar y atravesar objetos de elementos de contenido; haga planes para poder atravesar el contenido mediante (a) la ruta (los bordes del gráfico parecen un sistema de archivos) o (b) identificando contenedores singleton en alguna ubicación distinta.

  2. Identifique su contenido mediante los UUID RFC 4122 (uuidIDID tipo) o enteros de 64 bits.

  3. Use un catálogo central para indexar (por ejemplo, repoze.catalog); el catálogo debe estar en una ubicación conocida en relación con el objeto de aplicación raíz de su ZODB. Y su catálogo probablemente indexará los atributos de los objetos y devolverá los identificadores de registro (generalmente enteros) en la consulta. Su trabajo es asignar esos identificadores de enteros a (tal vez indirección a través de UUID) a alguna ruta de recorrido físico en la base de datos donde está almacenando el contenido. Es útil si usa zope.location y zope.container para interfaces comunes para recorrer su gráfico de objetos desde la raíz / aplicación hacia abajo.

  4. Use los manejadores zope.lifecycleevent para indexar el contenido y mantener las cosas frescas.

El problema – generalizado.

ZODB es demasiado flexible: es solo un gráfico de objetos persistentes con transacciones, pero esto deja espacio para que usted se hunda o nade en sus propias estructuras de datos e interfaces.

La solución – generalizada.

Por lo general, solo se seleccionarán los modismos preexistentes de la comunidad en torno a ZODB: zope.lifecycleevent handlers, recorrido en “contenedor” utilizando zope.container y zope.location, y algo así como repoze.catalog.

Mas particular

Solo cuando haya agotado los modismos generalizados y sepa por qué no funcionarán, intente crear sus propios índices utilizando los distintos sabores de BTrees en ZODB. De hecho, hago esto más de lo que me gustaría admitir, pero generalmente tengo una buena causa.

En todos los casos, mantenga sus índices (búsqueda, descubrimiento) y estructura del sitio (recorrido y almacenamiento) distintos.

Los modismos para el dominio del problema.

  • Maestro ZODB BTrees: probablemente quieras:

    • Para almacenar objetos de contenido como subclases de Persistente en contenedores que son subclases de OOBTree que proporcionan interfaces de contenedor (ver más abajo).
    • Para almacenar BTrees para su catálogo o índices globales o usar paquetes como repoze.catalog y zope.index para abstraer ese detalle (sugerencia: las soluciones de catálogo generalmente almacenan índices como OIBTrees que producirán identificadores de registros de enteros para los resultados de búsqueda; tipo de utilidad de mapeador de documentos que traduce esos identificadores de registros en algo que se puede resolver en tu aplicación como un uuid (siempre que puedas atravesar el gráfico hacia el UUID) o una ruta (como lo hace el catálogo de Zope2).
  • En mi humilde opinión, no se moleste en trabajar con referencias íntimas y claves y demás (esto es menos idiomático y más difícil si no los necesita). Simplemente use un Catálogo y DocumentMap de repoze.catalog para obtener resultados en forma de enteros a uuid o de ruta, y luego averigüe cómo obtener su objeto. Tenga en cuenta que es probable que desee alguna utilidad / singleton que tenga la tarea de recuperar su objeto dado un ID o un uuid devuelto de una búsqueda.

  • Utilice zope.lifecycleevent o un paquete similar que proporcione registros de callback de evento síncrono (manejador). Estos controladores son lo que debe llamar cada vez que se realice una edición atómica en su objeto (probablemente una vez por transacción, pero no en la maquinaria de transacción).

  • Aprende la architecture de componentes de Zope; no es un requisito absoluto, pero sin duda es útil, incluso si solo se entienden las interfaces zope.interface de paquetes ascendentes como zope.container

  • Comprensión de cómo Zope2 (ZCatalog) hace esto: un catálogo encabeza varios índices o varios tipos, cada uno de los cuales busca una consulta, cada uno tiene estructuras de datos especializadas y cada uno devuelve secuencias de ID de registros enteros. El catálogo los fusiona en los índices haciendo intersecciones de conjuntos y se devuelve como un mapeo perezoso de objetos “cerebrales” que contienen apéndices de metadatos (cada cerebro tiene un método getObject () para obtener el objeto de contenido real). Obtener objetos reales de una búsqueda de catálogo se basa en el lenguaje Zope2 de usar rutas desde el objeto de aplicación raíz para identificar la ubicación del elemento catalogado.

Piense en usar un hash de atributo (algo así como hashCode () de Java), luego use el valor de hash de 32 bits como la clave. Python tiene una función hash, pero no estoy realmente familiarizado con ella.