2010-07-23 10 views
12

Ich möchte eine erstellen: Speicher: Datenbank in Python und greifen Sie auf es von verschiedenen Threads. Grunde so etwas wie:sharing a: Arbeitsspeicher: Datenbank zwischen verschiedenen Threads in Python mit sqlite3 Paket

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

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

und haben alle Verbindungen zu derselben Datenbank bezieht.

Ich bin bewusst, check_same_thread=True an die Verbindungsfunktion übergeben und teilen Sie die Verbindung zwischen Threads, aber das möchte, wenn möglich zu vermeiden. Danke für jede Hilfe.

EDIT: korrigiert einen Tippfehler. Ich habe ursprünglich gesagt "haben alle Verbindungen auf den gleichen Thread verweisen" Thread für die Datenbank ersetzen.

+0

Können Sie bitte das Szenario beschreiben, für die dies benötigt wird? Kann es sein, dass es andere Optionen gibt als sqlite aus mehreren Threads zu verwenden? –

+1

@Muhammad Alkarouri Ich brauchte es für Unit-Tests eine Multi-Thread-Datenbank-Anwendung. Wenn eine Datei verwendet wird (wie es in der tatsächlichen Anwendung wäre), dann kann ich mehrere Verbindungen öffnen, wenn es gut geht. Am Ende habe ich die Datenbanklogik in einen Thread eingepackt, der das Consumer-Muster verwendet und zurückgebrachte Defaults zurückgibt, wenn es die Anfrage erhält. – aaronasterling

Antwort

6

Ohne hacking sqlite3 Bibliothek selbst können Sie :memory: Datenbank nicht wiederverwenden, weil es garantiert exklusiv und privat für jede Verbindung ist. Um Zugriff darauf zu hacken, schauen Sie sich src/pager.c in sqlite3 distribution genauer an (nicht Python-Modul-Distribution). Vielleicht wäre der einfachste Weg, dies zu implementieren, :memory:00, :memory:something, :memory:okay_hai usw. Aliase zu machen, um verschiedene pPager->memDb Zeiger durch eine einfache C-seitige Zuordnung zu adressieren.

+0

Hallo, gibt es neue Informationen darüber fast 4 Jahre später? Ist es mit dem aktuellen sqlite3 in Python noch nicht möglich? – compostus

+0

@compostus, es ist jetzt möglich mit der Angabe der Datenbank-URI, siehe http://www.sqlite.org/uri.html, suchen Sie nach 'mode = memory' und' cache = shared'. – toriningen

+0

@compostus, siehe meine neue Antwort auf diese Frage. – toriningen

25

SQLite hatte sich in den letzten 4 Jahren verbessert, so dass jetzt gemeinsame In-Memory-Datenbanken möglich sind. Überprüfen Sie den folgenden Code ein:

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 

Datenbankzugriffe absichtlich verstrickt sind, um zu zeigen, dass zwei Verbindungen zum In-Memory-Datenbank mit dem gleichen Namen das gleiche von SQLite-Sicht sind.

Referenzen:

  1. SQLite URIs
  2. SQLite shared cache

Leider Verbindung von URI ist nur da Python 3.4 verfügbar. Wenn Sie jedoch Python 2.6 oder höher (aber nicht Python 3) verwenden, ist das eingebaute Modul sqlite3 immer noch in der Lage, APSW-Verbindungen zu importieren, die für denselben Effekt verwendet werden können. Hier geht das Drop-In sqlite3 Modul Ersatz:

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

danke viel! Ich habe mir Python 3.3-Dokumente angeschaut, also habe ich die 'uri'-Option von' sqlite3.connect() 'übersehen, die erst seit Python 3.4 ** verfügbar ist. – compostus

+0

@compostus, herzlich willkommen! – toriningen

+0

Was ist die beste Alternative (falls vorhanden) für irgendjemand noch auf Python 2.7? – baconwichsand