Choisir les bonnes bases de données pour les microservices

Immeuble de bureaux éclairé la nuit

Les différents facteurs à prendre en compte au moment de déterminer les meilleures options de base de données lors de la restructuration vers une approche de microservices.

Dans un article précédent, nous avons abordé quelques-uns des différents aspects qui peuvent vous conduire à restructurer votre code pour passer à une approche basée sur les microservices. L’un des derniers aspects que nous avons abordés était la question de savoir quoi faire de vos données. Dans les applications d’entreprise à grande échelle, c’est souvent la question la plus épineuse qui mérite d’être traitée de manière plus approfondie. Nous avons constaté qu’il est parfois difficile de déterminer s’il s’agit d’un problème de codage ou d’un problème de modélisation de données qui se fait passer pour un problème de codage.

Nous examinerons plusieurs de ces cas et discuterons des choix de modélisation de données que vous pouvez faire pour simplifier et améliorer votre code restructuré. Mais d’abord, il convient de répondre à ce qui est souvent la première question posée par les équipes qui transforment des applications d’entreprise existantes en microservices.

 

Pour commencer en douceur : une seule grande base de données ou plusieurs petites bases de données ?

Si votre application ressemble à la plupart des applications que nous voyons qui commencent à être restructurées en microservices, nous pouvons probablement supposer qu’elle fonctionne avec une seule et grande base de données relationnelle. De plus, il y a une chance sur deux que cette base de données soit une base de données Oracle ; toutes les autres bases de données relationnelles (DB2, SQL Server, Informix, ou même une base de données open source telle que MySQL ou Postgres) se divisent la part restante. En fait, l’abandon de la base de données relationnelle d’entreprise (généralement coûteuse) est l’un des avantages souvent mis en avant pour la restructuration vers les microservices.

Aujourd’hui, il y a de très bonnes raisons de choisir d’autres types de bases de données, NewSQL ou NoSQL, pour de nombreux microservices. Cependant, abandonner d’un seul coup votre base de données relationnelle actuelle est souvent une décision imprudente. Au lieu de cela, vous pourriez envisager une approche plus progressive pour modifier votre base de données, tout comme nous préconisons une approche progressive pour restructurer votre code Java existant.

Tout comme pour l’approche progressive du codage que nous avons préconisée, le plus gros problème lorsqu’il s’agit de suivre une approche progressive pour la restructuration des bases de données est de savoir par où commencer. La première décision que vous devez prendre une fois que vous avez opté pour une approche progressive est de savoir s’il faut opter pour une seule grande base de données ou plusieurs petites bases de données. À première vue, cela semble absurde : bien sûr, vous ne voulez pas d’une grande base de données, c’est ce que vous avez dans votre monolithe ! Mais expliquons d’abord ce que nous entendons par là.

Il s’agit essentiellement de faire la distinction entre un serveur de base de données et un schéma de base de données. Pour ceux d’entre vous qui connaissent les bases de données d’entreprise comme Oracle ou Db2, il s’agit d’une seconde nature car les entreprises disposent généralement d’un grand serveur Oracle (ou d’un RAC, qui est un grand serveur composé de plusieurs petits serveurs) sur lequel plusieurs équipes hébergent leurs propres bases de données séparées (chacune représentée par un schéma distinct). Cela est dû au fait que les licences sont souvent accordées par unité centrale et que l’entreprise veut maximiser le nombre d’utilisations qu’elle peut obtenir pour son argent. Regrouper plusieurs équipes au sein d’un grand serveur est un moyen d’y parvenir. Pour ceux d’entre vous qui sont plus familiers avec les bases de données open source comme MySQL ou PostgreSQL, cela est un peu moins courant car la distinction est moins souvent nécessaire.

Cela est important car lorsque nous parlons de construire des bases de données pour les microservices, il est important de réduire ou d’éliminer le couplage au niveau de la base de données, mais cela signifie en réalité un couplage au niveau du schéma de la base de données. Des problèmes surviennent lorsque vous avez deux microservices différents qui utilisent les mêmes informations, les mêmes tables dans le même schéma. Vous comprendrez ce que nous voulons dire en regardant la figure 1.

Figure 1 : Application monolithique fonctionnant sur un seul grand schéma.
Figure 1 : Application monolithique fonctionnant sur un seul grand schéma.

En matière de base de données, l’équivalent d’une application monolithique (ou « Big Ball of Mud », c.-à-d. la « grosse boule de boue » dans laquelle toutes les connexions se font dans tous les sens) est l’utilisation d’un grand schéma unique qui connecte toutes les tables. Dans ce cas, le travail de démêlage nécessaire pour séparer les tables est énorme. 

