Met dank aan Pedro Boado en Abel Fernandez Alfonso van het technische team van Santander voor hun medewerking aan dit bericht over hoe Santander UK Apache HBase gebruikt als een bijna realtime service-engine om zijn innovatieve Spendlytics-app aan te drijven.
De Spendlytics iOS-app is ontworpen om de persoonlijke debet- en creditcardklanten van Santander te helpen bij het bijhouden van hun uitgaven, inclusief betalingen via Apple Pay. Het gebruikt realtime transactiegegevens om klanten in staat te stellen hun kaartuitgaven te analyseren over tijdsperioden (wekelijks, maandelijks, jaarlijks), per categorie (reizen, supermarkten, contant geld, enz.) en per winkelier.
In ons vorige bericht hebben we beschreven hoe Apache Flume en Apache Kafka worden gebruikt om transacties te transformeren, te verrijken en te streamen naar Apache HBase. Dit bericht gaat verder met een beschrijving van hoe transacties in Apache HBase worden geregeld om de prestaties te optimaliseren, en hoe we coprocessors gebruiken om per klant aggregaties van inkooptrends te bieden. Santander en Cloudera gingen (en zijn nog steeds) op een HBase-reis met Spendlytics, een reis met veel iteraties en optimalisaties van schema-ontwerp en coprocessor-implementaties. We hopen dat deze geleerde lessen de belangrijkste afhaalpunten van dit bericht zijn.
Schema 1.0
Een goed HBase-schemaontwerp gaat over het begrijpen van de beoogde toegangspatronen. Doe het goed en HBase vliegt; Als u het verkeerd doet, kunt u suboptimale prestaties krijgen als gevolg van compromissen in het ontwerp, zoals regiohotspots of het moeten uitvoeren van grote scans in meerdere regio's. (Een hotspot in een HBase-tabel is waar een ongelijke verdeling van rijtoetsen ervoor kan zorgen dat de meeste verzoeken naar een enkele regio worden gerouteerd, waardoor de RegionServer wordt overweldigd en trage responstijden ontstaan.)
Wat we wisten over de beoogde toegangspatronen van Spendlytics en hoe dit het oorspronkelijke schemaontwerp beïnvloedde:
- Klanten analyseren alleen transacties op hun eigen rekeningen:
- Voor snelle lineaire scanprestaties moeten alle klanttransacties opeenvolgend worden opgeslagen.
- Klant-ID's nemen monotoon toe:
- Sequentiële klant-ID's vergroten de kans dat nieuwere klanten zich samen in dezelfde regio bevinden, waardoor er mogelijk een regiohotspot ontstaat. Om dit probleem te voorkomen, moeten klant-ID's worden gezouten (prefix) of omgekeerd, zodat ze gelijkmatig over de regio's worden verdeeld wanneer ze aan het begin van de rijsleutel worden gebruikt.
- Klanten hebben meerdere kaarten
- Om scans te optimaliseren, moeten de transacties van een klant verder worden gegroepeerd en gesorteerd op kaartcontract, d.w.z. de contract-ID moet deel uitmaken van de rijsleutel.
- Transacties zijn in hun geheel toegankelijk, d.w.z. attributen zoals verkoper, verkoper, locatie, valuta en bedrag hoeven niet afzonderlijk te worden gelezen
- Het opslaan van transactieattributen in afzonderlijke cellen zou resulteren in een bredere, schaarse tabel, wat de zoektijden zal verlengen. Omdat de attributen samen worden benaderd, was het logisch om ze samen te serialiseren in een Apache Avro-record. Avro is compact en biedt ons een efficiënte weergave met de mogelijkheid om schema's te ontwikkelen.
- Transacties zijn afzonderlijk toegankelijk, in batches (op tijd, categorie en detailhandelaar) en geaggregeerd (op tijd, categorie en detailhandelaar).
- Door een unieke transactie-ID toe te voegen als kolomkwalificatie, kunnen individuele transacties worden opgehaald zonder de rijsleutel ingewikkelder te maken.
- Om snel scannen van transacties over variabele tijdsperioden mogelijk te maken, moet het transactietijdstempel deel uitmaken van de rijtoets.
- Het toevoegen van categorie en verkoper aan de rijsleutel kan te gedetailleerd zijn en zou resulteren in een erg lange en smalle tabel met een complexe rijsleutel. Lang en smal is OK, aangezien atomiciteit geen probleem is, maar als ze als kolomkwalificaties zouden worden gebruikt, zou de tabel breder worden terwijl secundaire aggregaties nog steeds worden ondersteund.
- Trendgegevens moeten zoveel mogelijk vooraf worden berekend om de leesprestaties te optimaliseren.
- Hierover later meer, maar weet nu dat we een tweede kolomfamilie hebben toegevoegd om de trends op te slaan.
Op basis van het bovenstaande wordt het initiële schema-ontwerp als volgt geïllustreerd:
Computertrends
Het aspect van het oorspronkelijke ontwerp waar we het meest van leerden, waren computertrends. De eis was om klanten in staat te stellen hun uitgaven per categorie en per retailer tot op het uur te analyseren. Gegevenspunten omvatten de kleinste en grootste transactiewaarden, de totale transactiewaarde en het aantal transacties. Reactietijden moesten 200 ms of minder zijn.
Het vooraf berekenen van trends zou ons de snelste responstijden opleveren, dus dit was onze eerste benadering. Trends konden niet achterblijven bij de transacties, dus moesten ze worden berekend op het schrijfpad. Dit zou geweldig zijn voor de leesprestaties, maar stelde ons voor een aantal uitdagingen:hoe kunnen we trends in HBase het beste organiseren en hoe kunnen ze snel en betrouwbaar worden berekend zonder de schrijfprestaties ernstig te beïnvloeden.
We experimenteerden met verschillende schema-ontwerpen en probeerden waar mogelijk gebruik te maken van een aantal bekende ontwerpen (zoals het schema van OpenTSDB). Na verschillende iteraties kwamen we uit op het hierboven geïllustreerde schema-ontwerp. Opgeslagen in de transactietabel, in een aparte kolomfamilie, worden trendwaarden samen in één rij georganiseerd, met één trendrij per klant. Door de rowkey hetzelfde prefix te geven als de transacties van een klant (bijvoorbeeld
<reverse_customer_id>::<contract_id>
) het zorgde ervoor dat de trendrij naast de bijbehorende transactierecords van de klant wordt gesorteerd. Met gedefinieerde regiogrenzen en een aangepast beleid voor regiosplitsing, kunnen we ook garanderen dat de trendrij altijd wordt gecolloceerd met de transactierecords van een klant, waardoor trendaggregatie volledig aan de serverzijde in de coprocessor blijft.Om trends vooraf te berekenen, hebben we een aangepaste waarnemer-coprocessor geïmplementeerd om in het schrijfpad te haken. (Observer-coprocessors zijn vergelijkbaar met triggers in een RDBMS in die zin dat ze gebruikerscode uitvoeren voor of na een specifieke gebeurtenis. Bijvoorbeeld pre of post
Put
ofGet
.)Op
postPut
de coprocessor voert de volgende acties uit:- Controleert de
Put
voor een trendattribuut (vlag). Het attribuut wordt alleen ingesteld op nieuwe transactierecords om recursieve oproepen te voorkomen bij het bijwerken van de trendrecord. Het staat ook toe dat de coprocessor wordt overgeslagen voorPut
s waarvoor geen trends hoeven te worden bijgewerkt (bijv. nederzettingen ). - Ontvang een trendrecord voor de klant. Het trendrecord van een klant wordt gecolocaliseerd met hun transacties (gebaseerd op rowkey-prefix), zodat de coprocessor het rechtstreeks uit de huidige regio kan ophalen. De trendrij moet worden vergrendeld om te voorkomen dat meerdere RegionServer-handlerthreads proberen de trends parallel bij te werken.
- Datapunten bijwerken:
- Trendrij bijwerken en ontgrendelen.
De oplossing bleek tijdens het testen nauwkeurig te zijn en zoals verwacht overtroffen de leesprestaties de vereisten. Er waren echter enkele bedenkingen bij deze aanpak. De eerste was hoe om te gaan met falen:trends worden in een aparte rij opgeslagen, dus atomiciteit kan niet worden gegarandeerd. De tweede was hoe de nauwkeurigheid van trends in de tijd te valideren; dat wil zeggen, we zouden een mechanisme moeten implementeren om eventuele trendonnauwkeurigheden te identificeren en te verhelpen. Toen we ook rekening hielden met de HA-vereisten en het feit dat we twee, actief-actieve exemplaren van HBase in verschillende datacenters zouden moeten draaien, zou dit een groter probleem kunnen zijn. Niet alleen kan de trendnauwkeurigheid in de loop van de tijd afnemen, maar de twee clusters kunnen ook afdrijven en moeten worden afgestemd, afhankelijk van de methode die we hebben gebruikt om ze te synchroniseren. Ten slotte zou het oplossen van bugs of het toevoegen van nieuwe datapunten moeilijk zijn, omdat we mogelijk alle trends zouden moeten terugdraaien en opnieuw moeten berekenen.
Daarna was er een schrijfvoorstelling. Voor elke nieuwe transactie moest de waarnemer een trendrecord ophalen, 32 datapunten bijwerken en het trendrecord terugzetten. Ondanks dat dit allemaal binnen de grenzen van een enkele regio gebeurde, ontdekten we dat de doorvoer werd teruggebracht van meer dan 20.000 schrijfbewerkingen per seconde naar 1000 schrijfbewerkingen per seconde (per RegionServer). Deze prestatie was acceptabel op korte termijn, maar zou niet worden geschaald om de voorspelde belasting op lange termijn te ondersteunen.
We wisten dat schrijfprestaties een risico vormden, dus we hadden een back-upplan en dat was een eindpuntcoprocessor . Endpoint-coprocessors zijn vergelijkbaar met opgeslagen procedures in een RDBMS, omdat ze u in staat stellen om server-side berekeningen uit te voeren - op de RegionServer waar de gegevens zich bevinden, in plaats van bij de client. Endpoints breiden de HBase API effectief uit.
In plaats van trends vooraf te berekenen, berekent het eindpunt ze on-the-fly, server-side. Als gevolg hiervan konden we de kolomfamilie trends uit het schema laten vallen en ging het risico van onnauwkeurigheden en divergentie ermee gepaard. Weggaan van de waarnemer resulteerde in goede schrijfprestaties, maar zou het lezen snel genoeg zijn? Kortom, ja. Met de transacties van een klant beperkt tot één regio en gesorteerd op kaart en tijdstempel, kan het eindpunt snel scannen en aggregeren, ruim binnen de doelstelling van Spendlytics van 200 ms. Dit betekent ook dat een clientverzoek (in dit geval van de Spendlytics-API) alleen wordt doorgestuurd naar een enkele Endpoint-instantie (single RegionServer) en dat de client één reactie terugkrijgt met een volledig resultaat, dat wil zeggen, geen client-side verwerking is vereist om gedeeltelijke resultaten van meerdere eindpunten samen te voegen, wat het geval zou zijn als de transacties van een klant meerdere regio's omvatten.
Leren geleerd
Spendlytics is live sinds juli 2015. Sindsdien hebben we toegangspatronen nauwlettend gevolgd en gekeken naar manieren om de prestaties te optimaliseren. We willen de gebruikerservaring continu verbeteren en klanten steeds meer inzicht geven in hun kaartbestedingen. De rest van dit bericht beschrijft de lessen die we hebben geleerd van het uitvoeren van Spendlytics in productie en enkele van de optimalisaties die zijn doorgevoerd.
Na de eerste release hebben we een aantal pijnpunten geïdentificeerd waarop we ons wilden concentreren op verbetering. De eerste was hoe resultaten te filteren op transactiekenmerk. Zoals eerder vermeld, worden transactieattributen gecodeerd in Avro-records, maar we ontdekten dat een toenemend aantal toegangspatronen op attribuut wilden filteren en gebruikers werden gedwongen dit aan de clientzijde te doen. De eerste oplossing was het implementeren van een aangepaste HBase
ValueFilter
die onze eigen complexe filterexpressies accepteerden, bijvoorbeeld:category='SUPERMARKETS' AND amount > 100 AND (brand LIKE 'foo%' OR brand = 'bar')
De expressie wordt geëvalueerd voor elk Avro-record, waardoor we de resultaten aan de serverzijde kunnen filteren en de hoeveelheid gegevens die naar de client wordt geretourneerd, kunnen verminderen (besparing van netwerkbandbreedte en verwerking aan clientzijde). Het filter heeft wel invloed op de scanprestaties, maar de responstijden bleven ruim binnen de doelstelling van 200 ms.
Dit werd uiteindelijk een tijdelijke oplossing vanwege verdere wijzigingen die nodig waren om het schrijven te optimaliseren. Vanwege de manier waarop het creditcardafrekeningsproces werkt, ontvangen we eerst een geautoriseerde transactie vanaf het moment van verkoop (in bijna realtime) en enige tijd later een afgewikkelde transactie van het creditcardnetwerk (in batch). Deze transacties moeten worden afgestemd, in wezen door het samenvoegen van de afgewikkelde transacties met de geautoriseerde transacties al in HBase, deelname op transactie-ID. Als onderdeel van dit proces kunnen transactiekenmerken veranderen en kunnen nieuwe kenmerken worden toegevoegd. Dit bleek pijnlijk te zijn vanwege de overhead van het moeten herschrijven van volledige Avro-records, zelfs bij het bijwerken van afzonderlijke attributen. Dus om de attributen toegankelijker te maken voor updates, hebben we ze ingedeeld in kolommen, ter vervanging van de Avro-serialisatie.
We geven ook alleen om de atomiciteit op transactieniveau, dus de transacties per uur in een emmer plaatsen gaf ons geen enkel voordeel. Bovendien hebben de geregelde transacties die nu in batch aankomen, hebben alleen granulariteit op dagniveau, waardoor het moeilijk (kostbaar) was om ze in overeenstemming te brengen met bestaande geautoriseerde transacties per uur opgeslagen. Om dit probleem op te lossen, hebben we de transactie-ID naar de rijsleutel verplaatst en de tijdstempelkorrel teruggebracht tot dagen in plaats van uren. Het afstemmingsproces is nu veel eenvoudiger omdat we de wijzigingen eenvoudig in HBase kunnen laden en de afrekening kunnen laten uitvoeren. waarden hebben voorrang.
Samengevat:
- Observer-coprocessors kunnen een waardevol hulpmiddel zijn, maar gebruik ze verstandig.
- Voor sommige gebruikssituaties is het uitbreiden van de HBase API met behulp van eindpunten een goed alternatief.
- Gebruik aangepaste filters om de prestaties te verbeteren door de resultaten aan de serverzijde bij te snijden.
- Geserialiseerde waarden zijn logisch voor het juiste gebruik, maar spelen in op de sterke punten van HBase door de voorkeur te geven aan native ondersteuning voor velden en kolommen.
- Het is moeilijk om vooraf berekende resultaten te beheren; de extra latentie van on-the-fly computergebruik kan de moeite waard zijn.
- Toegangspatronen zullen veranderen, dus wees wendbaar en sta open voor het aanbrengen van wijzigingen in het HBase-schema om u aan te passen en de concurrentie voor te blijven.
Routekaart
Een optimalisatie die we momenteel evalueren, zijn hybride coprocessors. Wat we hiermee bedoelen is de combinatie van zowel waarnemer- als eindpunt-coprocessors om trends vooraf te berekenen. In tegenstelling tot voorheen zouden we dit echter niet op het schrijfpad doen, maar op de achtergrond door in te haken op de spoel- en verdichtingsbewerkingen van HBase. Een waarnemer berekent trends tijdens spoel- en verdichtingsgebeurtenissen op basis van de vastgelegde transacties die op dat moment beschikbaar zijn. We zouden dan een eindpunt gebruiken om de vooraf berekende trends te combineren met directe aggregaties van de delta van transacties. Door op deze manier trends vooraf te berekenen, hopen we de leesprestaties een boost te geven, zonder de schrijfprestaties te beïnvloeden.
Een andere benadering die we evalueren voor trendaggregatie en voor HBase-toegang in het algemeen, is Apache Phoenix. Phoenix is een SQL-skin voor HBase die toegang mogelijk maakt met behulp van standaard JDBC-API's. We hopen dat het gebruik van SQL en JDBC de toegang tot HBase zal vereenvoudigen en de hoeveelheid code die we moeten schrijven zal verminderen. We kunnen ook gebruikmaken van de intelligente uitvoeringspatronen van Phoenix en ingebouwde coprocessors en filters voor snelle aggregaties. Phoenix werd bij het begin van Spendlytics als te onvolwassen beschouwd voor productiegebruik, maar met soortgelijke use-cases die worden gerapporteerd door eBay en Salesforce, is dit het moment om opnieuw te evalueren. (Een Phoenix-pakket voor CDH is beschikbaar voor installatie en evaluatie, maar zonder ondersteuning, via Cloudera Labs.)
Santander heeft onlangs aangekondigd dat het de eerste bank is die voice banking-technologie lanceert waarmee klanten met de SmartBank-app kunnen praten en vragen kunnen stellen over hun kaartuitgaven. Het platform achter deze technologie is Cloudera en de architectuur voor Spendlytics, zoals beschreven in deze reeks berichten, diende als blauwdrukontwerp.
James Kinley is Principal Solutions Architect bij Cloudera.
Ian Buss is Senior Solutions Architect bij Cloudera.
Pedro Boado is een Hadoop-ingenieur in Santander (Isban) VK.
Abel Fernandez Alfonso is een Hadoop-ingenieur bij Santander (Isban) VK.