在Python中序列化Sqlite3

时间:2011-06-09 16:19:54

标签: python multithreading concurrency sqlite

为了充分利用并发性,SQLite3允许线程以三种方式访问​​同一连接:

  1. 单螺纹。在此模式下,所有互斥锁都被禁用,并且SQLite一次不能在多个线程中使用。
  2. 多线程。在这种模式下,只要在两个或多个线程中不同时使用单个数据库连接,SQLite就可以被多个线程安全地使用。
  3. 序列化。在序列化模式下,SQLite可以被多个线程安全地使用而没有任何限制。
  4. 有谁知道如何在Python中序列化连接 Python具有“check_same_thread”,允许在多线程和单线程之间切换;但是,我无法知道如何将其序列化。

4 个答案:

答案 0 :(得分:6)

Python SQLite模块不是线程安全的。如果禁用其检查,则需要确保所有代码都已序列化并包含垃圾回收。 (我的APSW模块是线程安全的,也可以正确处理错误消息线程安全问题)。

然而,在同一个过程中同时使用多个独立连接是安全的,我建议你这样做。另外,将数据库切换到write ahead logging mode,即使有大量的写作,你也应该获得非常好的表现。

答案 1 :(得分:2)

我写了一个库来解决这个问题。适合我。

https://github.com/palantir/sqlite3worker

答案 2 :(得分:1)

sqlite页面http://www.sqlite.org/threadsafe.html说:“默认模式是序列化的。”你测试过它并发现这不是真的吗?

修改


如果它无法工作,也许是ctypes?我不知道这是否会对加载的sqlite模块产生任何影响。我想我有点怀疑它没有;正如我想象的那样,加载模块时可能会调用sqlite3_initialize()函数?或者也许只有在创建数据库对象时?

http://www.sqlite.org/c3ref/config.html

>>> import sqlite3
>>> import ctypes
>>> from ctypes.util import find_library
>>> sqlite_lib = ctypes.CDLL(find_library('sqlite3'))
>>> sqlite_lib.sqlite3_config(3) # http://www.sqlite.org/c3ref/c_abort.html
0   # no error....
>>> 

答案 3 :(得分:0)

Verse Quiz开始,您可能会对__init____serve__fetch方法感兴趣,以便开始在Python中创建序列化的SQLite3数据库接口。希望能进一步帮助你!


import _thread
import sqlite3
import queue

################################################################################

class Server:

    """Execute a protected SQLite3 database on a singular thread.

    Since a SQLite3 database can only accept queries on the thread that it
    was created on, this server receives requests through a queue and sends
    back the result through a list and mutex mechanism."""

    def __init__(self, *args):
        """Initialize the Server with a SQLite3 database thread."""
        self.__lock = _thread.allocate_lock()
        self.__lock.acquire()
        _thread.start_new_thread(self.__serve, args)
        self.__lock.acquire()
        del self.__lock
        if self.__error is not None:
            raise self.__error
        del self.__error

    def __serve(self, *args):
        """Run a server continuously to answer SQL queries.

        A SQLite3 connection is made in this thread with errors being raised
        again for the instantiator. If the connection was made successfully,
        then the server goes into a continuous loop, processing SQL queries."""
        try:
            database = sqlite3.connect(*args)
        except:
            self.__error = error = sys.exc_info()[1]
        else:
            self.__error = error = None
        self.__lock.release()
        if error is None:
            self.__QU = queue.Queue()
            while True:
                lock, one, sql, parameters, ret = self.__QU.get()
                try:
                    cursor = database.cursor()
                    cursor.execute(sql, parameters)
                    data = cursor.fetchone() if one else cursor.fetchall()
                    ret.extend([True, data])
                except:
                    ret.extend([False, sys.exc_info()[1]])
                lock.release()

    def fetch(self, one, sql, *parameters):
        """Execute the specified SQL query and return the results.

        This is a powerful shortcut method that is the closest connection
        other threads will have with the SQL server. The parameters for the
        query are dumped into a queue, and the answer is retrieved when it
        becomes available. This prevents SQLite3 from throwing exceptions."""
        lock, ret = _thread.allocate_lock(), []
        lock.acquire()
        self.__QU.put((lock, one, sql, parameters, ret))
        lock.acquire()
        if ret[0]:
            return ret[1]
        raise ret[1]