Cependant, lors de la transition vers les microservices, il faut prendre conscience que le partage du matériel et des licences au niveau du serveur engendre beaucoup moins de problèmes. De fait, lors de la restructuration initiale en microservices, il y a de nombreux avantages à conserver les nouveaux schémas plus nettement séparés dans les mêmes serveurs d’entreprise, car les entreprises ont généralement déjà des procédures en place pour la sauvegarde et la restauration des bases de données et pour les mises à jour des serveurs de base de données, dont les équipes peuvent profiter. 

D’une certaine manière, ce que l’entreprise offre en fournissant le matériel, le logiciel et la gestion d’une base de données d’entreprise est une version limitée d’une base de données en tant que service (DBaaS). Cela correspond particulièrement bien à une approche qui commence par une séparation plus nette des parties de votre monolithe par domaine fonctionnel, en commençant par une approche de « monolithe modulaire », comme illustré à la figure 2.

Figure 2 : Monolithe modulaire et schémas restructurés multiples
Figure 2 : Monolithe modulaire et schémas restructurés multiples

Dans cet exemple (destiné à montrer un travail de structuration en cours), vous pouvez voir comment la base de données a été décomposée en séparant les tables selon trois nouveaux schémas (A, B et C) qui correspondent à des modules spécifiques dans l’application restructurée. Une fois séparés de cette manière, ils peuvent être clairement décomposés en microservices distincts. Cependant, D et E sont encore en cours de restructuration : ils partagent toujours un seul schéma avec des tables interconnectées.

Finalement, même la liaison au niveau de la base de données et du serveur peut devenir un problème. Par exemple, vous êtes limité aux fonctionnalités disponibles d’une base de données d’entreprise si c’est ce que vous choisissez. Même dans un modèle relationnel, tous les schémas n’ont pas besoin de toutes ces fonctionnalités, ou ils peuvent avoir besoin de fonctionnalités mieux prises en charge par un autre serveur (par exemple, un meilleur partitionnement est souvent une raison d’utiliser une base de données NewSQL). De même, mettre à jour un serveur de base de données partagé par plusieurs microservices pourrait mettre plusieurs services hors service simultanément. 

Mais il s’agit d’une décision qui peut être reportée : il n’est pas nécessaire de la prendre immédiatement au début du projet. Lorsque les équipes commencent une restructuration, les conserver dans le même serveur de base de données pendant au moins les premières étapes d’un projet de restructuration permet d’apporter des modifications progressives tout en acquérant une expérience nécessaire en matière de restructuration de code et de base de données.

Envisager des modèles non relationnels

Comme indiqué dans la dernière section, à mesure que vous progressez dans le processus de restructuration, vous pouvez réfléchir un peu plus à vos options en matière de votre base de données, car tous les jeux de données ne sont pas parfaitement adaptés à un modèle relationnel. Le choix de la meilleure approche pour gérer ces ensembles de données se résume souvent à la question suivante : « Que stockez-vous réellement dans votre base de données ? ».

Nous avons passé des années à aider les entreprises à élaborer, maintenir et souvent malmener des cadres de mappage objet-relationnel. Cependant, le fait est que, dans plusieurs cas, les données stockées ne s’adaptaient pas du tout à un modèle de données relationnel. Chaque fois que cela se produisait, nous devions soit « tordre » le modèle relationnel pour qu’il s’adapte, soit, plus souvent, faire des pirouettes dans les programmes afin de forcer le code à correspondre au magasin de données relationnel.

Maintenant que nous avons évolué et avons le choix en matière de persistance polyglotte, nous sommes en mesure de réévaluer ces décisions et d’en adopter de plus judicieuses. Nous allons nous pencher sur quatre cas différents où il est évident que le modèle relationnel n’était pas la meilleure option, puis étudier un cas où le modèle relationnel était la meilleure option et où mettre les données sous une autre forme n’aurait pas été la bonne approche.

IBM DevOps

Qu’est-ce que le DevOps ?

Andrea Crawford présente le DevOps, démontre sa valeur, et explique de quelle façon les pratiques et les outils DevOps vous aident à faire progresser vos applications dans l’ensemble du pipeline de livraison logiciel, de l’idéation à la production. Dirigé par des leaders d’opinion d’IBM, le programme a pour but d’aider les chefs d’entreprise à acquérir les connaissances nécessaires pour donner la priorité aux investissements dans l’IA capables de stimuler la croissance.

Gestion du stockage Blob

