HBase
 sql >> Base de données >  >> NoSQL >> HBase

À l'intérieur de l'architecture d'ingestion de données en temps quasi réel de Santander (partie 2)

Merci à Pedro Boado et Abel Fernandez Alfonso de l'équipe d'ingénieurs de Santander pour leur collaboration sur cet article expliquant comment Santander UK utilise Apache HBase comme moteur de service en temps quasi réel pour alimenter son application innovante Spendlytics.

L'application Spendlytics pour iOS est conçue pour aider les clients personnels de Santander par carte de débit et de crédit à maîtriser leurs dépenses, y compris les paiements effectués via Apple Pay. Il utilise des données de transaction en temps réel pour permettre aux clients d'analyser leurs dépenses par carte sur des périodes (hebdomadaires, mensuelles, annuelles), par catégorie (voyages, supermarchés, espèces, etc.) et par détaillant.

Dans notre article précédent, nous avons décrit comment Apache Flume et Apache Kafka sont utilisés pour transformer, enrichir et diffuser des transactions dans Apache HBase. Cet article continue en décrivant comment les transactions sont organisées dans Apache HBase pour optimiser les performances et comment nous utilisons des coprocesseurs pour fournir des agrégations par client des tendances d'achat. Santander et Cloudera ont poursuivi (et poursuivent) un parcours HBase avec Spendlytics, qui a vu de nombreuses itérations et optimisations de la conception de schémas et des implémentations de coprocesseurs. Nous espérons que ces leçons apprises sont les principaux points à retenir de cet article.

Schéma 1.0

Une bonne conception de schéma HBase consiste à comprendre les modèles d'accès prévus. Faites-le bien et HBase volera; vous vous trompez et vous pourriez vous retrouver avec des performances sous-optimales en raison de compromis de conception tels que les points chauds régionaux ou la nécessité d'effectuer de grandes analyses dans plusieurs régions. (Un point d'accès dans une table HBase, c'est là qu'une distribution inégale des clés de ligne peut entraîner l'acheminement de la majorité des requêtes vers une seule région, surchargeant le RegionServer et entraînant des temps de réponse lents.)

