совместное использование: memory: базы данных между различными потоками в python с использованием пакета sqlite3 - PullRequest
13 голосов
/ 23 июля 2010

Я хотел бы создать базу данных: memory: на python и получить к ней доступ из разных потоков.По сути, что-то вроде:

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 функции соединения и совместном использовании соединения между потоками, нохотел бы избежать этого, если это возможно.Спасибо за любую помощь.

РЕДАКТИРОВАТЬ: исправлена ​​опечатка.Первоначально я сказал «иметь все соединения, ссылающиеся на один и тот же поток», заменяя поток для базы данных.

Ответы [ 2 ]

28 голосов
/ 12 июля 2014

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. URI SQLite
  2. Общий кэш SQLite

К сожалению, подключение по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
6 голосов
/ 12 октября 2010

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

...