Tak til Pedro Boado og Abel Fernandez Alfonso fra Santanders ingeniørteam for deres samarbejde om dette indlæg om, hvordan Santander UK bruger Apache HBase som en servicemotor i næsten realtid til at drive sin innovative Spendlytics-app.
Spendlytics iOS-appen er designet til at hjælpe Santanders personlige debet- og kreditkortkunder med at holde styr på deres forbrug, inklusive betalinger foretaget via Apple Pay. Den bruger transaktionsdata i realtid til at give kunderne mulighed for at analysere deres kortforbrug på tværs af tidsperioder (ugentlig, månedlig, årlig), efter kategori (rejser, supermarkeder, kontanter osv.) og efter forhandler.
I vores tidligere indlæg beskrev vi, hvordan Apache Flume og Apache Kafka bruges til at transformere, berige og streame transaktioner til Apache HBase. Dette indlæg fortsætter med at beskrive, hvordan transaktioner arrangeres i Apache HBase for at optimere ydeevnen, og hvordan vi gør brug af coprocessors til at levere per-kunde-aggregeringer af købstendenser. Santander og Cloudera tog på (og er stadig på) en HBase-rejse med Spendlytics, en der har set mange iterationer og optimeringer af skemadesign og coprocessorimplementeringer. Vi håber, at disse erfaringer er nøglepunkterne fra dette indlæg.
Skema 1.0
Godt HBase-skemadesign handler om at forstå de tilsigtede adgangsmønstre. Få det rigtigt, og HBase vil flyve; tager det galt, og du kan ende med suboptimal ydeevne på grund af designafvejninger som regionshotspots eller at skulle udføre store scanninger på tværs af flere regioner. (Et hotspot i en HBase-tabel er det, hvor en ujævn rækketastfordeling kan forårsage, at størstedelen af anmodninger bliver dirigeret til en enkelt region, hvilket overvælder RegionServeren og resulterer i langsomme svartider.)
Hvad vi vidste om Spendlytics tilsigtede adgangsmønstre, og hvordan det påvirkede det oprindelige skemadesign:
- Kunder analyserer kun transaktioner på deres egne konti:
- For hurtig lineær scanningsydelse bør alle kundetransaktioner gemmes sekventielt.
- Kunde-id'er stiger monotont:
- Sekventielle kunde-id'er øger sandsynligheden for, at nyere kunder bliver samlokaliseret inden for den samme region, hvilket potentielt skaber et områdes hotspot. For at undgå dette problem skal kunde-id'er være saltet (præfikset) eller omvendt til jævn fordeling på tværs af regioner, når de bruges i begyndelsen af rækketasten.
- Kunder har flere kort
- For at optimere scanninger bør en kundes transaktioner grupperes yderligere og sorteres efter kortkontrakt, dvs. kontrakt-id'et skal udgøre en del af rækketasten.
- Transaktioner vil blive tilgået i deres helhed, dvs. attributter som forhandler, sælger, placering, valuta og beløb skal ikke læses separat
- Lagring af transaktionsattributter i separate celler vil resultere i en bredere, sparsom tabel, hvilket vil øge søgetiden. Da attributterne vil blive tilgået sammen, var det fornuftigt at serialisere dem sammen i en Apache Avro-post. Avro er kompakt og giver os en effektiv repræsentation med mulighed for at udvikle skemaer.
- Transaktioner tilgås individuelt, i batches (efter tidspunkt, kategori og forhandler) og efter aggregeret (efter tidspunkt, kategori og forhandler).
- Tilføjelse af et unikt transaktions-id som en kolonnekvalifikation vil tillade hentning af individuelle transaktioner uden at tilføje mere kompleksitet til rækketasten.
- For at muliggøre hurtig scanning af transaktioner over variable tidsperioder, bør transaktionens tidsstempling udgøre en del af rækketasten.
- At tilføje kategori og forhandler til rækketasten kan være for detaljeret og vil resultere i en meget høj og smal tabel med en kompleks rækketast. Høj og smal er OK, da atomicitet ikke er et problem, men at have dem som kolonnekvalifikationer ville udvide tabellen, mens den stadig understøtter sekundære aggregeringer.
- Trenddata bør forudberegnes så meget som muligt for at optimere læseydelsen.
- Mere om dette senere, men ved nu, at vi tilføjede en anden kolonnefamilie for at gemme tendenserne.
Baseret på ovenstående er det indledende skemadesign illustreret som følger:
Computing Trends
Det aspekt af det oprindelige design, vi lærte mest af, var computertendenser. Kravet var at give kunderne mulighed for at analysere deres forbrug efter kategori og forhandler ned til timen. Datapunkter inkluderede den mindste og største transaktionsværdi, den samlede transaktionsværdi og antallet af transaktioner. Svartider skulle være 200 ms eller mindre.
Precomputing-tendenser ville give os de hurtigste svartider, så dette var vores første tilgang. Trends kunne ikke halte transaktionerne, så de skulle beregnes på skrivestien. Dette ville være fantastisk til læseydelse, men gav os et par udfordringer:hvordan man bedst organiserer tendenser i HBase, og hvordan man beregner dem hurtigt og pålideligt uden at påvirke skriveydeevnen alvorligt.
Vi eksperimenterede med forskellige skemadesigns og forsøgte at udnytte nogle velkendte designs, hvor det var muligt (såsom OpenTSDBs skema). Efter adskillige iterationer besluttede vi os for skemadesignet illustreret ovenfor. Gemt i transaktionstabellen i en separat kolonnefamilie, er trendværdier organiseret sammen i en enkelt række med en trendrække pr. kunde. Ved at give rækketasten det samme præfiks som en kundes transaktioner (f.eks.
<reverse_customer_id>::<contract_id>
) det sikrede, at trendrækken vil blive sorteret sammen med den tilsvarende kundes transaktionsposter. Med definerede regionsgrænser og en tilpasset regionopdelingspolitik på plads, kan vi også garantere, at trendrækken altid vil blive samlokaliseret med en kundes transaktionsregistreringer, hvilket gør det muligt for trendaggregering at forblive helt server-side i coprocessoren.For at forudberegne tendenser implementerede vi en tilpasset observatør-coprocessor at hægte sig ind på skrivestien. (Observer-coprocessorer ligner triggere i et RDBMS ved, at de udfører brugerkode før eller efter en specifik hændelse opstår. For eksempel før eller efter
Put
ellerGet
.)På
postPut
coprocessoren udfører følgende handlinger:- Tjekker
Put
for en trendattribut (flag). Attributten indstilles kun på nye transaktionsposter for at undgå rekursive opkald, når trendposten opdateres. Det giver også mulighed for, at coprocessoren kan springes over forPut
s, der ikke kræver, at trends opdateres (f.eks. afregninger ). - Få trendrekord for kunden. En kundes trendregistrering er samlokaliseret med deres transaktioner (baseret på rækketastpræfiks), så coprocessoren kan hente den direkte fra den aktuelle region. Trendrækken skal låses for at forhindre, at flere RegionServer-handlertråde forsøger at opdatere tendenserne parallelt.
- Opdater datapunkter:
- Opdater og lås op for trendrækken.
Løsningen viste sig at være nøjagtig under test, og som forventet oversteg læseydelsen kravene. Der var dog nogle bekymringer med denne tilgang. Den første var, hvordan man håndterer fiasko:trends gemmes i en separat række, så atomicitet ikke kan garanteres. Det andet var, hvordan man validerer nøjagtigheden af tendenser over tid; det vil sige, at vi er nødt til at implementere en mekanisme til at identificere og afhjælpe eventuelle trendunøjagtigheder. Når vi også overvejede HA-kravene og det faktum, at vi skulle køre to, aktive aktive instanser af HBase i forskellige datacentre, kunne dette være et større problem. Ikke kun kunne trendnøjagtigheden falde over tid, men de to klynger kunne også drive og skulle afstemmes afhængigt af den metode, vi brugte til at synkronisere dem. Endelig ville det være svært at rette fejl eller tilføje nye datapunkter, fordi vi muligvis ville være nødt til at spore tilbage og genberegne alle tendenser.
Så var der skriveforestilling. For hver ny transaktion skulle observatøren hente en trendrekord, opdatere 32 datapunkter og sætte trendrekorden tilbage. På trods af at alt dette skete inden for rammerne af en enkelt region, fandt vi ud af, at gennemstrømningen blev reduceret fra over 20.000 skrivninger pr. sekund til 1.000 skrivninger pr. sekund (pr. RegionServer). Denne ydeevne var acceptabel på kort sigt, men ville ikke skaleres til at understøtte den forudsagte langsigtede belastning.
Vi vidste, at skriveydeevne var en risiko, så vi havde en backup-plan, og det var en endepunkt-coprocessor . Endpoint-coprocessorer ligner lagrede procedurer i et RDBMS, idet de giver dig mulighed for at udføre server-side-beregning - på den RegionServer, hvor dataene er placeret, snarere end på klienten. Endpoints udvider effektivt HBase API.
I stedet for at forudberegne tendenser, beregner endepunktet dem på et fly, server-side. Som et resultat kunne vi fjerne trendsøjlefamilien fra skemaet, og risikoen for unøjagtigheder og divergens fulgte med. At bevæge sig væk fra observatøren resulterede i god skriveydelse, men ville læsninger være hurtige nok? Kort sagt, ja. Med en kundes transaktioner begrænset til en enkelt region og sorteret efter kort og tidsstempel, kan slutpunktet scanne og aggregere hurtigt, godt inden for Spendlytics' mål på 200 ms. Dette betyder også, at en klientanmodning (fra Spendlytics API i dette tilfælde) kun bliver dirigeret til en enkelt Endpoint-instans (enkelt RegionServer), og klienten vil få et enkelt svar tilbage med et komplet resultat - det vil sige ingen klientside behandling er påkrævet for at aggregere delresultater fra flere slutpunkter, hvilket ville være tilfældet, hvis en kundes transaktioner spændte over flere regioner.
Erfaringer
Spendlytics har været live siden juli 2015. Siden da har vi overvåget adgangsmønstre nøje og set på måder at optimere ydeevnen på. Vi ønsker løbende at forbedre brugeroplevelsen og give kunderne mere og mere indsigt i deres kortforbrug. Resten af dette indlæg beskriver de erfaringer, vi har lært af at køre Spendlytics i produktion, og nogle af de optimeringer, der er blevet iværksat.
Efter den første udgivelse identificerede vi en række smertepunkter, som vi ønskede at fokusere på at forbedre. Den første var, hvordan man filtrerer resultater efter transaktionsattribut. Som tidligere nævnt er transaktionsattributter kodet i Avro-poster, men vi fandt ud af, at et stigende antal adgangsmønstre ønskede at filtrere efter attribut, og brugere blev tvunget til at gøre dette på klientsiden. Den oprindelige løsning var at implementere et brugerdefineret HBase
ValueFilter
der accepterede vores egne komplekse filterudtryk, for eksempel:category='SUPERMARKETS' AND amount > 100 AND (brand LIKE 'foo%' OR brand = 'bar')
Udtrykket evalueres for hver Avro-post, hvilket giver os mulighed for at filtrere resultaterne på serversiden og reducere mængden af data, der returneres til klienten (besparelse af netværksbåndbredde og behandling på klientsiden). Filteret påvirker scanningsydelsen, men responstiden forblev et godt stykke inden for målsætningen på 200 ms.
Dette endte med at blive en midlertidig løsning på grund af yderligere ændringer, der var nødvendige for at optimere skrivninger. På grund af den måde, kreditkortafregningsprocessen fungerer på, modtager vi først en autoriseret transaktion fra salgstidspunktet (i næsten realtid) og derefter nogen tid senere en afgjort transaktion fra kreditkortnetværket (i batch). Disse transaktioner skal afstemmes, hovedsageligt ved at sammenlægge de afviklede transaktioner med de autoriserede transaktioner, der allerede er i HBase, tilsluttes på transaktions-id. Som en del af denne proces kan transaktionsattributter ændres, og nye attributter kan tilføjes. Dette viste sig at være smertefuldt på grund af omkostningerne ved at skulle omskrive hele Avro-poster - selv når enkelte attributter blev opdateret. Så for at gøre attributterne mere tilgængelige for opdateringer organiserede vi dem i kolonner, der erstattede Avro-serialiseringen.
Vi bekymrer os også kun om atomicitet på transaktionsniveau, så det gav os ingen fordel at samle transaktionerne efter time. Desuden afgjort transaktioner, der nu ankommer i batch, har kun granularitet på dagsniveau, hvilket gjorde det vanskeligt (dyrt) at afstemme dem med eksisterende autoriserede transaktioner gemt pr. time. For at løse dette problem flyttede vi transaktions-id'et ind i rækketasten og reducerede tidsstemplet til dage i stedet for timer. Afstemningsprocessen er nu meget nemmere, fordi vi simpelthen kan masseindlæse ændringerne i HBase og lade afregningen værdier har forrang.
Sammenfattende:
- Observatør-coprocessorer kan være et værdifuldt værktøj, men brug dem med omtanke.
- I nogle tilfælde er udvidelse af HBase API ved hjælp af slutpunkter et godt alternativ.
- Brug tilpassede filtre til at forbedre ydeevnen ved at trimme resultaterne på serversiden.
- Serialiserede værdier giver mening for den rigtige brugssituation, men spiller til HBases styrker ved at favorisere indbygget understøttelse af felter og kolonner.
- Det er svært at administrere forudberegnede resultater; den ekstra ventetid fra computer-on-the-fly kan være umagen værd.
- Adgangsmønstre vil ændre sig, så vær smidig og åben over for at foretage ændringer i HBase-skemaet for at tilpasse og være på forkant med spillet.
Køreplan
En optimering, som vi i øjeblikket evaluerer, er hybride coprocessorer. Det, vi mener med dette, er kombinationen af både observatør- og endepunkt-coprocessorer for at forudberegne tendenser. Men i modsætning til tidligere ville vi ikke gøre dette på skrivestien, men i baggrunden ved at tilslutte os HBases skylle- og komprimeringsoperationer. En observatør vil beregne tendenser under skylle- og komprimeringshændelser baseret på afgjort transaktioner, der er tilgængelige på det tidspunkt. Vi ville derefter bruge et slutpunkt til at kombinere de forudberegnede tendenser med aggregeringer af transaktionernes delta. Ved at forudberegne tendenser på denne måde håber vi at give læst et boost i ydeevnen uden at påvirke skriveydeevnen.
En anden tilgang, vi evaluerer for trendsammenlægning og for HBase-adgang generelt, er Apache Phoenix. Phoenix er en SQL-skin til HBase, der muliggør adgang ved hjælp af standard JDBC API'er. Vi håber, at det ved at bruge SQL og JDBC vil forenkle HBase-adgangen og reducere mængden af kode, vi skal skrive. Vi kan også udnytte Phoenixs intelligente eksekveringsmønstre og indbyggede coprocessorer og filtre til hurtige aggregeringer. Phoenix blev anset for at være for umoden til produktionsbrug ved Spendlytics' start, men med lignende brugssager, der blev rapporteret af eBay og Salesforce, er det nu tid til at revurdere. (En Phoenix-pakke til CDH er tilgængelig til installation og evaluering, men uden support, via Cloudera Labs.)
Santander annoncerede for nylig, at det er den første bank, der lancerer stemmebankteknologi, der gør det muligt for kunder at tale med sin SmartBank-app og spørge om deres kortforbrug. Platformen bag denne teknologi er Cloudera, og arkitekturen for Spendlytics – som beskrevet i dette sæt indlæg – fungerede som blueprint-designet.
James Kinley er Principal Solutions Architect hos Cloudera.
Ian Buss er Senior Solutions Architect hos Cloudera.
Pedro Boado er en Hadoop-ingeniør hos Santander (Isban) UK.
Abel Fernandez Alfonso er en Hadoop-ingeniør hos Santander (Isban) UK.