2010-07-23 3 views
12

Я хотел бы создать: память: базу данных в python и получить доступ к ней из разных потоков. существу что-то вроде:Обмен a: память: база данных между различными потоками в python с использованием пакета 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() 

и имеют все соединения, относящиеся к одной и той же базе данных.

Мне известно о передаче check_same_thread=True функции подключения и обмениваться соединением между потоками, но хотелось бы избежать этого, если это возможно. Спасибо за любую помощь.

EDIT: исправлена ​​опечатка. Я изначально сказал, что «все соединения ссылаются на один поток», заменяя поток для базы данных.

+0

Опишите ситуацию, в которой это необходимо. Могут ли быть другие варианты, а не использовать sqlite из нескольких потоков? –

+1

@Muhammad Alkarouri Мне нужно было это для модульного тестирования многопоточного приложения базы данных. Если файл используется (как и в реальном приложении), я могу открыть несколько подключений, если просто отлично. Я закончил обертывание логики базы данных одним потоком, который использует шаблон потребителя и возвращает отложенные данные, которые он заполняет, когда он попадает на запрос. – aaronasterling

ответ

6

Без взлома библиотеки sqlite3 вы не можете повторно использовать базу данных :memory:, так как она гарантированно будет эксклюзивной и конфиденциальной для каждого подключения. Чтобы взломать доступ к нему, посмотрите ближе на src/pager.c в дистрибутиве sqlite3 (а не на дистрибутив Python). Возможно, наиболее удобным способом реализации этого было бы сделать :memory:00, :memory:something, :memory:okay_hai и т. Д. Псевдонимы для адреса разных указателей pPager->memDb через некоторое простое сопоставление C-стороны.

+0

Привет, любая новая информация об этом почти 4 года спустя? Возможно ли это с текущим sqlite3 в Python? – compostus

+0

@compostus, теперь можно указать URI базы данных, обратитесь к этому http://www.sqlite.org/uri.html, ищите 'mode = memory' и' cache = shared'. – toriningen

+0

@compostus, см. Мой новый ответ на этот вопрос. – toriningen

25

SQLite улучшился за последние 4 года, поэтому теперь доступны общие базы данных в памяти. Проверьте следующий код:

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 

базы данных доступы запутанный умышленно, чтобы показать, что два соединения с базой данных в оперативной памяти, с тем же именем, то же самое с точки SQLite зрения.

Ссылки:

  1. SQLite URIs
  2. SQLite shared cache

К сожалению, подключение по URI доступен только с Python 3.4. Однако, если у вас есть Python 2.6 или новее (но не Python 3), встроенный модуль sqlite3 по-прежнему способен импортировать соединения APSW, которые могут быть использованы для достижения такого же эффекта. Здесь идет опускание модуля sqlite3:

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

thanx много! Я смотрел на python 3.3 docs, поэтому я упустил опцию 'uri'' sqlite3.connect() ', которая доступна только ** с Python 3.4 **. – compostus

+0

@ compostus, добро пожаловать! – toriningen

+0

Что является лучшей альтернативой (если есть) для тех, кто все еще на Python 2.7? – baconwichsand