CHAPITRE 6

Persistance et bases de données

Introduction

Par Aaron Watters, consultant informatique

On distingue trois sortes d’individus : ceux qui savent compter et ceux qui ne savent pas.

Cependant, il n’y a que deux sortes de programmes informatiques : les programmes jouets et ceux qui interagissent avec des bases de données persistantes. Ceci signifie que la plupart des vrais programmes informatiques doivent récupérer des informations qui ont été stockées au préalable et enregistrer des informations pour les utiliser plus tard. De nos jours, cette description s’applique à presque tous les jeux sur ordinateurs, qui doivent généralement sauvegarder et restaurer en permanence l’état d’une partie. Par conséquent, quand j’écris programmes jouets, je veux parler des programmes servant d’exercices ou écrits pour le plaisir de programmer. Quasiment tous les vrais programmes (comme ceux pour lesquels on paye ceux qui les écrivent) ont une composante de stockage/restauration manipulant une base de données persistante.

Lorsque je programmais en Fortran dans les années 80, j’avais remarqué que pour récupérer et stocker des informations presque tous les programmes utilisait des méthodes ad hoc. Comme les parties des programmes concernant le stockage et la restauration étaient celles qui intéressaient le moins les programmeurs, elles étaient souvent implémentées par-dessus la jambe et représentaient des sources abominables de bogues insolubles. Ces observations répétées m’ont convaincu que l’étude et l’implémentation des systèmes de bases de données étaient au cœur de la programmation pragmatique et que l’état de l’art que je constatais nécessitait de nombreuses améliorations.

Plus tard, à l’université, j’ai eu le plaisir de trouver un travail imposant et sophistiqué, lié à l’implémentation des bases de données. La littérature sur les systèmes de bases de données traitait, entre autres, des problèmes de concurrence, de tolérance aux pannes, de distribution, d’optimisation des requêtes, de conception des bases et de la sémantique des transactions. Selon la mode universitaire classique, la plupart de ces concepts ont été élaborés jusqu’à l’absurde (comme cette notion stupide de dépendances multi-valuées conditionnelles), mais la plupart du travail était directement lié à l’implémentation pratique de systèmes de stockage et de restauration fiables et efficaces. Le point de départ de ce travail était l’article de E.F. Codd, « A Relational Model of Data for Large Shared Data Banks7 ».

Chez mes camarades de promotion et même chez la plupart des étudiants de l’université, ce travail était soit ignoré soit considéré avec mépris. Tout le monde reconnaissait qu’une recherche sur la technologie relationnelle classique pourrait être lucrative, mais la considérait généralement au même niveau que l’apprentissage de l’écriture (ou, ce qui est plus important, de la maintenance) des programmes COBOL. Cette situation n’était pas aidée par le fait que le standard émergent des interfaces avec les bases de données, SQL (qui est désormais très bien établi), ressemblait à une extension de COBOL et peu aux langages de programmation modernes.

Plus de dix ans plus tard, il est peu probable que la technologie des bases de données relationnelles reposant sur SQL sera remplacée de sitôt dans les applications utilisant des bases de données. En fait, cette technologie semble plus influente que jamais. Les plus grands éditeurs de logiciels — IBM, Microsoft et Oracle — ont tous à leur catalogue des implémentations de bases de données relationnelles qui forment la composante essentielle de leur offre. D’autres sociétés informatiques, comme SAP et PeopleSoft, fournissent essentiellement des couches logicielles reposant sur des bases de données relationnelles.

En règle générale, les bases de données relationnelles ont été augmentées plutôt que remplacées. Le dogme du logiciel d’entreprise épouse souvent les systèmes « trois tiers », dans lesquels le tiers inférieur est une base de données relationnelle soigneusement conçue, le tiers du milieu définit une vue de cette base sous forme d’objets métiers et le tiers supérieur est formé d’applications ou de transactions qui manipulent ces objets et dont les effets sont finalement reportés dans les tables relationnelles sous-jacentes.

Le standard ODBC (Open Database Connectivity) de Microsoft fournit une API de programmation commune aux bases de données relationnelles, permettant ainsi aux programmes d’interagir avec différents moteurs de bases de données moyennant peu de modifications, voire aucune. Un programme Python, par exemple, peut d’abord être implémenté en utilisant Microsoft Jet8 comme serveur de bases de données pour les tests et le débogage. Lorsque ce programme est stable, il peut être mis en production et accéder à une base de données DB2 distante, située sur un mainframe IBM sur un autre continent, moyennant tout au plus la modification d’une seule ligne de code.

Les bases de données relationnelles ne conviennent pas à toutes les applications. Pour sauvegarder et restaurer des sessions, un jeu sur ordinateur ou un outil de conception industrielle, notamment, devraient sûrement utiliser une méthode plus directe que la représentation en tables des bases de données relationnelles pour assurer la persis- tance de leurs objets logiques. Cependant, même dans des domaines comme l’ingénierie ou le traitement des informations scientifiques, il est souvent souhaitable d’employer une approche hybride utilisant des méthodes relationnelles. J’ai, par exemple, vu un schéma de base de données relationnelles complexe pour archiver des informations sur les séquences génétiques — ces séquences étaient représentées par de gros objets binaires (BLOB) —, de même, un volume énorme d’informations auxiliaires peut très bien être stocké dans des tables relationnelles. Comme le lecteur l’aura probablement deviné, j’en ai peur, je suis un fanatique du modèle relationnel.

