Saya memiliki program Python yang menggunakan modul "threading". Setiap detik sekali, program saya memulai utas baru yang mengambil beberapa data dari web, dan menyimpan data ini ke hard drive saya. Saya ingin menggunakan sqlite3 untuk menyimpan hasil ini, tetapi saya tidak dapat membuatnya berfungsi. Masalahnya tampaknya tentang baris berikut:
conn = sqlite3.connect("mydatabase.db")
- Jika saya meletakkan baris kode ini di dalam setiap utas, saya mendapatkan OperationalError yang memberi tahu saya bahwa file database terkunci. Saya kira ini berarti thread lain telah membuka mydatabase.db melalui koneksi sqlite3 dan telah menguncinya.
- Jika saya meletakkan baris kode ini di program utama dan meneruskan objek koneksi (conn) ke setiap utas, saya mendapatkan ProgrammingError, yang mengatakan bahwa objek SQLite yang dibuat di utas hanya dapat digunakan di utas yang sama.
Sebelumnya saya menyimpan semua hasil saya dalam file CSV, dan tidak mengalami masalah penguncian file ini. Mudah-mudahan ini bisa dilakukan dengan sqlite. Ada ide?
Jawaban:
Anda bisa menggunakan pola konsumen-produsen. Misalnya Anda dapat membuat antrian yang dibagikan antar utas. Untaian pertama yang mengambil data dari web mengantrekan data ini dalam antrean bersama. Utas lain yang memiliki koneksi database menghapus data dari antrian dan meneruskannya ke database.
sumber
Berlawanan dengan kepercayaan populer, versi yang lebih baru dari sqlite3 melakukan akses dukungan dari beberapa benang.
Ini dapat diaktifkan melalui argumen kata kunci opsional
check_same_thread
:sqlite.connect(":memory:", check_same_thread=False)
sumber
Berikut ini ditemukan di mail.python.org.pipermail.1239789
Saya telah menemukan solusinya. Saya tidak tahu mengapa dokumentasi python tidak memiliki satu kata pun tentang opsi ini. Jadi kita harus menambahkan argumen kata kunci baru ke fungsi koneksi dan kita akan dapat membuat kursor darinya di utas yang berbeda. Jadi gunakan:
sqlite.connect(":memory:", check_same_thread = False)
bekerja dengan sempurna untuk saya. Tentu saja mulai sekarang saya perlu menjaga akses multithreading yang aman ke db. Pokoknya terima kasih semua untuk mencoba membantu.
sumber
check_same_thread
opsi: "Ketika menggunakan beberapa thread dengan koneksi yang sama menulis operasi harus serial oleh pengguna korupsi menghindari data" Jadi ya, Anda dapat menggunakan SQLite dengan beberapa utas selama kode Anda memastikan bahwa hanya satu utas yang dapat menulis ke database pada waktu tertentu. Jika tidak, Anda mungkin merusak database Anda.Beralih ke multiprosesing . Jauh lebih baik, menskalakan dengan baik, dapat melampaui penggunaan beberapa inti dengan menggunakan banyak CPU, dan antarmukanya sama dengan menggunakan modul penguliran python.
Atau, seperti yang disarankan Ali, cukup gunakan mekanisme pengumpulan utas SQLAlchemy . Ini akan menangani semuanya untuk Anda secara otomatis dan memiliki banyak fitur tambahan, hanya untuk mengutip beberapa di antaranya:
sumber
Anda tidak boleh menggunakan utas sama sekali untuk ini. Ini adalah tugas yang sepele untuk twisted dan itu kemungkinan akan membawa Anda lebih jauh secara signifikan.
Gunakan hanya satu utas, dan penyelesaian permintaan memicu peristiwa untuk melakukan penulisan.
twisted akan menangani penjadwalan, panggilan balik, dll ... untuk Anda. Ini akan memberi Anda seluruh hasil sebagai string, atau Anda dapat menjalankannya melalui stream-processor (Saya memiliki Twitter API dan friendfeed API yang keduanya menjalankan peristiwa ke pemanggil karena hasilnya masih diunduh).
Bergantung pada apa yang Anda lakukan dengan data Anda, Anda bisa membuang hasil lengkapnya ke sqlite setelah selesai, memasaknya dan membuangnya, atau memasaknya saat sedang dibaca dan membuangnya di akhir.
Saya memiliki aplikasi yang sangat sederhana yang melakukan sesuatu yang mendekati apa yang Anda inginkan di github. Saya menyebutnya pfetch (pengambilan paralel). Itu mengambil berbagai halaman pada jadwal, mengalirkan hasil ke file, dan secara opsional menjalankan skrip setelah berhasil menyelesaikan masing-masing. Itu juga melakukan beberapa hal mewah seperti GET bersyarat, tetapi masih bisa menjadi dasar yang baik untuk apa pun yang Anda lakukan.
sumber
Atau jika Anda malas, seperti saya, Anda bisa menggunakan SQLAlchemy . Ini akan menangani threading untuk Anda, ( menggunakan utas lokal, dan beberapa penggabungan koneksi ) dan cara melakukannya bahkan dapat dikonfigurasi .
Untuk bonus tambahan, jika / ketika Anda menyadari / memutuskan bahwa menggunakan Sqlite untuk aplikasi bersamaan akan menjadi bencana, Anda tidak perlu mengubah kode Anda untuk menggunakan MySQL, atau Postgres, atau apa pun. Anda bisa beralih saja.
sumber
Anda perlu menggunakan
session.close()
setelah setiap transaksi ke database untuk menggunakan kursor yang sama di utas yang sama, tidak menggunakan kursor yang sama di multi-utas yang menyebabkan kesalahan ini.sumber
Gunakan threading.Lock ()
sumber
Saya suka jawaban Evgeny - Antrian umumnya merupakan cara terbaik untuk mengimplementasikan komunikasi antar-utas. Untuk kelengkapannya, berikut beberapa opsi lainnya:
OperationalError
, tetapi membuka dan menutup koneksi seperti ini umumnya No-No, karena overhead kinerja.sumber
Anda perlu merancang konkurensi untuk program Anda. SQLite memiliki batasan yang jelas dan Anda harus mematuhinya, lihat FAQ (juga pertanyaan berikut).
sumber
Scrapy sepertinya merupakan jawaban potensial untuk pertanyaan saya. Halaman beranda menjelaskan tugas saya dengan tepat. (Meskipun saya belum yakin seberapa stabil kodenya.)
sumber
Saya akan melihat modul y_serial Python untuk data persistensi: http://yserial.sourceforge.net
yang menangani masalah kebuntuan di sekitar database SQLite tunggal. Jika permintaan pada konkurensi menjadi berat, seseorang dapat dengan mudah mengatur kelas Farm dari banyak database untuk menyebarkan beban selama waktu stokastik.
Semoga ini bisa membantu proyek Anda ... seharusnya cukup sederhana untuk diterapkan dalam 10 menit.
sumber
Saya tidak dapat menemukan tolok ukur apa pun dalam jawaban di atas, jadi saya menulis tes untuk membandingkan semuanya.
Saya mencoba 3 pendekatan
Hasil dan kesimpulan dari benchmark adalah sebagai berikut
Anda dapat menemukan kode dan solusi lengkap untuk tolok ukur di jawaban saya JADI DI SINI Semoga membantu!
sumber
Alasan paling mungkin Anda mendapatkan kesalahan dengan database terkunci adalah Anda harus mengeluarkannya
conn.commit()
setelah menyelesaikan operasi database. Jika tidak, database Anda akan dikunci untuk menulis dan tetap seperti itu. Utas lain yang menunggu untuk ditulis akan habis waktunya (default disetel ke 5 detik, lihat http://docs.python.org/2/library/sqlite3.html#sqlite3.connect untuk detailnya) .
Contoh penyisipan yang benar dan bersamaan adalah ini:
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()
Jika Anda menyukai SQLite, atau memiliki fitur lain yang bekerja dengan database SQLite, atau ingin mengganti file CSV dengan file db SQLite, atau harus melakukan sesuatu yang langka seperti IPC antar-platform, maka SQLite adalah fitur yang hebat dan sangat sesuai untuk tujuan tersebut. Jangan biarkan diri Anda ditekan untuk menggunakan solusi yang berbeda jika rasanya tidak tepat!
sumber