À de nombreuses reprises, nous avons examiné le code de persistance des systèmes d’entreprise pour découvrir, à notre grande surprise, que ce qu’ils stockaient réellement dans leurs bases de données relationnelles était des représentations binaires d’objets Java sérialisés. Ces éléments sont stockés dans des colonnes « Binary Large Object » ou « Blob » et sont généralement le résultat d’une équipe qui a baissé les bras devant la complexité du mappage de ses objets Java dans des tables et colonnes relationnelles. Le stockage Blob présente certains inconvénients : il ne peut jamais être interrogé colonne par colonne ; il est souvent lent ; et il est sensible aux changements de structure des objets Java eux-mêmes, les données plus anciennes peuvent devenir illisibles si la structure de l’objet change de manière significative.

Si votre application (ou, plus probablement, un sous-ensemble de votre application) utilise du stockage Blob dans une base de données relationnelle, c’est déjà un bon signe que vous feriez mieux d’utiliser un magasin clé-valeur comme Memcached ou Redis. Par ailleurs, il pourrait être utile de prendre du recul et de réfléchir plus attentivement au contenu que vous stockez. S’il s’avère qu’il s’agit simplement d’un objet Java structuré (peut-être profondément structuré, mais pas nativement binaire), il est préférable d’utiliser un magasin de documents comme Cloudant ou MongoDB. De plus, en consacrant quelques efforts à la manière dont vous stockez vos documents (par exemple, les deux bases de données mentionnées ci-dessus sont des magasins de documents JSON, et les analyseurs JSON sont largement disponibles et faciles à personnaliser), vous pouvez gérer tous les problèmes de « dérive de schéma » beaucoup plus facilement qu’avec une approche de stockage Blob, dont le mécanisme de stockage est beaucoup plus opaque.

Les objets plats et le modèle Active Record

Il y a quelques années, lorsque Martin Fowler rédigeait « Patterns of Enterprise Application Architectures » sur les modèles d’architecture d’applications d’entreprise, nous avons eu une correspondance active et plusieurs réunions de révision animées concernant bon nombre de ces modèles. L’un d’entre eux, en particulier, a toujours été considéré comme une bizarrerie : le modèle Active Record. C’était étrange car nous venions de la communauté Java, mais nous ne l’avions jamais rencontré (bien que Martin nous ait assuré qu’il était connu dans la communauté de programmation Microsoft .NET). Mais ce qui nous a vraiment frappés, surtout lorsque nous avons commencé à voir quelques implémentations Java utilisant des technologies open source comme Ibatis, c’est qu’il semblait que la meilleure utilisation était lorsque les objets étaient, disons, plats.

Si l’objet que vous mappez à une base de données est complètement et totalement plat, sans aucune relation avec d’autres objets (à l’exception limitée des objets imbriqués), vous ne profitez probablement pas de toutes les capacités du modèle relationnel. En réalité, vous avez beaucoup plus de chances de stocker un document. Très souvent, dans les cas que nous avons observés, les équipes stockent littéralement des versions électroniques de documents papier, qu’il s’agisse d’enquêtes de satisfaction de la clientèle, de fiches de problèmes, etc. Dans ce cas, une base de données documentaire comme Cloudant ou MongoDB est probablement la solution la plus adaptée à vos besoins. Découper votre code en services distincts qui fonctionnent sur ce type de base de données se traduira par un code beaucoup plus simple et sera souvent plus facile à maintenir que d’essayer de faire la même chose au sein d’une grande base de données d’entreprise.

Manipulation des données de référence

Un autre modèle courant que nous avons vu dans les systèmes ORM est la combinaison de « données de référence dans une table aspirées dans un cache en mémoire ». Les données de référence sont des éléments qui ne sont pas souvent (ou jamais) mis à jour, mais qui sont constamment lus. Un bon exemple est la liste des États américains ou des provinces canadiennes ; d’autres exemples incluent les codes médicaux ou les listes de pièces standard. Ce type de données est souvent utilisé pour remplir les menus déroulants dans les interfaces graphiques.

Le modèle courant consiste à commencer par lire la liste à partir d’une table (généralement une table à deux ou, au maximum, trois colonnes) chaque fois qu’elle est utilisée. Cependant, vous constaterez qu’effectuer cette opération à chaque fois est trop coûteux en performance. L’application charge donc ces données dans un cache en mémoire tel qu’EhCache lors de son initialisation.

Chaque fois que ce problème survient, il est grand temps de le restructurer en un mécanisme de mise en cache plus simple et plus rapide. Là encore, Memcached ou Redis seraient parfaitement adaptés à cette situation. Si les données de référence sont indépendantes du reste de la structure de votre base de données (et qu’elles sont souvent ou sont, tout au plus, faiblement couplées), il peut être utile de séparer les données et leurs services du reste de votre système.

