Routage de transactions dans un cluster de bases de données ...

des transactions qui préserve l'autonomie des bases de données et des ... MOTS-CLES: Bases de données, architecture en cluster, traitement de transactions,.
275KB taille 2 téléchargements 142 vues
Routage de transactions dans un cluster de bases de données répliquées. Hubert Naacke1, François Dang Ngoc2, Patrick Valduriez3 1

LIP6 Université Paris 6, France [email protected] 2

PRISM Lab Université de Versailles, France [email protected] 3

Projet Atlas INRIA et IRIN, Nantes, France [email protected]

RESUME: Nous

considérons l'utilisation d'un cluster (i.e. une grappe d'ordinateurs) pour gérer des bases de données autonomes recevant des mises à jour intensives. Pour optimiser l'équilibrage de charge, nous utilisons la réplication optimiste des bases de données dont nous contrôlons la fraîcheur. Nous proposons une solution pour le routage (i.e. cheminement) des transactions qui préserve l'autonomie des bases de données et des applications, ainsi qu'un modèle de coût pour estimer la fraîcheur des répliques. Nous proposons ensuite un algorithme pour le routage des transactions qui tient compte des exigences de fraîcheur des transactions et de la fraîcheur des répliques. Nous avons mis en œuvre notre solution sur un cluster de serveurs Oracle8i sous Linux, et nous avons réalisé de nombreuses mesures de performances en utilisant TPC-C le banc de test d'applications transactionnelles. Nos résultats montrent que notre solution surpasse les solutions existantes pour les charges transactionnelles typiques. MOTS-CLES: Bases de données, architecture en cluster, traitement de transactions, équilibrage de charge, réplication, cohérence.

2

1. Introduction Nous considérons l'utilisation d'un cluster (i.e. une grappe d'ordinateurs sans partage de mémoire ni de disque) pour gérer des bases de données autonomes recevant des mises à jour intensives, et hébergées chez un fournisseur de services applicatifs (ASP pour Application Service Provider) [4]. Le défi pour le fournisseur est d'exploiter pleinement les possibilités de parallélisme et d'équilibrages offertes par le cluster, pour obtenir un meilleur rapport performance/prix. La solution typique pour bien équilibrer la charge consiste à répliquer les données sur plusieurs nœuds pour que les demandes des utilisateurs puissent être traitées par un nœud au choix selon la charge courante. Cette solution a été utilisée avec succès pour les sites Web tels que les moteurs de recherche, hébergés sur un grand nombre de serveurs juxtaposés en parallèle (ex. Google). Cependant, les sites Web sont des applications qui accèdent aux données principalement pour des requêtes de lecture intensive sans mise à jour. Dans ce cas particulier, il est possible de traiter chaque nouvelle requête sur le nœud suivant, à tour de rôle. Par contre, dans le contexte ASP, le routage est plus difficile car d'une part les applications effectuent des mises à jour intensives, et d'autre part, les applications et les bases de données doivent conserver leur autonomie car elles peuvent évoluer selon les besoins des clients. Ce travail est fait dans le contexte du projet Leg@Net1 dont l'objectif est de démontrer la viabilité du modèle ASP pour des applications pharmaceutiques en France. Notre solution exploite la réplication optimiste des bases de données pour effectuer le routage des transactions. Avec la réplication optimiste [7], les transactions sont validées localement, ainsi les répliques peuvent avoir des valeurs différentes. La divergence des répliques demeure jusqu'à la réconciliation. Entre temps, la divergence doit être contrôlée pour au moins deux raisons. Premièrement, puisque la synchronisation consiste à produire une séquence de traitements unique à partir de plusieurs séquences divergentes, plus la divergence est importante, plus la réconciliation est difficile. La deuxième raison est que les applications n'ont pas toujours nécessairement besoin de lire des données parfaitement cohérentes, et peuvent tolérer des incohérences. Dans ce cas, l'incohérence reflète la divergence entre la valeur lue effectivement et la valeur qui aurait due être lue en mode ACID. Les requêtes non isolées sont aussi utiles dans les environnements non répliqués[3]. La spécification de l'incohérence pour les requêtes a été largement étudiée dans la littérature. L'incohérence peut avoir une dimension temporelle et spatiale [10] comme, par exemple, la dimension temporelle de l'incohérence des quasi-copies [1], pour lesquelles une copie cachée (image) peut être accédée en lecture selon certaine condition temporelle telle que la durée maximale autorisée entre la dernière mise à jour de la copie et la dernière mise à jour de la donnée de référence. Un exemple de dimension spatiale est la "quantité de modifications" autorisée entre la valeur lue et la valeur de référence stockée effectivement au même moment. Cette quantité de 1

Projet RNTL entre le LIP6, Prologue Software et ASPLine.

3

modification, appelée limite d'importation dans les transactions epsilon [12], peut être par exemple le nombre d’articles modifiés, le nombre de mises à jour réalisées ou la valeur absolue de la mise à jour. Dans le modèle de cohérence continue[13], les dimensions temporelles et spatiales sont toutes les deux contrôlées. Chaque nœud propage ses écritures vers les autres nœuds par un accès en force (push) ou à la demande (pull), ainsi chaque nœud maintient un niveau de cohérence prédéfini pour chaque dimension. Puis chaque requête peut être dirigée vers un nœud qui a un niveau de cohérence satisfaisant (par rapport à la requête) dans le but d’optimiser l’équilibrage de charge. Dans cet article, nous nous efforçons d'exploiter les travaux concernant le relâchement de la cohérence des données pour améliorer les performances, pour les appliquer dans le contexte des clusters de bases de données. Nos contributions sont les suivantes : •

Une architecture de routage, pour les systèmes en clusters, qui préserve l’autonomie des bases de données et des applications grâce à des techniques non intrusives qui fonctionnent indépendamment de tout SGBD.



Un modèle pour estimer la fraîcheur des répliques, assez générique pour estimer la fraîcheur des bases de données modifiées par des applications autonomes, sans la nécessité de connaître le code source détaillé des transactions, toutefois suffisamment précis pour améliorer le routage des transactions.



Un algorithme pour le routage des transactions qui prend en compte la fraîcheur requise des transactions et la fraîcheur courante des répliques pour améliorer l’équilibrage de charge.



La validation de la solution proposée, au moyen de TPC-C [11] le banc de test d’applications transactionnelles, pour mesurer le gain en performance de notre approche, par rapport aux solutions existantes.

Cet article est organisé comme suit. Nous définissons, section 2, les concepts principaux que sont la fraîcheur d’une réplique, la politique d’une transaction et le plan d’exécution d’une transaction. Nous présentons l’architecture pour le routage en section 3, puis nos algorithmes pour le routage efficace des transactions en section 4. Puis, les résultats expérimentaux de la section 5 valident l’efficacité de nos algorithmes de routage. Finalement, nous concluons en section 6. 2. Concepts principaux Notre objectif général est de minimiser l’équilibrage de charge au moyen de la réplication sans compromettre l’autonomie des bases de données [5]. Par conséquent, nous pouvons seulement répliquer la base de données toute entière sur plusieurs nœuds du cluster. Autrement dit, nous ne pouvons pas répliquer un sous

4

ensemble de la base de données. Avec la réplication optimiste, plusieurs répliques (sur différents nœuds du cluster) peuvent avoir des états différents à un instant donné, car elles n’ont pas encore atteint l’état complètement cohérent, c'est-à-dire l’état obtenu après l’exécution correcte de toutes les transactions reçues. De plus, les transactions à traiter sur le cluster peuvent avoir des exigences de cohérence différentes. Afin de pouvoir router les transactions en contrôlant la précision, nous devons définir la précision d’une réplique de la base de données. Puis, à partir de cette définition, nous pouvons définir les concepts de politique de transaction et de plan d’exécution produit par le module d’équilibrage des transactions. 2.1. Fraîcheur d’une réplique Intuitivement, la fraîcheur d’une réplique correspond à la quantité de changements (effectués sur les autres répliques de la même base de données) qui n’ont pas encore été appliqués à la réplique [6]. Donc, si la quantité de changements vaut zéro, la réplique aura la fraîcheur maximale, c'est-à-dire que son état est complètement cohérent. Cette quantité de changements est appelée limite d'importation dans les transactions epsilon [12]. Un changement consiste à mettre à jour une relation. Par conséquent, nous définissons la quantité de changements au niveau d’une relation. Soit R une relation modifiée par T, nous appelons Change(T, R) le nombre maximum de n-uplets modifiés par T. La fraîcheur d’une réplique R est représentée par la quantité de changements effectuée sur toutes les autres répliques sauf R. Soit TR l’ensemble des transactions qui ont modifié ces autres répliques, la fraîcheur F de R peut donc être définie comme étant la somme des changements effectués par TR :

F(R) = ∑i (Change(Ti,R) |Ti ∈ TR) Ainsi, nous définissons la fraîcheur F d’un nœud N, possédant la réplique d’une base de données avec n relations, par :

F(N) = { (Ri,F(Ri)) |i=1,n} 2.2. Politique d’une transaction Le routeur utilise la politique d’une transaction pour contrôler l’exécution d’une transaction avec un niveau de fraîcheur requis. Afin de préserver l’autonomie d’une base de données, nous définissons la politique d’une transaction basée seulement sur les transactions et les relations. Nous ne pouvons pas utiliser un niveau plus fin de granularité car les bases de données et les applications ne divulguent pas au routeur le code source détaillé des transactions. La politique d’une transaction décrit les exigences d’une transaction et ses effets sur la base de données. La politique d’une transaction décrit tout d’abord l’état requis du cluster avant d’exécuter une transaction. Nous définissons l’état du cluster pour une transaction T en fonction de

5

la fraîcheur des données du cluster, et de l’exécution des transactions conflictuelles avec T. L’exigence de fraîcheur est la fraîcheur minimale de toutes les relations accédées par la transaction. Soit l’ensemble des relations accédées par T, nous définissons la fraîcheur F de T par :

F(T) = {(Ri, F(Ri)) | Ri ∈ RT} L’exigence concernant les transactions en conflit avec T a pour but d’empêcher l’occurrence de conflits insolubles. Soit Precede(T) l’ensemble des transactions qui doivent précéder la transaction, on a :

Precede(T) ={Ti | (Ti, T) non commutatives} La politique d’une transaction décrit aussi les changements effectués par la transaction pour toutes les relations modifiées par T. Soit l’ensemble des relations modifiées par T, nous définissons les changements de T par :

Change(T) = {(Ri , Change(T, Ri)) | Ri ∈ R'T} Ainsi, nous définissons la politique d’une transaction (TP) par :

TP(T) = (F(T), Precede(T), Change(T)) 2.3. Plan d’exécution d’une transaction Un plan d’exécution d’une transaction décrit comment le routeur exécute une transaction T dans un cluster. Le plan décrit la réplique N où T sera exécutée ainsi que les traitements prérequis nécessaires pour atteindre l’état du cluster requis par T. Soit Sync la séquence de toutes les transactions devant précéder T et celles qui rendront la réplique suffisamment fraîche, nous définissons le plan d’exécution, d’une transaction (TEP) par :

TEP(T) = (N, Sync(T)) 2.4. Exemple Pour illustrer comment le routage des transactions en contrôlant la fraîcheur améliore l’équilibrage de charge, nous considérons un exemple issu du benchmark TPC-C. Nous nous focalisons sur la relation Stock(item, quantity, threshold). La transaction D décrémente la quantité d’article id par q : procedure D(id, q): update Stock set quantity = quantity – q where item = id; Nous supposons maintenant la requête Q donnant la liste des articles à renouveler : select item from Stock where quantity < threshold Nous mesurons les changements effectués par une transaction ainsi que la fraîcheur comme étant le nombre de n-uplets modifiés. D modifie un seul n-uplet et

6

requiert une fraîcheur de 2 (c'est-à-dire que D peut être exécutée sur une réplique qui n’a pas encore reçu les 2 dernières transactions D exécutées sur les autres répliques), et Q n’a aucune exigence de fraîcheur. Soient une application de vente de produits exécute une séquence de transactions D, et une application de réapprovisionnement exécute une séquence de requête Q. La charge est composée de deux applications de vente et deux applications de réapprovisionnement s’exécutant en concurrence. La relation Stock est répliquée sur les nœuds N1 et N2. La figure 1 illustre les transactions à exécuter. Transactions entrantes D1

D2

Q1

Q2

… D12 D11

… D22 D21

… Q12 Q11

… Q22 Q21

Routeur SGBD N1 routage 1 routage 2

SGBD N2

D11, D21, Q22,…

Q11, Q21, D11, D12,…

D11, D21, D12, D22

Q12, Q21, Q12, Q22

Figure 1: Exemple de routage Supposons que N1 et N2 peuvent traiter une requête en même temps, nous montrons le scénario de deux stratégies de routage. La première stratégie consiste à choisir le nœud le moins chargé pour exécuter chaque nouvelle transaction. Donc, en moyenne, N1 et N2 traitent autant de transactions D et Q. Par exemple, au départ, D11 est exécutée sur N1, puis Q11 sur N2, puis D21 sur N1 concurremment avec D11 en cours d’exécution, puis Q21 sur N2 concurremment avec Q11 en cours d’exécution. A cause de l’exigence de fraîcheur de D, lorsque D12 arrive, si le nœud le moins chargé est N2, le routeur commence par propager D11 sur N2 avant de traiter D12 afin de rendre N2 suffisamment frais. Après un certain temps, les deux nœuds seront complètement frais au surcoût de traiter toutes les ventes D sur tous les deux nœuds. Cette stratégie produit des temps de réponse élevés à cause de la surcharge pour traiter toutes les ventes deux fois Pour éviter cette surcharge, la deuxième stratégie consiste à choisir le nœud dont la fraîcheur est aussi proche que possible des exigences de la transaction. Donc, lorsque D12 arrive, le routeur choisit N1 car il est suffisamment frais pour traiter D12, tandis que le traitement sur N2 aurait nécessité de propager D11. Par conséquent, après un certain temps, N1 se retrouve dédié aux ventes D et N2 aux réapprovisionnements Q. Donc, chaque vente est traitée une seule fois et le temps de réponse de D et Q est amélioré par rapport à la stratégie précédente. L’inconvénient secondaire est que N2 devient de moins en moins frais car il n’a reçu aucune

7

vente D. Cet exemple illustre l’importance de connaître le coût de rafraîchissement d’un nœud, afin d’effectuer le routage adéquat. 3. Architecture du routeur Dans cette section, nous présentons l’architecture pour exécuter sur le cluster les transactions que les applications demandent, et nous proposons une solution pour l’équilibrage de charge. Typiquement, une application est un programme qui se connecte à un SGBD pour y traiter une transaction ; l’application pouvant traiter plusieurs transactions au cours de son exécution. Du fait de son autonomie, l’application ne connaît pas les opportunités d’équilibrage de charge qu’offre le cluster. C’est pourquoi nous concevons un routeur chargé de l’équilibrage de charge. Les étapes du traitement général d’une transaction sont les suivantes : Premièrement, l’application s’exécute sur un nœud du cluster. Lorsque l’application appelle un SGBD, l’appel est intercepté pour être redirigé vers le routeur. Le routeur choisit un SGBD sur un nœud pour y exécuter la transaction, puis renvoie finalement le résultat de la transaction à l’application. Ainsi, le routeur s’intègre de manière transparente avec les applications existantes et leurs bases de données, pour bénéficier du parallélisme et de la capacité de traitement en parallèle du cluster. L’architecture du routeur s’intègre avec celle du cluster, en préservant l’autonomie des applications et des bases de données [5]. Pour préserver l’autonomie des bases de données, le cluster repose sur une architecture, dite shared nothing, sans partage de mémoire ni de disque entre plusieurs nœuds. Donc chaque nœud accède séparément aux données stockées sur son disque. Le routeur est luimême réparti sur chaque nœud applicatif pour éviter les congestions dues à un point d’accès unique au routeur. Les instances du routeur utilisent un gestionnaire de mémoire répartie pour partager leurs données communes [2]. Ainsi, nous avons conçu l’architecture de notre routeur pour répondre aux besoins suivants : •

Le routeur doit savoir quels sont les nœuds capables de traiter une transaction. Il doit empêcher les conflits insolubles entre des transactions concurrentes, il ne doit pas compromettre la cohérence des données.



Si plusieurs nœuds peuvent traiter une transaction, le routeur doit choisir celui qui fournit l’exécution la plus efficace.



A cause de la réplication, le routeur doit propager une transaction sur les autres répliques. Cette opération de synchronisation doit interférer le moins possible avec le traitement effectif des transactions, ceci afin de réduire la surcharge.

La figure 2 montre l’architecture du routeur avec les modules suivants :

8

app1

transactions

appn Routeur

Gest. des politiques de trans. TP Génération des plans d’exécution des trans. Modèle de coût Ordonnancement Synchronisation Stratégie d’optimisation TEP Contrôle de l’exécution

Rafraîchissement

S1

Légende: module fonction

Règles d’exécution

Etat du cluster

Sonde Sn

données statiques

app application autonome (nœud du cluster)

données dynamiques

Sn SGBD autonome (nœud du cluster)

Figure 2: Architecture du routeur 3.1 Gestionnaire des politiques de transaction Le gestionnaire produit une politique de transaction (TP) pour chaque transaction qu’envoie l’application. Son rôle est de déterminer les exigences initiales des transactions, à partir des règles d’exécution. Il calcule la fraîcheur minimale requise pour traiter T, sachant que la fraîcheur minimale permettra d’augmenter les possibilités d’accès parallèle aux répliques, avec pour conséquence l’amélioration globale des performances. Puis, le gestionnaire distingue parmi les transactions déjà exécutées mais non encore propagées vers toutes les autres répliques, celles qui doivent précéder T afin d’éviter les conflits insolubles. Finalement, le gestionnaire calcule l’effet de T sur la réplique (i.e., Change(T)) ; cette valeur est utilisée pour estimer la fraîcheur d’une réplique. 3.2 Module produisant les plans d’exécution Pour effectuer l’équilibrage de charge, le module produisant les plans d’exécution (TEP) choisit une réplique (i.e., un nœud) pour y traiter une politique de transaction (TP) avec le maximum d’efficacité. Il prends en considération plusieurs nœuds comme candidats potentiels. Lorsqu’un nœud candidat n’est pas suffisamment frais, le module produisant les TEP détermine l’ensemble des transactions à propager sur ce nœud afin d’atteindre les exigences de TP.

9

Pour choisir un nœud efficace, le module produisant les TEP a besoin d’informations sur l’état du cluster. Intuitivement, mieux nous connaissons l’état du cluster, mieux nous pouvons estimer le comportement du cluster, et meilleur sera le choix du nœud. C’est pourquoi, le module produisant les TEP repose sur un modèle de coût pour estimer la durée de traitement d’une transaction. Le modèle de coût est fonction de l’état courant du cluster composé de la charge courante et de la fraîcheur de tous les nœuds, et de la liste des transactions en cours d’exécution sur chaque nœud. Si plusieurs transactions arrivent simultanément (ou dans un court intervalle de temps), l’objectif d’optimisation est de minimiser le temps de réponse d’un ensemble de transactions. Dans ce cas, nous réordonnons l’ensemble des transactions entrantes afin de déterminer un ordre efficace. Le nombre de transactions à réordonner dépend du temps d’inter-arrivée des transactions. Un cas particulier est de traiter individuellement chaque transaction entrante, ainsi l’objectif est de réduire le temps de réponse de chaque transaction. Nous nous retrouvons dans ce cas particulier dès lors que le temps d’attendre plusieurs transactions avant de procéder au routage, est largement supérieur au temps de réponse des transactions. 3.3 Module de synchronisation Le module de synchronisation détermine le planning approprié pour propager les transactions vers les autres répliques, en surchargeant le moins possible le cluster. Pour éviter la synchronisation inutile, une solution consiste à propager les transactions le plus tard possible, seulement lorsque cela est nécessaire pour traiter une nouvelle transaction. Cependant, la synchronisation tardive n’est pas toujours optimale. Par exemple, pendant une période où les transactions entrantes ne requièrent aucune fraîcheur, la fraîcheur des nœuds a tendance à diminuer continuellement. A la fin de cette période, une nouvelle transaction arrive, exigeant une forte fraîcheur. Or tous les nœuds sont loin d’être suffisamment frais pour traiter directement la nouvelle transaction. Donc la quantité de synchronisation à exécuter est grande, ce qui ralenti d’autant plus la nouvelle transaction. Nous pouvons éviter ce problème en prévoyant l’arrivée d’une transaction exigeant une forte fraîcheur et en synchronisant les nœuds par anticipation. En conséquence, le module de synchronisation propage une transaction sur un nœud suivant deux conditions : (i) si la fraîcheur d’un nœud est en dessous d’une certaine limite ou (ii) si la charge d’un nœud est en dessous d’un certain seuil. Les valeurs de seuil qui déclenchent la synchronisation dépendent de la charge et des applications.

10

3.4 Exemple de routage Par exemple, l’application app1 envoie une transaction de vente D(q, id) pour diminuer de q le nombre de produits id. A cet instant, le gestionnaire des politiques de transaction produit une politique pour D conforme aux règles d’exécution. La règle d’exécution pour D énonce qu’une vente ne doit pas vider entièrement le stock (c'est-à-dire qu’une quantité minimale doit rester en stock). Donc, le gestionnaire construit une politique en terme d’exigence de fraîcheur, déclarant que toute réplique peut traiter D, même si la réplique n’a pas reçu toutes les ventes précédentes, à condition que la fraîcheur de la réplique soit supérieure à f. Le gestionnaire détermine la valeur de f, à partir de sa connaissance de l’application. Si le gestionnaire n’a pas suffisamment de détails au sujet de la vente D pour garantir que le stock ne soit pas vidé, alors il construit une politique plus restrictive imposant de traiter D et toutes les autres ventes potentiellement conflictuelles, sur la même réplique ; ainsi le gestionnaire demande au SGBD sous-jacent, de garantir la cohérence du stock, ce qui réduit les opportunités de traitement en parallèle. 4. Algorithmes pour router les transactions Le problème du routage des transactions est le suivant : étant donné l’état du cluster (charge des nœuds, transactions en cours d’exécution) et un ensemble de transactions entrantes, déterminer quels sont les nœuds, suffisamment frais, sur lesquels exécuter les transactions de manière optimale. Un nœud est candidat pour exécuter T dès qu’il a reçu toutes les transactions appartenant à l’ensemble des transactions devant précéder T, et que sa fraîcheur est supérieure à la fraîcheur requise par T. Dans cette section, nous proposons un algorithme pour générer les TEP candidats, ainsi qu’une fonction de coût pour choisir un TEP. De plus, nous proposons un algorithme pour trouver un nœud efficace tout en garantissant un temps de réponse inférieur à une limite fixée. 4.1 Algorithme pour générer les TEP candidats Nous décrivons brièvement l’algorithme pour générer un ensemble de TEP candidats pour chaque politique de transaction. L’algorithme recherche les candidats parmi les nœuds du cluster. En supposant que le temps nécessaire pour déterminer les candidats est faible par rapport à la durée des transactions, nous explorons exhaustivement tous les nœuds du cluster. Pour chaque nœud N, nous vérifions tout d’abord si N a déjà reçu toutes les transactions devant précéder la transation. Soit Tprec l’ensemble des transactions de Precede(T) à exécuter sur N. Si Tprec n’est pas vide, nous ajustons la fraîcheur du nœud pour refléter l’état après avoir traité Tprec. Puis, nous vérifions que N est suffisamment frais. Soit Tsync l’ensemble minimal des transactions à exécuter sur N tel que F(N) atteigne F(T).

11

Pour déterminer Tsync, nous devons auparavant calculer Ns, l’état du nœud après la synchronisation. Premièrement, initialiser Ns avec l’état de N. Puis, itérer sur l’ensemble des exigences de fraîcheur. Pour chaque exigence, (1) rechercher la fraîcheur f de Ns qui n’atteint pas l’exigence, (2) tant que la fraîcheur de Ns demeure inférieure à l’exigence, (2.1) trouver une transactions ts à synchroniser pour améliorer f, (2.2) ajouter ts à Tsync, et mettre à jour la fraîcheur de Ns pour refléter la propagation de ts sur Ns. Tsync est ensuite ajoutée à Tprec pour obtenir le plan candidat : TEP(T) = (N, Tprec union Tsync). Après avoir généré tous les TEP, nous utilisons une fonction de coût pour en choisir un. 4.2 Fonction de coût Soit P l’ensemble des TEP candidats et la fonction de coût cost1(p) pour estimer le temps de réponse d’un TEP p, nous choisissons N qui minimise le coût d’exécuter p et le coût de synchroniser N. Donc, nous choisissons N tel que : cost1(p) = min({cost1(p) | p ∈ P}), avec p= TEP(T) = (N, Sync(T)), et cost1(p) = cost1(T, N) + ∑i {cost1(si, N) | si ∈ Sync(T)} Nous définissons maintenant la fonction cost1(T, N). Dans notre contexte, à cause de l’autonomie des applications, nous ne connaissons pas le détail des opérations élémentaires de lecture et d’écriture d’une transaction. Par conséquent, nous supposons que la consommation des ressources (processeur et E/S) est répartie uniformément sur toute la durée de traitement d’une transaction. Pour tenir compte des traitements concurrents, nous définissons une fonction de coût cost1(T,N) basée sur la charge du nœud et le temps écoulé des transactions en cours d’exécution sur N. Soit load(N) la charge du SGBD du nœud N (load >1). La charge repose sur une fonction du système d’exploitation dont la valeur augmente avec le nombre de transactions concurrentes. Soit avg_time(T, N) le temps d’exécution moyen de T sur N (avg_time dépend de l’exécution précédente et est normalisée pour une charge valant 1), et ∆t le temps écoulé par T sur N, rt(T, N) le temps restant pour T sur N est défini par: rt (T , N ) = max(1, avg_time (T ) −

∆t ) load ( N )

Nous fixons une borne inférieure pour rt (rt>1) signifiant qu’une transaction en cours d’exécution n’est pas encore terminée. Ceci évite l’occurrence de valeurs de rt sans signification au cas où le temps le temps estimé moyen est inférieur au temps de réponse. Puis, étant donné RTN l’ensemble des transactions en cours sur N, nous définissons cost1(T,N) par : cost 1(T , N ) = load ( N ) * (avg_time(T , N ) +

∑ min(avg_time(T , N ), rt (i, N )))

i∈RTN

12

L’intervalle de temps, pendant lequelle T s’exécute en concurrence avec une autre transaction de RTN,, est le minimum entre le temps moyen de T et le temps restant des transactions en concurrence. Pour valider la définition de la fonction de coût cost1, nous la comparons avec la fonction cost2 qui demande moins d’information sur l’exécution en cours. La fonction cost2 dépend seulement de la charge du nœud et du nombre de transactions actives sur le nœud, sans nécessiter plus de détails sur la durée des traitements en concurrence. Par conséquent, étant donné c le nombre de transactions en cours d’exécution sur le nœud N, et s le nombre de transactions à propager sur N (s = card(Sync(T))), nous définissons cost2(p) par : cost2(p) = load(N) * (c+s+1) / c Une autre stratégie fréquemment utilisée pour l’équilibrage est le tourniquet (round robin). Toutefois, le round robin est efficace seulement si la charge à équilibrer est uniforme. De plus, le round robin considère chaque nœud sans distinction pour exécuter les transactions pouvant exiger une certaine fraîcheur. Donc, le routeur propage les transactions sur chaque nœud à tour de rôle et manque l’opportunité de minimiser la charge due à la synchronisation. L’efficacité d’une stratégie de routage quantifie l’aptitude à choisir le nœud optimal, c'est-à-dire celui qui minimise le temps de réponse. Le choix d’un nœud dépend de la précision de la fonction de coût utilisée. Soient N1 et N2 les nœuds choisis avec les fonctions cost1 et cost2 respectivement, l’efficacité relative de cost1 par rapport à cost2 est : Eff = time(T, N2) / time(T, N1) 4.3 Routage des transactions de synchronisation Lorsque le routeur reçoit une transaction T, il choisit un nœud. Puis, si le nœud nécessite une synchronisation, le routeur traite d’abord la synchronisation requise sur le nœud avant de traiter T. Donc, le temps de synchronisation peut augmenter fortement le temps de réponse totale de la transaction. Pour éviter cela, le module de synchronisation propage les transactions par anticipation afin de préparer certains nœuds en vue des exécutions à venir. Pour limiter la surcharge, la synchronisation s’effectue seulement sur les nœuds libres (sans transaction en cours). Pour cela, le module de synchronisation partage avec le routeur la liste des transactions à synchroniser sur chaque nœud. Nous avons conçu le routeur dans le contexte d’applications transactionnelles (de type OLTP) autonomes. Le besoin principal pour de telles applications est de s’assurer que le temps de réponse ne dépasse jamais une limite acceptable ; c'est-àdire d’éviter le blocage, même temporaire, d’une transaction. Le blocage se produit lorsque de nombreuses transactions viennent simultanément surcharger le cluster. Pour des applications OLTP classiques, la charge transactionnelle est l’ensemble

13

des terminaux qui envoient une séquence de transactions. Un terminal attend pendant une durée think time entre l’envoi de deux transactions consécutives. Puisque le think time est réparti aléatoirement (dans un certain intervalle), lorsque n terminaux travaillent en concurrence, le nombre de transactions reçues simultanément par le routeur varie aléatoirement de 1 à n. En conséquence, lorsque n transactions arrivent au même instant, le routeur doit leur allouer suffisamment de ressource (i.e., de nœuds) pour assurer que le temps de réponse reste inférieur à une certaine limite. Le temps de réponse maximum dépend de l’application. Par exemple, une transaction de vente faisant partie d’un dialogue interactif avec un utilisateur, exige un temps de réponse «convivial» inférieur à 1 seconde. Par la suite, nous considérons que le temps de réponse exigé ne peut pas être inférieur à la durée d’exécution d’une transaction seule sur un nœud du cluster, lorsque toutes les ressources du nœud sont disponibles pour traiter la transaction. En plus de la charge transactionnelle, la charge des requêtes est un ensemble de requêtes de lecture qui nécessitent beaucoup de ressources. Notre objectif principal est tout d’abord de s’assurer que les requêtes ne dégradent pas le débit transactionnel. Ensuite de manière secondaire, nous tentons de réduire le temps de réponse des requêtes. Par conséquent, les objectifs d’optimisation se résument ainsi : garantir que le temps de réponse des transactions reste inférieur à un certain seuil, puis minimiser le temps de réponse des requêtes sans pénaliser les transactions en cours d’exécution. Nous supposons que nous pouvons traiter une transaction sur un nœud qui n’a pas encore reçu toutes les transactions. Cela implique que toutes les transactions n’exigent pas une fraîcheur parfaite. Donc, la plupart des transactions requièrent de la fraîcheur seulement pour les données modifiées par un sous ensemble des transactions précédentes (l’application n’impose pas un unique ordre global pour toutes les transactions, mais ce contente d’un ordre partiel). Donc notre solution utilise plusieurs nœuds, si nécessaire, pour traiter les transactions. Notre algorithme, orienté pour les applications OLTP, distingue les nœuds consacrés au traitement des transactions, des nœuds consacrés au traitement des requêtes. Parce que le nombre de transactions reçues varie dans le temps, l’algorithme affecte dynamiquement un nœud au traitement soit des transactions, soit des requêtes. Pour garantir l’usage optimal des nœuds du cluster, notre algorithme affecte le nombre minimal de nœuds pour traiter les transactions, les nœuds restants servent au traitement des requêtes. Pour faire face à l’arrivée brutale de plusieurs transactions, le routeur maintient toujours un nœud aussi frais que possible, prêt à suppléer au traitement des transactions lorsque les nœuds alloués ne peuvent plus garantir des temps de réponse convenables. L’algorithme détermine la plus petite liste ordonnée des nœuds consacrés aux transactions. L’algorithme s’intègre avec les algorithmes décrits plus haut pour la génération des plans d’exécution, l’estimation du coût et la synchronisation. Soient N l’ensemble de tous les nœuds du ni cluster, NT l’ensemble des nœuds consacrés aux transactions, NQ l’ensemble des nœuds consacrés aux requêtes (N = NQ ∪ NT), et Tmax le temps de réponse maximum toléré pour une transaction. L’algorithme se déroule de la manière suivante. Tout d’abord affecter un seul nœud au traitement

14

des transactions (NT ← n1), puis pour chaque demande T reçue : si T est une requête (i.e., , Change(T) = ∅), énumérer les plans candidats parmi NQ, puis choisir le nœud n qui minimise cost1, sinon (i.e., T est une transaction), itérer sur les nœuds n de NT pour déterminer le TEP pour T sur n et calculer son coût avec la fonction cost1, jusqu’à ce que le coût soit inférieur à Tmax. Puis T est exécuté sur n. Nous garantissons que NT a exactement un seul nœud libre en réaffectant le nœud le moins chargé de NQ à NT (après l’avoir synchronisé) ou en supprimant les nœuds libres en trop de NT pour les réaffecter à NQ. 4.4 Discussion Nous comparons brièvement l’algorithme propose en section 4.3 avec les autres solutions existantes. Une solution naïve consiste à allouer seulement un seul nœud pour le traitement des transactions. Dans ce cas, le temps de réponse des transactions augmente avec le nombre de transactions concurrentes. Cette solution ne passe pas à l’échelle car le seul moyen de garantir un seuil maximal pour le temps de réponse est de limiter le nombre de terminaux qui s’exécutent en concurrence. Inversement, une autre solution consiste à réserver tous les nœuds, sauf un, pour traiter les transactions ; ce qui offre le meilleur temps de réponse lorsque le nombre de transactions concurrentes est égal au nombre de nœuds réservés. Mais, pour un plus petit nombre de transactions, de nombreux nœuds sont inutilisés au lieu de servir à alléger la charge des requêtes concentrées sur un seul nœud. Une autre solution est de traiter à la fois des transactions et des requêtes sur tous les nœuds. Bien que cela minimise le temps de réponse moyen des transactions, cette solution ne garantie aucune limite maximale pour le temps de réponse des transactions, ni ne favorise pas les transactions sur les requêtes lorsque cela est nécessaire. Il est possible d’améliorer cette transaction en ajoutant une file avec priorité sur chaque nœud, et en affectant une priorité plus haute aux transactions qu’aux requêtes. Mais cela n’empêche pas les situations où tous les nœuds sont utilisés pour le traitement des requêtes ; dans ce cas aucun nœud ne pourra traiter la prochaine transaction dans le temps souhaité. Nous pourrions pallier cet inconvénient en abandonnant les requêtes en cours sur le nœud le moins chargé afin de le rendre disponible pour la nouvelle transaction ; mais nous considérons que la pénalité d’arrêter puis de re-exécuter une requête est trop élevée dans notre contexte de requêtes longues (i.e., requêtes d'analyse de données). 5. Validation des performances Dans cette section, nous validons notre approche à travers l'expérimentation. Nous décrivons tout d'abord l'implémentation du routeur et l'environnement expérimental pour le routage des transactions. Puis, à partir des expériences, nous comparons les performances de notre stratégie de routage basée sur le coût avec les

15

autres stratégies de routage. Nous montrons que pour des transactions avec une faible exigence de fraîcheur, le routeur améliore le temps de réponse. Nous montrons également, pour des requêtes longues, que le routeur économise des opérations de synchronisation tout en garantissant un temps de réponse efficace pour les transactions. 5.1. Environnement expérimental Nous avons implanté tous les modules du routeur en Java. Le routeur a le rôle de serveur JDBC pour l'application, préservant ainsi l'autonomie de l'application via l'interface standard JDBC. La communication, entre les processus de l'application et du routeur, utilise le standard RMI. Le cluster a 5 nœuds (Pentium IV à 2GHz avec 512Mo de mémoire) connecté par un réseau à 1Go/s. Un nœud sert pour router les transactions et contrôler la fraîcheur et chacun des autres nœuds héberge une répliques de la base de données. Le SGBD Oracle 8i sous Linux est installé sur tous les nœuds. Pour réduire la congestion, le routeur utilise le multi-threading de Java qui utilise les threads natifs du système Linux. Pour chaque transaction reçue, le routeur délègue, à un thread distinct, la génération du plan d'exécution et son exécution. Le routeur se connecte aux SGBD pour y exécuter les transactions, par le biais des pilotes JDBC fournis par Oracle (ou tout autre éditeur de SGBD). Pour réduire la latence pendant l'exécution des transactions, le routeur maintient un groupe de connexions pré-établies pour tous les nœuds du cluster. Nous avons implanté la fonction load appelée par la fonction d'estimation du coût, au moyen d'une fonction du noyau Linux qui renvoie la charge moyenne du système pendant la dernière minute glissante. Nous n'utilisons pas la valeur instantanée de la charge CPU car elle diffère trop de la charge moyenne pendant la durée de traitement d'une requête (quelques secondes dans notre contexte). Pour mesurer la performance du routeur pour des applications de type OLTP, nous avons mis en œuvre le benchmark TPC-C [11]. La base de données TPC-C (2 GO de données dans 9 relations, les relations Stock et OrderLine ont respectivement 1 million et 3 millions de n-uplets) est répliquée sur chaque nœud SGBD. Les transactions TPC-C sont implantées par des procédures stockées. Dans les expériences suivantes, nous considérons la transaction de mise à jour new-order, la transaction stock-level et la requête order-status. Nous avons développé un générateur de charge transactionnelle qui démarre plusieurs terminaux applicatifs simultanément. Un terminal envoie des transactions avec un débit constant ou variable selon les besoins de l'expérience. Un terminal envoie des transactions de type guichet (new-order) ou de type gestion d'arrière plan (stock-level, orderstatus). Dans les expériences suivantes, nous comparons les temps de réponse mesurés avec un temps de réponse de référence qui est le temps nécessaire pour exécuter une transaction sur un nœud dont toutes les ressources sont disponibles pour traiter la transaction.

16

5.2. Comparaison avec les autres stratégies de routage Nous comparons l'algorithme de routage basé sur le coût utilisant la fonction cost1, avec deux autres stratégies plus simples : le round-robin (RB) et la stratégie basée sur la charge CPU utilisant la fonction cost2. Le but est de montrer que la stratégie utilisant cost1 surpasse les autres stratégies lorsque la charge est suffisamment irrégulière pour refléter des applications réelles. Nous nous concentrons sur des irrégularités réalistes: des temps d'exécution et une consommation CPU biaisés. Ainsi, pour observer le comportement du routeur, nous définissons deux charges W1 et W2. W1 est un mélange de requêtes courtes et longues consommant le CPU uniformément. W2 est un mélange de transactions et de requêtes dont la consommation CPU diffère mais qui ont le même temps de réponse de référence.

RB

80000

CPU

60000

EST

40000 20000 0 2

3

4

Nombre de noeuds

Figure 3a: Temps d'exécution sous la charge W1

temps d'exec. (ms)

temps d'exéc. (ms)

Soit n le nombre de nœuds SGBD. Pour W1, le générateur envoie une séquence de requêtes courtes, et une requête longue toutes les n requêtes. Pour s'assurer que les deux types de requêtes consomment uniformément la ressource CPU, nous avons implanté les requêtes par une procédure itérative effectuant c appels à order-status (avec c=5 pour la requête courte et c=15 pour la longue). Le temps de réponse de référence vaut respectivement 2s et 7s pour une requête courte et une longue. La Figure 3a montre, pour des valeurs différentes de n, le temps d'exécution moyen des requêtes pour les stratégies round robin (RB), charge CPU (CPU), et estimation du coût avec cost1 (EST). Nous observons les temps de réponse de la stratégie roundrobin sont jusqu'à dix fois supérieurs à ceux des autres stratégies. Donc, la stratégie round-robin n'est pas adaptée pour des requêtes ayant des temps de réponse différents. Nous observons que le désavantage de round- robin décroît lorsque le nombre de nœuds augmente, car l'irrégularité de W1 (une requête longue toutes les n requêtes) a moins d'influence (1/n) lorsque n augmente. Nous vérifions, sur la figure 3b, les performances médiocres de la stratégie round-robin pour les requêtes longues ; en effet, elles sont toujours traitées sur le même nœud au lieu d'être réparties sur plusieurs nœuds. 160000

requêtes courtes requêtes longues

120000 80000 40000 0 2

3

4

Nombre de noeuds

Figure 3b: Temps d'exécution des requêtes avec la stratégie RB

17

Pour chaque stratégie, nous avons également mesuré la variation de la charge CPU pendant toute la durée de l'expérience, voir les figures 4a, 4b et 4c. Une valeur de charge supérieure à 1 signifie que le nœud est surchargé. Plus la valeur est élevée, plus la surcharge est forte. 12 N1

N2

N3

8

2,5

N4

charge CPU

charge CPU

10

6 4 2

N1

N2

N3

N4

2 1,5 1 0,5 0

0 0

200

0

400

200

400

temps(s) temps (s)

charge CPU

Figure 4a: Charge CPU pour la stratégie round-robin

Figure 4b: Charge CPU pour la stratégie basée sur le CPU

1,4 1,2 1 0,8 0,6 0,4 0,2 0

N1 N2 N3 N4

0

100

200

300

400

temps(s)

Figure 4c: Charge CPU pour la stratégie EST basée sur l'estimation du coût La figure 4a montre que le nœud recevant les requêtes longues est saturé, ce qui provoque des temps de réponse très longs, tandis que les autres nœuds sont en souscharge. Les performances de la stratégie CPU (figure 4b) sont bonnes car toutes les requêtes ont la même consommation unitaire du CPU, cela signifie que le temps de réponse est principalement proportionnel au nombre de requêtes en cours d'exécution sur un nœud. Cependant, la stratégie CPU repose sur la charge moyenne de la dernière minute glissante, qui met un certain délai avant de prendre en compte une nouvelle requête. A un instant donné, soit N1 le nœud le moins chargé, le routeur va envoyer toutes les requêtes suivantes sur N1, tant que la charge moyenne ne reflète pas le changement de charge. Par conséquent, N1 va devenir surchargé avant que la charge moyenne ne reflète l'état de N1. La figure 4b illustre cet inconvénient : la charge moyenne mesurée avec la stratégie CPU vaut environ 1,4 signifiant une légère surcharge des nœuds. Par contre, nous constatons que la charge

18

moyenne mesurée avec la stratégie EST (figure 4c) vaut environ 0,7, soit aucune surcharge. Cela renforce l'intérêt de la stratégie EST par rapport à la stratégie CPU. Dans l'expérience suivante, nous mesurons le temps de réponse des transactions lorsque la charge est W2. Un terminal envoie alternativement trois types de transactions: new-order, order-status et stock-level. Il y a n terminaux (égal au nombre de nœuds) et chaque terminal a un débit constant de 15 transactions par minute. Donc, le débit de la charge W2 augmente de 15 à 60 lorsque le nombre de nœuds augmente de 1 à 4. Chaque transaction a une consommation CPU différente. Le temps d'exécution est montré sur la figure 5a. Nous observons un temps de réponse particulièrement élevé pour 3 nœuds avec la stratégie RB. En effet, dans ce cas, un nœud reçoit toujours le même type de transaction, ce qui provoque un ralentissement dû au contrôle de concurrence local. La figure 5b montre que orderstatus n'est pas ralentie par les autres transactions de type order-status car elles ne demandent aucun verrouillage. Cependant, les transactions new-order et stock-level posent des verrous ce qui provoque de l'attente car le SGBD sous-jacent sérialise l'exécution des transactions. Hormis ce cas, la stratégie CPU est moins efficace que RB, à cause du manque de réactivité de la fonction mesurant la charge CPU. Pour pallier cet inconvénient, la stratégie EST prends en compte (de manière plus générique que dans [8]) les transactions en cours d'exécution ainsi que la charge CPU, ce qui pallie au manque de réaction de la charge CPU. RB

80000

CPU

60000

Est

40000 20000 0 2 3 Nombre de noeuds

4

120000

New Order

80000

Order Status

40000

Stock Level

temps d'exec. (ms)

temps d'exec (ms)

100000

0 2 3 4 Nombre de noeuds

Figure 5a: Temps moyen d'exécution Figure 5b: Temps d'exécution détaillé pour la stratégie RB. des transactions pour la charge W2 5.3. Synchronisation différée Nous montrons maintenant, que le routeur améliore le temps de réponse des transactions qui ont une faible exigence de fraîcheur. Nous comparons deux stratégies de synchronisation: immédiate et différée. Pour la synchronisation immédiate, le routeur propage immédiatement la transaction sur toutes les répliques, ce qui apporte donc une fraîcheur élevée aux répliques. Pour la synchronisation différée, le routeur synchronise les répliques lorsque les nœuds consomment peu de ressources CPU.

19

Intuitivement, la synchronisation différée est bénéfique lorsque la charge reçue comprend principalement des transactions avec une faible exigence de fraîcheur. Pour illustrer cela, nous considérons une charge composée de transactions neworder et order-status. La politique de la transaction new-order déclare la mise à jour d'un n-uplet de la relation order-line, i.e., TP(new-order) = (∅, ∅, {(1, orderline)}), et la politique de la requête order-status tolère une fraîcheur maximale de f n-uplets, i.e., TP(order-status) = ((f, order-line), ∅, ∅). Le routeur envoie les requêtes order-status vers un nœud satisfaisant la condition de fraîcheur. Si aucun nœud n'est assez frais, alors l'exécution est reportée après la prochaine synchronisation. Dans notre mise en œuvre expérimentale, une transaction et sa synchronisation durent le même temps. Nous avons donc configuré le générateur de charge afin de laisser suffisamment de temps disponible pour la synchronisation. Le générateur de charge envoie alternativement pendant 30s, une requête order-status par seconde et une transaction new-order toutes les 3s, puis le générateur s'arrête temporairement pendant une minute, et ainsi de suite. La figure 6 montre le temps exécution de new-order et d'order-status en fonction de la fraîcheur f tolérée par order-status. Les courbes NO_def et OS_def représentent respectivement le temps de réponse de new-order et d'order-status avec la synchronisation différée. Les courbes NO_imm et OS_imm représentent respectivement le temps de réponse de new-order et d'order-status avec la synchronisation immédiate.

temps d'exec (ms)

35000 30000 25000

NO_def

20000

OS_def

15000

NO_imm

10000

OS_imm

5000 0 2

5

10

fraîcheur f

Figure 6: Temps de réponse avec la synchronisation immédiate et différée. Avec la synchronisation immédiate, les transactions new-order sont 4 fois plus lentes en comparaison avec la synchronisation différée pour laquelle le temps de réponse de new-order est presque constant (environ 6s avec une légère augmentation lorsque f augmente car plusieurs order-status peuvent être traitées en concurrence). Dans ce cas, le temps d'exécution des order-status décroît rapidement (de 31s à 8s) car de moins en moins d'order-status sont mises en attente après la prochaine synchronisation. Dans notre modèle, puisque la synchronisation est déclenchée aux moments de faible charge, plus la valeur f tolérée par les requêtes

20

est élevée, plus celles-ci sont rapides. Cette expérience met en avant le compromis entre efficacité et fraîcheur. Par exemple, pour exécuter les order-status en moins de 10s, les requêtes doivent tolérer une fraîcheur au moins égale à 7. Nous observons que le gain en temps de réponse (d'un facteur 4 pour les transactions new-order) est égal au nombre de nœuds SGBD utilisés pour l'expérience. Ce résultat prometteur nous laisse présager des gains élevés avec un cluster ayant plus de nœuds. Nous remarquons aussi, que le routeur détecte efficacement la fin des périodes de forte charge pour effectuer entièrement la synchronisation pendant les périodes peu chargées. 5.4 Limiter le nombre de nœuds à synchroniser Lorsque les applications effectuent des mises à jour intensives, il est parfois avantageux de ne pas synchroniser toutes les répliques afin d'économiser une partie des ressources CPU. Une surconsommation peut arriver si l'administrateur du cluster surestime le nombre de répliques d'une relation qui reçoit principalement des mises à jour (et très peu de requêtes). Dans l'expérience suivante, nous évaluons notre stratégie de routage dans le cas où la meilleure solution consiste à ne synchroniser que quelques nœuds. Nous considérons la transaction new-order ajoutant un n-uplet dans la relation order-line, et tolérant une fraîcheur de 10 nuplets. Le générateur de charge produit une charge constante composée de 8 neworder et de 10 order-status simultanées. Après chaque new-order, le générateur attend suffisamment pour que la synchronisation se produise.

temps d'exec (ms)

La figure 7 montre le temps d'exécution des requêtes pour deux méthodes. Premièrement, la méthode "dédiée" réserve n nœuds parmi p (p=4) pour les transactions new-order, les (p-n) nœuds restants servent à traiter les order-status. Deuxièmement, la méthode "partagée" accepte n nœuds pour traiter les new-order, tous les nœuds pouvant traiter les order-status. Les courbes NO et OS présentent les temps de réponse respectifs de new-order et d'order-status, en utilisant la méthode dédiée. Les courbes NOLB et OSLB présentent les temps de réponse respectifs de new-order et d'order-status, en utilisant la méthode partagée. 20000

NO

15000

OS

10000 5000

NOLB

0 1 2 3 4 nombre de nœuds n

OSLB

Figure 7: Temps d'exécution, selon n, avec les méthodes dédiée et partagée. Pour les deux méthodes, nous constatons que les temps d'exécution des neworder n'augmente pas lorsque n (le nombre de nœuds synchronisés) augmente.

21

Cependant, pour la méthode dédiée, à partir d'un certain nombre de nœuds alloués pour les new-order, il ne reste pas assez de nœuds pour traiter les autres requêtes. La méthode dédiée ne pénalise ni les new-order qui requièrent de la synchronisation, ni les order-status. La plupart des new-order ont été dirigées vers le même nœud, comme nous le souhaitions. 6. Conclusion Dans cet article, nous étudions le problème de l'équilibrage de charge et du routage des transactions dans un cluster de bases de données autonomes recevant des mises à jour intensives. Pour optimiser l'équilibrage de charge, nous utilisons la réplication optimiste des bases de données dont nous contrôlons la fraîcheur, et nous exploitons les travaux concernant le relâchement de la cohérence des données pour améliorer les performances, pour les appliquer dans le contexte des clusters de bases de données. Notre solution est une nouvelle stratégie de routage basée sur le coût, apportant plusieurs contributions : Premièrement, nous proposons une architecture de routage, pour les systèmes en cluster, qui préserve l'autonomie des applications et des bases de données grâce à des techniques non intrusives qui fonctionnent indépendamment de tout SGBD. Le routeur intercepte les transactions envoyées par l'application pour bénéficier du parallélisme et de la capacité de traitement du cluster, établit une politique pour empêcher les conflits entre les transactions tout en autorisant les traitements en parallèle sur les nœuds du cluster, et réduit les surcoûts liés à la synchronisation des répliques. Deuxièmement, nous proposons un modèle de coût pour estimer la fraîcheur d'une réplique. Le modèle est suffisamment générique pour estimer la fraîcheur des bases de données modifiées par des applications autonomes, sans la nécessité de connaître le code source détaillé des transactions. Il est toutefois suffisamment précis pour améliorer le routage des transactions. Pour chaque relation de chaque nœud, le modèle décrit la quantité de n-uplets, mis à jour par des transactions sur les autres nœuds, qui n'ont pas été mis à jour localement. Troisièmement, nous proposons un algorithme pour le routage des transactions qui prend en compte la fraîcheur requise des transactions et la fraîcheur courante des répliques, pour améliorer l’équilibrage de charge. Il explore les nœuds du cluster, détermine la synchronisation prérequise pour respecter la politique de la transaction, et choisit le nœud de moindre coût. La fonction de coût estime le temps pour exécuter une transaction sur un nœud, selon la charge du nœud, le temps restant des transactions en cours d'exécution qui partagent les mêmes ressources, et le temps pour traiter la synchronisation permettant au nœud d'atteindre la fraîcheur requise. Finalement, nous avons mis en œuvre notre solution sur un cluster de serveurs Oracle8i sous Linux, et effectué de nombreuses expériences en utilisant TPC-C, le banc de test pour applications transactionnelles. Nos résultats montrent que notre

22

solution surpasse les solutions existantes pour les charges transactionnelles typiques. Tous d'abord, nous avons montré que notre stratégie surpasse largement la stratégie round robin lorsque la charge est biaisée de manière réaliste. De plus, notre stratégie surpasse la stratégie CPU (choix du nœud le moins chargé), car elle a une meilleure réactivité face aux charges irrégulières. Puis, nous avons montré l'avantage de négocier de la fraîcheur en échange du temps de réponse, avec un gain aussi élevé que le nombre de nœuds lorsque les périodes de faible charge sont utilisées pour la synchronisation. Notre routeur améliore également le temps de réponse des transactions commutatives concurrentes, car nous avons défini la fraîcheur au niveau d'une relation, ce qui est un niveau de granularité plus fin que celui de [9]. Enfin, nous avons montré que notre algorithme ne minimise pas seulement le temps de réponse ; il économise également des opérations de synchronisation dès que le temps de réponse est assez court, en ajustant le nombre minimal de nœuds réservés pour traiter les transactions, et libérant ainsi davantage de nœuds pour les autres requêtes. Tout cela apporte des innovations supplémentaires par rapport aux solutions existantes. Bibliographie [1] R. Alonso, D. Barbará, and H. Garcia-Molina. Data Caching Issues in an Information Retrieval System. ACM Transactions on Database Systems (TODS), 15(3), 1990. [2] C. Amza et al. TreadMarks: Shared Memory Computing on Networks of Workstations. IEEE Computer, 29(2), 1996. [3] H. Berenson, P. Bernstein, J. Gray, J. Melton, E. O'Neil and P. O'Neil. A Critique of ANSI SQL Isolation Levels. In ACM SIGMOD Int. Conf. on Management of Data, 1995. [4] S. Gançarski, H. Naacke, and P. Valduriez. Load Balancing of Autonomous Applications and Databases in a Cluster System. In 4th Int. Meeting on Distributed Data and Structure (WDAS), 2002. [5] S. Gançarski, H. Naacke, E. Pacitti, and P. Valduriez. Parallel Processing with Autonomous Databases in a Cluster System. In Tenth Int. Conf. on Cooperative Information Systems (CoopIS), 2002. [6] S. Gançarski, C. Lepape, and P. Valduriez. Trading Freshness for Performance in a Cluster of Replicated Databases. In 5th Int. Meeting on Distributed Data and Structure (WDAS), 2003. [7] C. N. Nikolaou, M. Marazakis, and G. Georgiannakis. Transaction Routing for Distributed OLTP Systems: Survey and Recent Results. Information Sciences (1-2) 1996. [8] U. Röhm, K. Böhm, and H.-J. Schek. Cache-Aware Query Routing in a Cluster of Databases. Int. Conf. on Data Engineering (ICDE), 2001. [9] U. Röhm et al. FAS - A Freshness-Sensitive Coordination Middleware for a Cluster of OLAP Components. In Int. Conf. On Very Large Databases (VLDB), 2002. [10] A. Sheth, M. Rusinkiewicz. Management of Interdependent Data: Specifying Dependency and Consistency Requirements. Workshop on the Management of Replicated Data, 1990.

23 [11] Transaction Processing Performance Council. TPC Benchmark C, Rev 5.1, www.tpc.org/tpcc/.2002. [12] K. L. Wu, P. S Yu, and C. Pu. Divergence Control for Epsilon-Serializability. In 8th Int. Conf. on Data Engineering (ICDE), 1992. [13] H. Yu, A. Vahdat. Efficient Numerical Error Bounding for Replicated Network Services. In Int. Conf. On Very Large Databases (VLDB), 2000.