У меня есть программа Python, которая использует модуль "threading". Раз в секунду моя программа запускает новый поток, который извлекает некоторые данные из Интернета и сохраняет эти данные на моем жестком диске. Я хотел бы использовать sqlite3 для хранения этих результатов, но я не могу заставить его работать. Проблема заключается в следующей строке:
conn = sqlite3.connect("mydatabase.db")
Раньше я сохранял все свои результаты в файлах CSV и не имел ни одной из этих проблем с блокировкой файлов. Надеюсь, это будет возможно с помощью sqlite. Любые идеи?
Вы можете использовать шаблон потребительского производителя. Например, вы можете создать очередь, которая разделяется между потоками. Первый поток, который извлекает данные из сети, помещает эти данные в общую очередь. Другой поток, которому принадлежит соединение с базой данных, удаляет данные из очереди и передает их в базу данных.
Вопреки распространенному мнению, более новые версии sqlite3 do поддерживают доступ из нескольких потоков.
Это можно включить с помощью необязательного аргумента ключевого слова check_same_thread
:
sqlite.connect(":memory:", check_same_thread=False)
Ниже показано на mail.python.org.pipermail.1239789
Я нашел решение. Я не знаю, почему в документации на python нет ни слова об этой опции. Поэтому нам нужно добавить новый аргумент ключевого слова в функцию подключения
и мы сможем создавать курсоры из него в разных потоках. Поэтому используйте:
sqlite.connect(":memory:", check_same_thread = False)
отлично работает для меня. Конечно, теперь я должен заботиться безопасного многопоточного доступа к db. В любом случае, все пытаются помочь.
Вы не должны использовать нитки для этого. Это тривиальная задача для twisted, и это, вероятно, значительно повлияет на вас.
Используйте только один поток и завершите запрос, чтобы инициировать событие для записи.
twisted позаботится о планировании, обратных вызовах и т.д. для вас. Он передаст вам весь результат в виде строки, или вы можете запустить его через потоковый процессор (у меня есть twitter API и API-интерфейс friendfeed, который как отключает события для вызывающих, так и результаты по-прежнему загружаются).
В зависимости от того, что вы делаете с вашими данными, вы можете просто скомпилировать полный результат в sqlite по мере его завершения, сварить и свалить, или приготовьте его, пока он читается, и выгрузите его в конце.
У меня очень простое приложение, которое делает что-то близкое к тому, что вы хотите на github. Я называю это pfetch (параллельная выборка). Он захватывает различные страницы по расписанию, передает результаты в файл и, возможно, запускает script после успешного завершения каждого из них. Он также делает некоторые причудливые вещи, такие как условные GET, но все же может быть хорошей основой для того, что вы делаете.
Переключитесь на multiprocessing. Это намного лучше, хорошо масштабируется, может выходить за рамки использования нескольких ядер с использованием нескольких процессоров, а интерфейс такой же, как с использованием модуля потоковой передачи python.
Или, как предположил Али, просто используйте механизм объединения потоков SQLAlchemy. Он будет обрабатывать все для вас автоматически и имеет много дополнительных функций, просто чтобы процитировать некоторые из них:
Или, если вы ленивы, как и я, вы можете использовать SQLAlchemy. Он будет обрабатывать потоки для вас, (используя поток local и некоторый пул соединений), и как он это делает, даже настраивается.
Для дополнительного бонуса, если/когда вы понимаете/решаете, что использование Sqlite для любого параллельного приложения будет катастрофой, вам не придется менять свой код на использование MySQL или Postgres или что-то еще. Вы можете просто переключиться.
Используйте threading.Lock()
Я бы посмотрел на модуль y_serial Python для сохранения данных: http://yserial.sourceforge.net
который обрабатывает проблемы взаимоблокировки, связанные с одной базой данных SQLite. Если спрос на concurrency становится тяжелым, можно легко настроить класс Farm из многих баз данных, чтобы рассеять нагрузку на стохастическое время.
Надеюсь, это поможет вашему проекту... он должен быть достаточно простым, чтобы реализовать через 10 минут.
Scrapy кажется потенциальным ответом на мой вопрос. Его домашняя страница описывает мою точную задачу. (Хотя я не уверен, насколько стабилен код.)
Вам нужно создать concurrency для вашей программы. SQLite имеет четкие ограничения, и вам нужно их соблюдать, см. FAQ (также следующий вопрос).
Мне нравится ответ Евгения. Очереди - это, как правило, лучший способ реализовать межпоточную связь. Для полноты, вот некоторые другие варианты:
OperationalError
, но открытие и закрытие соединений, как это, как правило, No-No из-за служебных накладных расходов.Наиболее вероятная причина, по которой вы получаете ошибки с заблокированными базами данных, заключается в том, что вы должны указать
conn.commit()
после завершения операции с базой данных. Если вы этого не сделаете, ваша база данных будет заблокирована для записи и останется такой. Другие потоки, которые ждут записи, будут тайм-аутом через некоторое время (по умолчанию установлено значение 5 секунд, см. http://docs.python.org/2/library/sqlite3.html#sqlite3.connect для получения подробной информации об этом).
Примером правильной и параллельной вставки будет следующее:
import threading, sqlite3
class InsertionThread(threading.Thread):
def __init__(self, number):
super(InsertionThread, self).__init__()
self.number = number
def run(self):
conn = sqlite3.connect('yourdb.db', timeout=5)
conn.execute('CREATE TABLE IF NOT EXISTS threadcount (threadnum, count);')
conn.commit()
for i in range(1000):
conn.execute("INSERT INTO threadcount VALUES (?, ?);", (self.number, i))
conn.commit()
# create as many of these as you wish
# but be careful to set the timeout value appropriately: thread switching in
# python takes some time
for i in range(2):
t = InsertionThread(i)
t.start()
Если вам нравится SQLite или другие инструменты, которые работают с базами данных SQLite, или вы хотите заменить CSV файлы файлами SQLite db или должны делать что-то редкое, как межплатформенный IPC, то SQLite - отличный инструмент и очень подходит для цель. Не позволяйте себе оказывать давление на использование другого решения, если оно не кажется правильным!