2010-07-23 16 views
12

Me gustaría crear una: memoria: base de datos en python y acceder desde diferentes hilos. Esencialmente algo como:compartiendo a: memoria: base de datos entre diferentes hilos en python usando el paquete sqlite3

class T(threading.Thread): 
    def run(self): 
     self.conn = sqlite3.connect(':memory:') 
     # do stuff with the database 

for i in xrange(N): 
    T().start() 

y tienen todas las conexiones que se refieren a la misma base de datos.

Soy consciente de pasar check_same_thread=True a la función de conexión y compartir la conexión entre hilos, pero me gustaría evitar hacerlo si es posible. Gracias por cualquier ayuda.

EDITAR: corrigió un error tipográfico. Originalmente dije "tener todas las conexiones refiriéndose al mismo hilo" sustituyendo hilo por base de datos.

+0

Puede por favor describir el escenario para el que esto es necesario? ¿Puede haber otras opciones en lugar de usar sqlite desde varios subprocesos? –

+1

@Muhammad Alkarouri Lo necesitaba para probar la unidad de una aplicación de base de datos de múltiples hilos. Si se usa un archivo (como lo sería en la aplicación real), entonces puedo abrir múltiples conexiones si está bien. Terminé envolviendo la lógica de la base de datos en un hilo que usa el patrón del consumidor y devuelve diferidos que llena cuando llega a la solicitud. – aaronasterling

Respuesta

6

Sin hackear la biblioteca sqlite3 en sí no puede reutilizar la base de datos :memory:, ya que se garantiza que será exclusiva y privada para cada conexión. Para hackear el acceso a él, mire más de cerca al src/pager.c en la distribución sqlite3 (no en la distribución del módulo de Python). Tal vez, la forma más conveniente de implementar esto sería hacer :memory:00, :memory:something, :memory:okay_hai etc. alias para direccionar diferentes punteros pPager->memDb a través de un simple mapeo en el lado C.

+0

Hola, ¿hay alguna información nueva sobre esto casi 4 años después? ¿Todavía no es posible con sqlite3 actual en Python? – compostus

+0

@compostus, ahora es posible especificar el URI de la base de datos, consulte este http://www.sqlite.org/uri.html, busque 'mode = memory' y' cache = shared'. – toriningen

+0

@compostus, vea mi nueva respuesta a esta pregunta. – toriningen

25

SQLite ha mejorado en los últimos 4 años, por lo que ahora son posibles las bases de datos compartidas en memoria. Compruebe el siguiente código:

import sqlite3 

foobar_uri = 'file:foobar_database?mode=memory&cache=shared' 
not_really_foobar_uri = 'file:not_really_foobar?mode=memory&cache=shared' 

# connect to databases in no particular order 
db2 = sqlite3.connect(foobar_uri, uri=True) 
db_lol = sqlite3.connect(not_really_foobar_uri, uri=True) 
db1 = sqlite3.connect(foobar_uri, uri=True) 

# create cursor as db2 
cur2 = db2.cursor() 

# create table as db2 
db2.execute('CREATE TABLE foo (NUMBER bar)') 

# insert values as db1 
db1.execute('INSERT INTO foo VALUES (42)') 
db1.commit() 

# and fetch them from db2 through cur2 
cur2.execute('SELECT * FROM foo') 
print(cur2.fetchone()[0]) # 42 

# test that db_lol is not shared with db1 and db2 
try: 
    db_lol.cursor().execute('SELECT * FROM foo') 
except sqlite3.OperationalError as exc: 
    print(exc) # just as expected 

accesos de base de datos se enredan intencionadamente, para mostrar que dos conexiones a la base de datos en memoria con el mismo nombre son equivalentes desde el punto de vista de SQLite.

Referencias:

  1. SQLite URIs
  2. SQLite shared cache

Desafortunadamente, conexión por URI sólo está disponible desde Python 3.4. Sin embargo, si tiene Python 2.6 o posterior (pero no Python 3), el módulo sqlite3 incorporado aún puede importar conexiones APSW, que se pueden usar para lograr el mismo efecto. Aquí va la gota en el reemplazo sqlite3 módulo:

from sqlite3 import * 
from sqlite3 import connect as _connect 
from apsw import Connection as _ApswConnection 
from apsw import SQLITE_OPEN_READWRITE as _SQLITE_OPEN_READWRITE 
from apsw import SQLITE_OPEN_CREATE as _SQLITE_OPEN_CREATE 
from apsw import SQLITE_OPEN_URI as _SQLITE_OPEN_URI 

# APSW and pysqlite use different instances of sqlite3 library, so initializing 
# APSW won't help pysqlite. Because pysqlite does not expose any way to 
# explicitly call sqlite3_initialize(), here goes an ugly hack. This only has 
# to be done once per process. 
_connect(':memory:').close() 

def connect(database, timeout=5.0, detect_types=0, isolation_level=None, 
      check_same_thread=True, factory=Connection, cached_statements=100, 
      uri=False): 
    flags = _SQLITE_OPEN_READWRITE | _SQLITE_OPEN_CREATE 

    if uri: 
     flags |= _SQLITE_OPEN_URI 

    db = _ApswConnection(database, flags, None, cached_statements) 
    conn = _connect(db, timeout, detect_types, isolation_level, 
        check_same_thread, factory, cached_statements) 

    return conn 
+0

gracias mucho! Estaba buscando en Python 3.3 documentos, así que pasé por alto la opción 'uri' de' sqlite3.connect() ', que solo está ** disponible desde Python 3.4 **. – compostus

+0

@compostus, eres bienvenido! – toriningen

+0

¿cuál es la mejor alternativa (si existe) para cualquier persona que todavía esté en Python 2.7? – baconwichsand

Cuestiones relacionadas