La requête de l’enfer

Dans un système client sur lequel nous travaillions, nous faisions une modélisation financière complexe qui nécessitait des requêtes très complexes (de l’ordre de six ou sept jointures) juste pour créer les objets que le programme manipulait. Les mises à jour étaient encore plus compliquées, car nous devions combiner plusieurs niveaux de vérifications de verrouillage optimistes simplement pour savoir ce qui avait changé et si ce qui se trouvait dans la base de données correspondait toujours à la structure que nous avions créée et manipulée.

Avec le recul, il est clair que ce que nous faisions aurait été plus naturellement modélisé sous forme de graphique. De telles situations (dans ce cas, nous modélisions des tranches de fonds, chacune composée de différents types d’actions et de titres de créance, chacune étant cotée dans des devises différentes et arrivant à échéance à des moments différents, avec des règles de valorisation différentes) demandent presque une structure de données qui vous permettra de faire facilement ce que vous voulez vraiment faire : parcourir le graphique de haut en bas et déplacer des parties du graphique à volonté.

C’est là qu’une solution comme Apache Tinkerpop ou Neo4J serait une bonne approche. En modélisant la solution directement sous forme de graphique, nous aurions pu éviter de nombreux codes Java et SQL compliqués, tout en améliorant probablement nos performances d’exécution de manière significative.

Retour vers le futur ; ou quand le SQL traditionnel ou le NewSQL brille

Bien qu’il existe de nombreux cas où les bases de données NoSQL sont la bonne approche logique pour certaines structures de données, il est souvent difficile de surpasser la flexibilité et la puissance du modèle relationnel. L’avantage des bases de données relationnelles, c’est que vous pouvez très efficacement « découper » les mêmes données sous différentes formes, à des fins différentes. Des astuces telles que les vues de base de données vous permettent de créer plusieurs mappages des mêmes données, ce qui est souvent utile lors de la mise en œuvre d’un ensemble de requêtes liées à un modèle de données complexe. 

Pour une analyse plus approfondie de la comparaison entre NoSQL et SQL, consultez « SQL et NoSQL : quelle est la différence ? ».

Comme nous l’avons évoqué dans un article précédent, si la « limite inférieure » d’un microservice est un ensemble d’entités regroupées dans un agrégat avec l’ensemble associé de services qui opèrent sur ces données, la mise en œuvre des vues pour représenter un ensemble de requêtes connexes est souvent plus simple et plus directe avec SQL.

Nous l’avons vu pour la première fois dans l’exemple du client qui a conduit à notre exemple simple de compte/élément dans l’article précédent. Dans ce cas, il s’agissait d’une banque qui s’efforçait de faire fonctionner un modèle simple comme celui-ci dans une base de données NoSQL orientée vers les documents, mais qui a été vaincue par le théorème CAP. Cependant, l’équipe avait choisi ce modèle de données pour de mauvaises raisons. Elle avait choisi d’utiliser la base de données orientée documents pour tous ses microservices par souci de cohérence architecturale.

Dans ce cas précis, elle avait besoin de tous les attributs du mode ACID, mais pas du partitionnement ; son système existant fonctionnait depuis des années avec un niveau de performance tout à fait acceptable sur un modèle relationnel, et elle ne prévoyait pas une croissance énorme. Mais si le cœur du système nécessitait des transactions ACID et n’avait pas besoin de partitionnement, ce n’était pas nécessairement le cas de toutes les parties du système. Les bases de données SQL excellent dans certains domaines, et les transactions ACID en font partie. Dans les systèmes de microservices, regrouper ces transactions ACID autour du plus petit ensemble de données sur lequel elles opèrent est généralement la bonne approche.

SQL ne signifie toutefois pas nécessairement les bases de données SQL traditionnelles. C’est possible, et il y a certainement une place pour cela dans de nombreuses architectures de microservices, mais SQL est également implémenté dans au moins deux autres types de bases de données qui peuvent être des choix utiles pour de nombreuses équipes mettant en œuvre des microservices. La première est le « petit SQL », qui est le domaine des bases de données open source comme MySQL et Postgres. Pour de nombreuses équipes mettant en œuvre des microservices, ces bases de données constituent un choix parfaitement raisonnable pour de nombreuses raisons :

  1. Les équipes qui restructurent des applications existantes en microservices ont généralement une expérience à la fois avec SQL et des cadres d’exigence de mappage objet-relationnel comme Hibernate ou Spring Persistence. Il est tout à fait raisonnable d’exploiter ces connaissances tout en restant dans les limites d’une approche de microservice.
  2. Ces bases de données bénéficient d’un très bon support, à la fois de la part de la communauté open source et de nombreux fournisseurs. Il est relativement facile de trouver de la documentation et des tutoriels pour ces produits et de trouver des développeurs compétents.
  3. Ces bases de données sont suffisamment petites et légères pour être facilement déployées et mises à jour via les mêmes mécanismes GitOps que vous utilisez pour le code  de vos applications.
  4. Ces bases de données sont prises en charge dans les versions SaaS de presque tous les clouds publics hyperscalaires.

