2010-08-19 19 views
13

Estoy almacenando una tabla usando Python y necesito persistencia.El estante es demasiado lento para diccionarios grandes, ¿qué puedo hacer para mejorar el rendimiento?

Esencialmente estoy almacenando la tabla como una cadena de diccionario para los números. Y el conjunto se almacena con balda

self.DB=shelve.open("%s%sMoleculeLibrary.shelve"%(directory,os.sep),writeback=True) 

utilizo writeback-True como descubrí el sistema tiende a ser inestable si no lo hago.

Después de los cálculos, el sistema necesita cerrar la base de datos y volver a almacenarla. Ahora la base de datos (la tabla) es de aproximadamente 540MB, y está demorando años. El tiempo explotó después de que la mesa creció a aproximadamente 500 MB. Pero necesito una mesa mucho más grande. De hecho, necesito dos de ellos.

Probablemente estoy usando la forma incorrecta de persistencia. ¿Qué puedo hacer para mejorar el rendimiento?

+0

¿Ha encontrado un uso inadecuado de la CPU con grandes dicts usando estante? –

Respuesta

13

Para almacenar un gran diccionario de pares de valores-clave string : number, sugiero una solución de almacenamiento nativa JSON como MongoDB. Tiene una maravillosa API para Python, Pymongo. MongoDB en sí mismo es liviano e increíblemente rápido, y los objetos json nativamente serán diccionarios en Python. Esto significa que puede usar su clave string como identificación del objeto, lo que permite el almacenamiento comprimido y la búsqueda rápida.

Como un ejemplo de lo fácil que sería el código, vea lo siguiente:

d = {'string1' : 1, 'string2' : 2, 'string3' : 3} 
from pymongo import Connection 
conn = Connection() 
db = conn['example-database'] 
collection = db['example-collection'] 
for string, num in d.items(): 
    collection.save({'_id' : string, 'value' : num}) 
# testing 
newD = {} 
for obj in collection.find(): 
    newD[obj['_id']] = obj['value'] 
print newD 
# output is: {u'string2': 2, u'string3': 3, u'string1': 1} 

Se podría simplemente tiene que convertir de nuevo a Unicode, lo que es trivial.

+0

Gracias. Los datos son en realidad un número de tabla simétrica * número -> número, pero como shelve quería cadenas como claves, de alguna manera fui inducido a escribirlo como una cadena de tabla -> número, donde la cadena es "a_b" con a y b números y a

1

¿Cuánto más? ¿Cuáles son los patrones de acceso? ¿Qué tipo de cálculo necesitas hacer en él?

Tenga en cuenta que tendrá algunos límites de rendimiento si no puede mantener la tabla en la memoria sin importar cómo lo haga.

Es posible que desee ver ir a SQLAlchemy, o directamente utilizando algo como bsddb, pero ambos sacrificarán la simplicidad del código. Sin embargo, con SQL puede descargar parte del trabajo a la capa de la base de datos dependiendo de la carga de trabajo.

+2

Estoy desarrollando un algoritmo teórico, por lo que el problema que estoy usando ahora probablemente tenga tablas de algunos giga. Pero como la gente usará el algoritmo para otros problemas (principalmente en biología de sistemas, piense en grande, luego aumente) es importante encontrar una solución que pueda escalar. El acceso es aleatorio, y cada término se accederá pocas veces. El único cálculo que necesito hacer es obtener el valor, calcular el valor si no está allí y almacenarlo. Estaba considerando usar MySQL para que el DB no estuviera en la memoria. Pero haría el código más complejo y más lento. Gracias. –

9

Según mi experiencia, recomendaría usar SQLite3, que viene con Python. Funciona bien con bases de datos y números de clave más grandes. Millones de claves y gigabytes de datos no son un problema. Shelve está totalmente perdido en ese punto. Además, tener un proceso de db por separado no es beneficioso, solo requiere más intercambios de contexto. En mis pruebas, descubrí que SQLite3 era la opción preferida para usar, al manejar localmente conjuntos de datos más grandes. Ejecutar el motor de base de datos local como mongo, mysql o postgresql no proporciona ningún valor adicional y también fue más lento.

0

Creo que su problema se debe al hecho de que utiliza el writeback=True. El documentation dice (el subrayado es mío):

Debido a la semántica de Python, un estante no se puede saber cuando se modifica una entrada mutable persistente-diccionario. Por defecto, los objetos modificados se escriben solo cuando se asignan al estante (ver Ejemplo).Si el parámetro de reescritura opcional se establece en True, todas las entradas a las que se accede son también almacenadas en la memoria caché y escritas de nuevo en sync() y close(); este puede hacer que sea más práctico para mutar entradas mutables en el diccionario persistente , pero, si se accede a muchas entradas, puede consumir grandes cantidades de memoria para la memoria caché, y puede hacer que la operación de cierre muy lento ya que todos los accesos las entradas se escriben de nuevo (no hay forma de para determinar qué entradas accesibles son mutables, ni cuáles estaban realmente mutados).

Usted podría evitar el uso de writeback=True y asegúrese de que los datos se escriben una sola vez (hay que prestar atención a que las modificaciones posteriores se van a perder).

Si crees que esta no es la opción de almacenamiento correcta (es difícil de decir sin saber cómo están estructurados los datos), sugiero sqlite3, está integrado en python (por lo tanto muy portátil) y tiene muy buenas actuaciones. Es algo más complicado que una simple tienda de valores-clave.

Vea otras respuestas para las alternativas.

Cuestiones relacionadas