Блокировка базы данных sqlite3 в Python (повторный запрос разъяснений) - PullRequest
7 голосов
/ 31 января 2012

Несколько недель назад я опубликовал этот вопрос на SO относительно того, как заблокировать базу данных sqlite3 в python:

Как заблокировать базу данных sqlite3 в Python?

Однако я не совсем уверен, что ответ работает. Или, может быть, я просто неправильно понимаю ответ.

Вот ситуация, с которой я столкнулся:

  • У меня есть база данных "test"
  • В базе данных "test" есть одна таблица "book"
  • В таблице "book" есть два столбца: "title" и "checked_out_by"

Тогда у меня есть функция, которая работает так:

def checkout(title, user):
    con = get_connection_from_db()
    with con:
        checked_out_by = get_checked_out_by(title)
        if checked_out_by == '': # If NOT checked out:
            checkout(title, user)
            print user, "checked out", title
        elif checked_out_by == 'user':
            print user, "already got it"
        else:
            print user, "can't check it out because", checked_out_by, "has it!"

Таким образом, функция checkout () сначала проверяет, что книга НЕ извлечена, и, если это так, проверяет книгу. Обратите внимание, что я использую рекомендованный трюк "с con:", чтобы гарантировать, что все будет транзакционным, счастливым и копацетным.

Однако я запустил несколько тестов параллелизма и обнаружил проблемы. В частности, когда я запускаю два следующих вызова одновременно:

checkout('foo', 'steve')
checkout('foo', 'tim')

Вывод показывает, что он работает не совсем правильно. Я ожидаю увидеть один из двух следующих возможных выводов:

steve checked out foo
tim can't check it out because steve has it!

OR

tim checked out foo
steve can't check it out because tim has it!

Но иногда я получаю такой вывод:

tim checked out foo
steve checked out foo

Я думал, что трюк 'with con:' обеспечит связывание моих вызовов в БД. Может ли кто-нибудь объяснить мне, если / как я понял это неправильно? Если так, есть ли способ заставить это работать?

Ответы [ 2 ]

29 голосов
/ 12 октября 2012

'с con' НЕ то, что здесь нужно. (или эта нить, запирающая мусор)

Чтобы получить эксклюзивный доступ на определенный период (а не только во время выполнения отдельного запроса / транзакции), вам нужно сделать;

con = sqlite3.connect()
con.isolation_level = 'EXCLUSIVE'
con.execute('BEGIN EXCLUSIVE')
#exclusive access starts here. Nothing else can r/w the db, do your magic here.
con.commit()
con.close()

Надеюсь, это спасет кого-то от поисков / экспериментов, через которые я только что прошел!

Помните, что он не является эксклюзивным, пока вы не запустите start exclusive, и он останется эксклюзивным, пока вы не закроете (или не запустите commit, я думаю). Вы всегда можете протестировать приложение интерпретатора Python / CL sqlite3, если не уверены.

0 голосов
/ 31 января 2012

Следует отметить одну важную вещь: когда база данных заблокирована, это означает, что она не принимает несколько писателей.Однако он принимает несколько считывателей.

Один простой способ проверить, работала ли транзакция должным образом, - записать значение в базу данных и после этого вызвать исключение до завершения кода транзакции.Если значение не записано, то транзакция работала нормально.В противном случае что-то не так.

Транзакции базы данных - это оптимистический подход к параллелизму, то есть они терпят неудачу только тогда, когда собираются сделать коммит.Поскольку кажется, что вы ищете пессимистический подход, возможно, вам следует попробовать threading.Lock :

import threading
db_lock = threading.Lock()

def checkout(title, user):
    with db_lock:
        con = get_connection_from_db()
        with con:
            checked_out_by = get_checked_out_by(title)
            if checked_out_by == '': # If NOT checked out:
                checkout(title, user)
                print user, "checked out", title
            elif checked_out_by == 'user':
                print user, "already got it"
            else:
                print user, "can't check it out because", checked_out_by, "has it!"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...