Le principal inconvénient de ces « petites » bases de données SQL est qu’elles n’offrent généralement pas les mêmes capacités de mise à l’échelle (notamment pour le sharding) que celles des bases de données d’entreprise. Cependant, ce problème a été admirablement résolu par un nouveau groupe de fournisseurs de bases de données et de projets open source qui combinent les meilleurs attributs des petites bases de données SQL et l’évolutivité des bases de données NoSQL. Souvent appelées bases de données « NewSQL », elles comprennent CockroachDB, Apache Trofidion et Clustrix.

Lorsque vous avez besoin de toutes les transactions ACID, d’un moteur SQL qui prend entièrement en charge le modèle relationnel, ainsi que de vastes capacités de mise à l’échelle et de partage, les bases de données NewSQL peuvent constituer un bon choix pour les équipes. Ce choix a cependant un coût : ces bases de données sont souvent plus complexes à configurer et à gérer que les anciennes solutions de « petit SQL ». Il est également plus difficile de trouver des personnes ayant des compétences dans ces bases de données. Quoi qu’il en soit, il convient de les examiner attentivement lorsque vous étudiez les différentes options qui s’offrent à vous.

Que faire à partir de là ?

Nous avons fait un tour d’horizon des différents problèmes de modélisation des bases de données qui peuvent se faire passer pour des problèmes de codage. Si vous rencontrez un ou plusieurs de ces problèmes, il est préférable de vous éloigner de votre magasin de données d’entreprise existant et de le repenser en optant pour un autre type de magasin de données. Dans tous les cas, nous vous conseillons de procéder lentement et progressivement. N’oubliez pas que toutes les applications n’ont pas besoin de tous les types de modèles de données et que vous devez développer des compétences et comprendre les capacités opérationnelles des bases de données que vous avez choisies au fil du temps avant de commencer à les implémenter à grande échelle.

Enfin, soyez conscient que toute l’approche de microservices repose sur l’idée que les équipes qui construisent chaque microservice peuvent prendre leurs propres décisions concernant le magasin de données qui leur convient le mieux. Le plus gros problème que nous ayons rencontré (comme le laissent entendre nombre de ces articles) est d’essayer de mettre en place une approche unique de représentation et de stockage des données pour toutes les situations. 

J’ai vu de nombreuses fois des équipes devoir faire face aux réalités du théorème CAP dans des situations où elles n’auraient même pas dû utiliser une base de données NoSQL. De même, essayer de créer des rapports complexes à partir d’une base de données NoSQL est souvent un exercice frustrant. D’autre part, les situations que nous avons présentées montrent que le modèle relationnel n’est pas exhaustif. Les autres modèles ont aussi leur place. Le meilleur conseil que nous puissions donner est de veiller à ce que vos équipes disposent de l’autonomie nécessaire pour choisir le bon modèle pour chaque microservice.

En savoir plus

IBM Garage est conçu pour gagner en rapidité, travailler plus intelligemment et innover de manière à vous permettre d’éradiquer toute interruption.

Auteur

Kyle Brown

IBM Fellow, CTO

IBM CIO Office

Solutions connexes
IBM Instana Observability

Exploitez le pouvoir de l’IA et de l’automatisation pour résoudre de manière proactive les problèmes de la pile d’applications.

Découvrir IBM Instana Observability
Solutions DevOps

Utilisez les logiciels et outils DevOps pour construire, déployer et gérer des applications cloud natives sur plusieurs appareils et environnements.

Découvrir les solutions DevOps
Services de conseil en cloud

Renforcez l’agilité et la croissance de votre entreprise. Modernisez en continu vos applications sur n’importe quelle plateforme grâce à nos services de conseil cloud.

Découvrir les services de conseil cloud
Passer à l’étape suivante

De la détection proactive des incidents avec IBM Instana aux informations en temps réel sur l’ensemble de votre pile, garantissez la fiabilité de vos applications cloud-native.

  1. Découvrir IBM Instana
  2. Découvrir les solutions DevOps