Ce que nous savions des schémas d'accès prévus par Spendlytics et de leur influence sur la conception initiale du schéma :

  • Les clients n'analysent que les transactions sur leurs propres comptes :
    • Pour des performances d'analyse linéaire rapides, toutes les transactions client doivent être stockées de manière séquentielle.
  • Les numéros de client augmentent de manière monotone :
    • Les ID client séquentiels augmentent la probabilité que de nouveaux clients soient situés au même endroit dans la même région, créant potentiellement un hot spot régional. Pour éviter ce problème, les ID client doivent être salés (préfixés) ou inversés pour une distribution uniforme entre les régions lorsqu'ils sont utilisés au début de la clé de ligne.
  • Les clients ont plusieurs cartes
    • Pour optimiser les analyses, les transactions d'un client doivent être regroupées et triées par contrat de carte, c'est-à-dire que l'ID de contrat doit faire partie de la clé de ligne.
  • Les transactions seront accessibles dans leur intégralité, c'est-à-dire que les attributs tels que le détaillant, le marchand, l'emplacement, la devise et le montant n'ont pas besoin d'être lus séparément
    • Le stockage des attributs de transaction dans des cellules séparées entraînerait une table plus large et clairsemée, ce qui augmenterait les temps de recherche. Comme les attributs seront accessibles ensemble, il était logique de les sérialiser ensemble dans un enregistrement Apache Avro. Avro est compact et nous fournit une représentation efficace avec une capacité d'évolution du schéma.
  • Les transactions sont accessibles individuellement, par lots (par heure, catégorie et détaillant) et par agrégation (par heure, catégorie et détaillant).
    • L'ajout d'un ID de transaction unique en tant que qualificatif de colonne permettra de récupérer des transactions individuelles sans compliquer davantage la clé de ligne.
    • Pour permettre une analyse rapide des transactions sur des périodes de temps variables, l'horodatage de la transaction doit faire partie de la clé de ligne.
    • L'ajout d'une catégorie et d'un détaillant à la clé de ligne pourrait être trop précis et entraînerait un tableau très long et étroit avec une clé de ligne complexe. Grand et étroit est OK étant donné que l'atomicité n'est pas un problème, mais les avoir comme qualificatifs de colonne élargirait le tableau tout en prenant en charge les agrégations secondaires.
  • Les données de tendance doivent être précalculées autant que possible pour optimiser les performances de lecture.
    • Plus d'informations à ce sujet plus tard, mais pour l'instant sachez que nous avons ajouté une deuxième famille de colonnes pour stocker les tendances.

    Sur la base de ce qui précède, la conception initiale du schéma est illustrée comme suit :

    Tendances informatiques

    L'aspect de la conception initiale dont nous avons le plus appris était les tendances informatiques. L'exigence était de permettre aux clients d'analyser leurs dépenses par catégorie et par détaillant à l'heure près. Les points de données comprenaient les valeurs de transaction les plus petites et les plus élevées, la valeur totale des transactions et le nombre de transactions. Les temps de réponse devaient être inférieurs ou égaux à 200 ms.

    Les tendances de précalcul nous donneraient les temps de réponse les plus rapides, c'était donc notre première approche. Les tendances ne pouvaient pas retarder les transactions, elles devaient donc être calculées sur le chemin d'écriture. Ce serait formidable pour les performances de lecture, mais nous avons dû relever quelques défis :comment organiser au mieux les tendances dans HBase et comment les calculer rapidement et de manière fiable sans affecter gravement les performances d'écriture.

    Nous avons expérimenté différentes conceptions de schémas et essayé de tirer parti de certaines conceptions bien connues lorsque cela était possible (comme le schéma d'OpenTSDB). Après plusieurs itérations, nous avons opté pour la conception de schéma illustrée ci-dessus. Stockées dans la table des transactions, dans une famille de colonnes séparée, les valeurs de tendance sont organisées ensemble sur une seule ligne, avec une ligne de tendance par client. En donnant à la clé de ligne le même préfixe que les transactions d'un client (par exemple, <reverse_customer_id>::<contract_id> ), il garantissait que la ligne de tendance serait triée avec les enregistrements de transaction du client correspondant. Avec des limites de région définies et une politique de division de région personnalisée en place, nous pouvons également garantir que la ligne de tendance sera toujours colocalisée avec les enregistrements de transaction d'un client, permettant à l'agrégation des tendances de rester entièrement côté serveur dans le coprocesseur.

    Pour précalculer les tendances, nous avons implémenté un coprocesseur observateur personnalisé pour s'accrocher au chemin d'écriture. (Les coprocesseurs observateurs sont similaires aux déclencheurs dans un SGBDR en ce sens qu'ils exécutent le code utilisateur avant ou après qu'un événement spécifique se produise. Par exemple, avant ou après Put ou Get .)

    Sur postPut le coprocesseur effectue les actions suivantes :

    1. Vérifie le Put pour un attribut de tendance (drapeau). L'attribut est défini sur les nouveaux enregistrements de transaction uniquement pour éviter les appels récursifs lors de la mise à jour de l'enregistrement de tendance. Il permet également de sauter le coprocesseur pour Put s qui ne nécessitent pas la mise à jour des tendances (par exemple, établissements ).
    2. Obtenir un enregistrement de tendance pour le client. L'enregistrement de tendance d'un client est colocalisé avec ses transactions (basé sur le préfixe de la clé de ligne) afin que le coprocesseur puisse le récupérer directement à partir de la région actuelle. La ligne de tendance doit être verrouillée pour empêcher plusieurs threads de gestionnaire RegionServer d'essayer de mettre à jour les tendances en parallèle.
    3. Mettre à jour les points de données :
    4. Mettre à jour et déverrouiller la ligne de tendance.

    La solution s'est avérée précise lors des tests et, comme prévu, les performances de lecture ont dépassé les exigences. Cependant, il y avait quelques inquiétudes avec cette approche. Le premier était de savoir comment gérer les échecs :les tendances sont stockées dans une ligne séparée, de sorte que l'atomicité ne peut pas être garantie. La seconde était de savoir comment valider l'exactitude des tendances au fil du temps ; c'est-à-dire que nous aurions besoin de mettre en œuvre un mécanisme pour identifier et corriger toute inexactitude des tendances. Lorsque nous avons également pris en compte les exigences de haute disponibilité et le fait que nous aurions besoin d'exécuter deux instances actives-actives de HBase dans différents centres de données, cela pourrait être un problème plus important. Non seulement la précision des tendances pourrait diminuer avec le temps, mais les deux groupes pourraient également dériver et devoir être réconciliés en fonction de la méthode que nous avons utilisée pour les synchroniser. Enfin, corriger des bogues ou ajouter de nouveaux points de données serait difficile car nous aurions éventuellement à revenir en arrière et à recalculer toutes les tendances.

    Ensuite, il y a eu les performances d'écriture. Pour chaque nouvelle transaction, l'observateur devait récupérer un enregistrement de tendance, mettre à jour 32 points de données et remettre l'enregistrement de tendance. Bien que tout cela se produise dans les limites d'une seule région, nous avons constaté que le débit était passé de plus de 20 000 écritures par seconde à 1 000 écritures par seconde (par RegionServer). Ces performances étaient acceptables à court terme, mais ne seraient pas adaptées à la charge prévue à long terme.

    Nous savions que les performances d'écriture étaient un risque, nous avions donc un plan de sauvegarde, et c'était un coprocesseur de point de terminaison . Les coprocesseurs de point de terminaison sont similaires aux procédures stockées dans un SGBDR en ce sens qu'ils vous permettent d'effectuer des calculs côté serveur, sur le RegionServer où se trouvent les données, plutôt que sur le client. Les points de terminaison étendent efficacement l'API HBase.

    Au lieu de précalculer les tendances, le terminal les calcule à la volée, côté serveur. En conséquence, nous pouvions supprimer la famille de colonnes de tendances du schéma et le risque d'inexactitudes et de divergences allait avec. S'éloigner de l'observateur a entraîné de bonnes performances d'écriture, mais les lectures seraient-elles assez rapides ? Bref, oui. Avec les transactions d'un client confinées à une seule région et triées par carte et horodatage, le point de terminaison peut analyser et agréger rapidement, bien dans l'objectif de 200 ms de Spendlytics. Cela signifie également qu'une demande client (depuis l'API Spendlytics dans ce cas) n'est jamais acheminée vers une seule instance Endpoint (un seul RegionServer) et que le client recevra une seule réponse avec un résultat complet, c'est-à-dire qu'il n'y a pas de côté client. le traitement est nécessaire pour agréger les résultats partiels de plusieurs points de terminaison, ce qui serait le cas si les transactions d'un client s'étendaient sur plusieurs régions.

    Leçons apprises

    Spendlytics est en ligne depuis juillet 2015. Depuis lors, nous avons surveillé de près les modèles d'accès et cherché des moyens d'optimiser les performances. Nous voulons continuellement améliorer l'expérience utilisateur et fournir aux clients de plus en plus d'informations sur leurs dépenses par carte. Le reste de cet article décrit les leçons que nous avons tirées de l'exécution de Spendlytics en production et certaines des optimisations qui ont été mises en place.

    Après la version initiale, nous avons identifié un certain nombre de points faibles que nous voulions nous concentrer sur l'amélioration. Le premier était de savoir comment filtrer les résultats par attribut de transaction. Comme mentionné précédemment, les attributs de transaction sont encodés dans les enregistrements Avro, mais nous avons constaté qu'un nombre croissant de modèles d'accès souhaitaient filtrer par attribut et que les utilisateurs étaient contraints de le faire côté client. La solution initiale consistait à implémenter un ValueFilter HBase personnalisé qui acceptaient nos propres expressions de filtre complexes, par exemple :

    category='SUPERMARKETS' AND amount > 100 AND 
    (brand LIKE 'foo%' OR brand = 'bar')

    L'expression est évaluée pour chaque enregistrement Avro, ce qui nous permet de filtrer les résultats côté serveur et de réduire la quantité de données renvoyées au client (économie de bande passante réseau et traitement côté client). Le filtre affecte les performances d'analyse, mais les temps de réponse sont restés bien en deçà de l'objectif de 200 ms.

    Cela s'est avéré être une solution temporaire en raison de modifications supplémentaires nécessaires pour optimiser les écritures. En raison du fonctionnement du processus de règlement par carte de crédit, nous recevons d'abord un autorisation transaction à partir du moment de la vente (en temps quasi réel) puis quelque temps plus tard un réglé transaction depuis le réseau des cartes de crédit (par lot). Ces transactions doivent être réconciliées, essentiellement en fusionnant les transactions réglées transactions avec le autorisé transactions déjà dans HBase, se joignant à l'ID de transaction. Dans le cadre de ce processus, les attributs de transaction peuvent changer et de nouveaux attributs peuvent être ajoutés. Cela s'est avéré pénible en raison de la surcharge liée à la réécriture d'enregistrements Avro entiers, même lors de la mise à jour d'attributs uniques. Donc, pour rendre les attributs plus accessibles pour les mises à jour, nous les avons organisés en colonnes, remplaçant la sérialisation Avro.

    Nous ne nous soucions également que de l'atomicité au niveau des transactions, donc classer les transactions par heure ne nous a donné aucun avantage. De plus, le réglé les transactions qui arrivent maintenant par lots n'ont qu'une granularité au niveau du jour, ce qui rend difficile (coûteux) leur rapprochement avec les transactions autorisées existantes transactions stockées par heure. Pour résoudre ce problème, nous avons déplacé l'ID de transaction dans la clé de ligne et réduit le grain d'horodatage à des jours plutôt qu'à des heures. Le processus de rapprochement est maintenant beaucoup plus facile car nous pouvons simplement charger en masse les modifications dans HBase et laisser le règlement les valeurs sont prioritaires.

    En résumé :

    • Les coprocesseurs observateurs peuvent être un outil précieux, mais utilisez-les judicieusement.
    • Pour certains cas d'utilisation, l'extension de l'API HBase à l'aide de points de terminaison est une bonne alternative.
    • Utilisez des filtres personnalisés pour améliorer les performances en réduisant les résultats côté serveur.
    • Les valeurs sérialisées ont du sens pour le bon cas d'utilisation, mais exploitent les atouts de HBase en favorisant la prise en charge native des champs et des colonnes.
    • La gestion des résultats précalculés est difficile ; la latence supplémentaire liée au calcul à la volée peut être intéressante.
    • Les modèles d'accès changeront, alors soyez agile et ouvert à apporter des modifications au schéma HBase pour vous adapter et garder une longueur d'avance.

    Feuille de route

    Une optimisation que nous évaluons actuellement est celle des coprocesseurs hybrides. Ce que nous entendons par là, c'est la combinaison des coprocesseurs d'observateur et de point final pour précalculer les tendances. Cependant, contrairement à avant, nous ne le ferions pas sur le chemin d'écriture mais en arrière-plan en nous connectant aux opérations de vidage et de compactage de HBase. Un observateur calculera les tendances lors des événements de rinçage et de compactage en fonction du décanté transactions disponibles à ce moment-là. Nous utiliserions ensuite un point de terminaison pour combiner les tendances précalculées avec des agrégations à la volée du delta des transactions. En précalculant les tendances de cette manière, nous espérons améliorer les performances des lectures, sans affecter les performances en écriture.

    Apache Phoenix est une autre approche que nous évaluons pour l'agrégation des tendances et pour l'accès à HBase en général. Phoenix est un habillage SQL pour HBase qui permet l'accès à l'aide d'API JDBC standard. Nous espérons qu'en utilisant SQL et JDBC cela simplifiera l'accès à HBase et réduira la quantité de code que nous devons écrire. Nous pouvons également tirer parti des modèles d'exécution intelligents de Phoenix et des coprocesseurs et filtres intégrés pour des agrégations rapides. Phoenix était considéré comme trop immature pour une utilisation en production au début de Spendlytics, mais avec des cas d'utilisation similaires signalés par eBay et Salesforce, il est maintenant temps de réévaluer. (Un package Phoenix pour CDH est disponible pour l'installation et l'évaluation, mais sans support, via Cloudera Labs.)

    Santander a récemment annoncé qu'elle était la première banque à lancer une technologie de banque vocale qui permet aux clients de parler à son application SmartBank et de poser des questions sur leurs dépenses par carte. La plate-forme derrière cette technologie est Cloudera, et l'architecture de Spendlytics, telle que décrite dans cet ensemble d'articles, a servi de modèle de conception.

    James Kinley est architecte de solutions principal chez Cloudera.

    Ian Buss est architecte de solutions senior chez Cloudera.

    Pedro Boado est ingénieur Hadoop à Santander (Isban) UK.

    Abel Fernández Alfonso est ingénieur Hadoop à Santander (Isban) UK.