compartiendo un :recuerdo:base de datos entre diferentes subprocesos en Python usando el paquete sqlite3

StackOverflow https://stackoverflow.com/questions/3315046

Pregunta

Me gustaría crear una :memoria:base de datos en Python y acceder a ella desde diferentes hilos.Básicamente algo como:

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 tener todas las conexiones referidas 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 subprocesos, pero le gustaría evitar hacerlo si es posible.Gracias por cualquier ayuda.

EDITAR:corregido un error tipográfico.Originalmente dije "tener todas las conexiones haciendo referencia al mismo hilo" sustituyendo el hilo por la base de datos.

¿Fue útil?

Solución

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

Otros consejos

SQLite ha mejorado en los últimos 4 años, por lo que ahora es posible compartir bases de datos en memoria.Verifique 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

Los accesos a la base de datos están entrelazados intencionalmente, para mostrar que dos conexiones a la base de datos en memoria con el mismo nombre son iguales desde el punto de vista de SQLite.

Referencias:

  1. URI de SQLite
  2. Caché compartido de SQLite

Desafortunadamente, la 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 software integrado sqlite3 El módulo todavía es capaz de importar conexiones APSW, que se pueden utilizar para lograr el mismo efecto.Aquí va el drop-in sqlite3 reemplazo del 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top