Dans le monde Python, il existe plusieurs façons d’assurer la persistance et de fournir les fonctionnalités des bases de données. Ma solution favorite est Gadfly, http://gadfly.sourceforge.net/, une implémentation SQL simple et minimale qui fonctionne essentiellement avec des bases de données en mémoire. Autre avantage, si elle ne vous convient plus, il est facile de passer à un autre moteur SQL plus solide. Nombreux sont ceux qui ont débuté une application avec Gadfly (à cause de sa simplicité d’utilisation) et qui ont basculé ensuite sur un autre moteur (car leurs besoins avaient grandi).

Cependant, de nombreux développeurs préfèrent commencer en utilisant d’autres implémentations de SQL, comme MySQL, Microsoft Access, Oracle, Sybase, Microsoft SQL Server, SQLite ou d’autres systèmes offrant les avantages d’une interface ODBC (ce qui n’est pas le cas de Gadfly).

Python fournit une interface standard pour accéder aux bases de données relationnelles : Py-DBAPI (Python DB Application Programming Interface), initialement conçue par Greg Stein. Chaque API de base de données nécessite une implémentation enveloppe de Py-DBAPI et il en existe pour toutes les interfaces sous-jacentes, notamment Oracle et ODBC.

Si l’approche relationnelle est disproportionnée par rapport à vos besoins, vous pouvez utiliser les outils prédéfinis de Python pour stocker et restaurer les données. Au niveau le plus bas, le programmeur peut manipuler directement les fichiers, comme cela est expliqué dans le chapitre 2. Un cran au-dessus des fichiers, le module marshal permet aux programmes de sérialiser les structures de données construites à partir de types Python simples (ce qui ne comprend ni les classes, ni les instances de classes). marshal a l’avantage de pouvoir récupérer à une vitesse phénoménale de grosses structures de données. Les modules pickle et cPickle permettent un stockage général des objets, ce qui comprend les classes, les instances de classe et les données circulaires. cPickle s’appelle ainsi parce qu’il est écrit en C et qu’il est donc assez rapide, tout en restant plus lent que marshal. Pour accéder à des données structurées sous une forme à peu près lisible, cela vaut également la peine de considérer un stockage et une restauration des données au format XML (en tirant parti des différents modules de Python pour le traitement et la génération de documents XML). Cette dernière approche est décrite dans le chapitre 8 — mais cette option fonctionne plutôt pour les applications de type écrite une fois, lue plusieurs fois. Les données sérialisées ou les représentations XML peuvent être stockées dans des bases de données relationnelles pour créer également une approche hybride.

Bien que marshal et pickle fournissent une sérialisation et une désérialisation de base des structures de données, un développeur d’applications souhaitera souvent plus de fonctionnalités, comme un support des transactions et un contrôle des accès simultanés. Lorsque le modèle relationnel ne convient pas à une application, une implémentation de base de données objet directe, comme la base de données Z-Object Database (ZODB) peut s’avérer pertinente — voir http://zope.org/Products/ZODB3.2.

Je dois conclure par une prière adressée à tous ceux qui dédaignent la technologie des bases de données relationnelles. N’oubliez pas que son succès a de bonnes raisons et qu’il peut être intéressant de les prendre en compte. Pour paraphraser Churchill :

 texte = """ Bien sûr, il a été dit que la démocratie était la
  pire forme de gouvernement, à l’exception de tous les autres qui ont
  été tentés de temps en temps. """
 import string
 for a, b in [("la démocratie", "SQL"), ("gouvernement", "bases de données")]:
  texte = string.replace(texte, a, b)
 print texte

6.1 Sérialiser des données avec le module marshal

Par Luther Blissett

Prob lème

Vous voulez sérialiser et reconstruire aussi vite que possible une structure de données Python dont les éléments sont des objets fondamentaux (des listes, des tuples, des nombres et des chaînes, mais ni des classes, ni des instances, etc.).

Solution

Si vous savez que vos données sont entièrement composées d’objets fondamentaux de Python (et que vous ne devez gérer qu’une seule version de Python, mais éventuellement sur des plates-formes différentes), l’approche de plus bas niveau, la plus rapide pour sérialiser vos données (c’est-à-dire les transformer en chaîne d’octets et les reconstruire plus tard à partir d’une telle chaîne) consiste à utiliser le module marshal. Supposons que donnees ne contienne que des éléments appartenant aux types élémentaires de Python :

 donnees = {12:’douze’, ’salut’:list(’ciao’), 1.23:4+5j, (1,2,3):u’wer’}

Vous pouvez alors sérialiser donnees à vitesse grand V dans une chaîne d’octets de la façon suivante :

 import marshal
 octets =...