J'ai un programme Python qui utilise le module "threading". Une fois par seconde, mon programme démarre un nouveau thread qui extrait des données du Web et les stocke sur mon disque dur. J'aimerais utiliser sqlite3 pour stocker ces résultats, mais je ne parviens pas à le faire fonctionner. Le problème semble concerner la ligne suivante:
conn = sqlite3.connect("mydatabase.db")
Auparavant, je stockais tous mes résultats dans des fichiers CSV et je n’avais aucun de ces problèmes de verrouillage de fichier. Espérons que cela sera possible avec sqlite. Des idées?
Vous pouvez utiliser le modèle consommateur-producteur. Par exemple, vous pouvez créer une file d'attente partagée entre les threads. Le premier thread qui récupère les données du Web les met en file d'attente dans la file d'attente partagée. Un autre thread qui possède la connexion à la base de données élimine les données de la file d'attente et les transmet à la base de données.
Contrairement aux idées reçues, les nouvelles versions de sqlite3 do prennent en charge les accès à partir de plusieurs threads.
Ceci peut être activé avec l'argument facultatif check_same_thread
:
sqlite.connect(":memory:", check_same_thread=False)
Ce qui suit se trouve sur mail.python.org.pipermail.1239789
J'ai trouvé la solution. Je ne sais pas pourquoi la documentation python n'a pas un seul mot sur cette option. Nous devons donc ajouter un nouvel argument de mot-clé à la fonction de connexion .__ et nous pourrons en créer des curseurs dans différents threads. Alors utilisez:
sqlite.connect(":memory:", check_same_thread = False)
fonctionne parfaitement pour moi. Bien sûr, à partir de maintenant, je dois veiller à assurer un accès multithreading sécurisé à la base de données. Quoi qu'il en soit, merci d'avoir essayé d'aider.
Vous ne devriez pas utiliser de fil du tout pour cela. Ceci est une tâche triviale pour twisted et cela vous mènerait probablement beaucoup plus loin de toute façon.
Utilisez un seul thread et faites en sorte que la requête complète déclenche un événement pour effectuer l'écriture.
twisted se chargera de la planification, des rappels, etc ... pour vous. Il vous donnera le résultat complet sous forme de chaîne ou vous pouvez l'exécuter via un processeur de flux (j'ai une API Twitter et une API friendfeed qui déclenchent tous les deux des événements en tant qu'appelants. toujours en cours de téléchargement).
Selon ce que vous faites avec vos données, vous pouvez simplement transférer le résultat complet dans sqlite au fur et à mesure de son achèvement, le cuire et le vider, ou le cuire pendant sa lecture et le vider à la fin.
J'ai une application très simple qui fait quelque chose de proche de ce que vous voulez sur github. Je l'appelle pfetch (extraction parallèle). Il saisit différentes pages d'un calendrier, transmet les résultats dans un fichier et exécute éventuellement un script à la fin de chacune d'elles. Il propose également des options sophistiquées telles que les GET conditionnels, mais pourrait tout de même constituer une bonne base pour tout ce que vous faites.
Basculer vers multitraitement . Il est bien meilleur, évolue bien, peut aller au-delà de l’utilisation de plusieurs cœurs en utilisant plusieurs processeurs, et l’interface est identique à celle du module de threading python.
Ou, comme suggéré par ALi, utilisez simplement le mécanisme de pool de threads de SQLAlchemy . Il gérera automatiquement tout pour vous et comportera de nombreuses fonctionnalités supplémentaires, pour ne citer que celles-ci:
Ou si vous êtes paresseux, comme moi, vous pouvez utiliser SQLAlchemy . Il gérera le thread pour vous ( en utilisant un thread local et un regroupement de connexions ) et la façon dont il le fait est même configurable .
Pour un bonus supplémentaire, si/quand vous réalisez/décidez que l'utilisation de Sqlite pour une application concurrente sera un désastre, vous n'aurez pas à changer votre code pour utiliser MySQL, ou Postgres, ou toute autre chose. Vous pouvez simplement basculer.
Vous devez utiliser session.close()
après chaque transaction avec la base de données pour pouvoir utiliser le même curseur dans le même thread sans utiliser le même curseur dans les multi-threads à l'origine de cette erreur.
J'aime la réponse d'Evgeny - Les files d'attente sont généralement le meilleur moyen d'implémenter la communication inter-thread. Pour être complet, voici quelques autres options:
OperationalError
, mais l'ouverture et la fermeture de connexions de ce type constituent généralement un non-non, en raison de la surcharge de performances.Utilisez threading.Lock ()
Scrapy semble être une réponse possible à ma question. Sa page d'accueil décrit ma tâche exacte. (Bien que je ne sois pas sûr de la stabilité du code.)
Vous devez concevoir la simultanéité pour votre programme. SQLite a des limitations claires et vous devez les respecter, voir le FAQ (également la question suivante).
Je ne pouvais trouver aucune référence dans aucune des réponses ci-dessus, j'ai donc écrit un test pour tout comparer.
J'ai essayé 3 approches
Les résultats et les points à retenir de l’indice de référence sont les suivants
Vous pouvez trouver le code et la solution complète pour les tests de performance dans ma SO réponse ICI J'espère que cela vous aidera!
Je voudrais jeter un oeil sur le module Python y_serial pour la persistance des données: http://yserial.sourceforge.net
qui gère les problèmes de blocage liés à une base de données SQLite unique. Si la demande en accès simultané devient lourde, vous pouvez facilement configurer la classe Farm de nombreuses bases de données pour répartir la charge sur le temps stochastique.
J'espère que cela aidera votre projet ... il devrait être assez simple à mettre en œuvre en 10 minutes.