sql >> Databasteknik >  >> NoSQL >> HBase

Inuti Santanders nästan realtidsdataintagsarkitektur (del 2)

Tack till Pedro Boado och Abel Fernandez Alfonso från Santanders ingenjörsteam för deras samarbete i det här inlägget om hur Santander UK använder Apache HBase som en nästan realtidsservicemotor för att driva sin innovativa Spendlytics-app.

Spendlytics iOS-appen är utformad för att hjälpa Santanders personliga betal- och kreditkortskunder att hålla koll på sina utgifter, inklusive betalningar som görs via Apple Pay. Den använder transaktionsdata i realtid för att göra det möjligt för kunder att analysera sina kortutgifter över tidsperioder (veckovis, månadsvis, årligen), efter kategori (resor, stormarknader, kontanter, etc.) och efter återförsäljare.

I vårt tidigare inlägg beskrev vi hur Apache Flume och Apache Kafka används för att transformera, berika och strömma transaktioner till Apache HBase. Det här inlägget fortsätter med att beskriva hur transaktioner ordnas i Apache HBase för att optimera prestanda, och hur vi använder oss av samprocessorer för att tillhandahålla aggregationer per kund av inköpstrender. Santander och Cloudera gick på (och är fortfarande på) en HBase-resa med Spendlytics, en som har sett många iterationer och optimeringar av schemadesign och samprocessorimplementeringar. Vi hoppas att dessa lärdomar är de viktigaste punkterna från det här inlägget.

Schema 1.0

Bra HBase-schemadesign handlar om att förstå de avsedda åtkomstmönstren. Gör det rätt och HBase flyger; får det fel och du kan sluta med suboptimal prestanda på grund av designavvägningar som regions hotspots eller att behöva utföra stora skanningar över flera regioner. (En hotspot i en HBase-tabell är det där en ojämn radnyckelfördelning kan orsaka att majoriteten av förfrågningarna dirigeras till en enskild region, vilket överväldigar RegionServer och resulterar i långsamma svarstider.)

Vad vi visste om Spendlytics avsedda åtkomstmönster och hur det påverkade den initiala schemadesignen:

  • Kunder analyserar endast transaktioner på sina egna konton:
    • För snabb linjär skanningsprestanda bör alla kundtransaktioner lagras sekventiellt.
  • Kund-ID:n ökar monotont:
    • Sekventiella kund-ID:n ökar sannolikheten för att nyare kunder kommer att samlokaliseras inom samma region, vilket kan skapa en regions hotspot. För att undvika detta problem bör kund-ID:n saltas (föregå) eller vändas till jämn fördelning över regioner när de används i början av radtangenten.
  • Kunder har flera kort
    • För att optimera skanningar bör en kunds transaktioner grupperas ytterligare och sorteras efter kortkontrakt, dvs. kontrakts-ID:t bör utgöra en del av radknappen.
  • Transaktioner kommer att nås i sin helhet, d.v.s. attribut som återförsäljare, handlare, plats, valuta och belopp behöver inte läsas separat
    • Att lagra transaktionsattribut i separata celler skulle resultera i en bredare, glesare tabell, vilket kommer att öka söktiderna. Eftersom attributen kommer att nås tillsammans var det meningsfullt att serialisera dem tillsammans i en Apache Avro-post. Avro är kompakt och ger oss en effektiv representation med möjlighet att utveckla scheman.
  • Transaktioner nås individuellt, i omgångar (efter tid, kategori och återförsäljare) och efter sammanställning (efter tid, kategori och återförsäljare).
    • Om du lägger till ett unikt transaktions-ID som kolumnkvalificerare kan individuella transaktioner hämtas utan att radtangenten blir mer komplex.
    • För att möjliggöra snabb genomsökning av transaktioner över varierande tidsperioder bör transaktionens tidsstämpel utgöra en del av radtangenten.
    • Att lägga till kategori och återförsäljare till radtangenten kan vara för granulärt och skulle resultera i en mycket lång och smal tabell med en komplex radnyckel. Lång och smal är OK med tanke på att atomicitet inte är ett problem, men att ha dem som kolumnkvalificerare skulle bredda tabellen samtidigt som det stöder sekundära aggregationer.
  • Trenddata bör förberäknas så mycket som möjligt för att optimera läsprestanda.
    • Mer om detta senare, men vet nu att vi har lagt till en andra kolumnfamilj för att lagra trenderna.

    Baserat på ovanstående illustreras den initiala schemadesignen enligt följande:

    Datortrender

    Den aspekt av den ursprungliga designen vi lärde oss mest av var datortrender. Kravet var att göra det möjligt för kunderna att analysera sina utgifter per kategori och återförsäljare ner till timme. Datapunkter inkluderade de minsta och största transaktionsvärdena, totalt transaktionsvärde och antal transaktioner. Svarstiderna måste vara 200 ms eller mindre.

    Precomputing trender skulle ge oss de snabbaste svarstiderna så detta var vår första metod. Trender kunde inte släpa efter transaktionerna så de måste beräknas på skrivvägen. Detta skulle vara bra för läsprestanda, men gav oss ett par utmaningar:hur man bäst organiserar trender i HBase, och hur man beräknar dem snabbt och tillförlitligt utan att allvarligt påverka skrivprestandan.

    Vi experimenterade med olika schemadesigner och försökte utnyttja några välkända konstruktioner där det var möjligt (som OpenTSDB:s schema). Efter flera iterationer bestämde vi oss för schemadesignen som illustreras ovan. Lagrade i transaktionstabellen, i en separat kolumnfamilj, är trendvärden organiserade i en enda rad, med en trendrad per kund. Genom att ge radtangenten samma prefix som en kunds transaktioner (till exempel <reverse_customer_id>::<contract_id> ) det säkerställde att trendraden kommer att sorteras tillsammans med motsvarande kunds transaktionsposter. Med definierade regiongränser och en anpassad regionuppdelningspolicy på plats kan vi också garantera att trendraden alltid kommer att samlokaliseras med en kunds transaktionsposter, vilket gör att trendaggregation förblir helt på serversidan i samprocessorn.

    För att förberäkna trender implementerade vi en anpassad observatörssamprocessor att haka på skrivvägen. (Observatörssamprocessorer liknar triggers i ett RDBMS genom att de exekverar användarkod före eller efter en specifik händelse inträffar. Till exempel, före eller efter Put eller Get .)

    postPut samprocessorn utför följande åtgärder:

    1. Kontrollerar Put för ett trendattribut (flagga). Attributet sätts endast på nya transaktionsposter för att undvika rekursiva anrop vid uppdatering av trendposten. Det gör också att samprocessorn kan hoppas över för Put s som inte kräver att trender uppdateras (t.ex. avräkningar ).
    2. Få trendrekord för kunden. En kunds trendpost samlokaliseras med deras transaktioner (baserat på radnyckelprefix) så att samprocessorn kan hämta den direkt från den aktuella regionen. Trendraden måste låsas för att förhindra att flera RegionServer-hanterartrådar försöker uppdatera trenderna parallellt.
    3. Uppdatera datapunkter:
    4. Uppdatera och lås upp trendraden.

    Lösningen visade sig vara korrekt under testning och som förväntat överträffade läsprestanda kraven. Det fanns dock vissa bekymmer med detta tillvägagångssätt. Den första var hur man hanterar misslyckanden:trender lagras i en separat rad så atomicitet kan inte garanteras. Det andra var hur man validerar riktigheten av trender över tid; det vill säga vi skulle behöva implementera en mekanism för att identifiera och åtgärda eventuella trendfel. När vi även övervägde HA-kraven och det faktum att vi skulle behöva köra två, aktiva aktiva instanser av HBase i olika datacenter, kan detta vara ett större problem. Inte bara kunde trendnoggrannheten minska med tiden, utan de två klustren kan också glida och behöva stämmas av beroende på metoden vi använde för att synkronisera dem. Slutligen skulle det vara svårt att fixa buggar eller lägga till nya datapunkter eftersom vi möjligen skulle behöva backa och räkna om alla trender.

    Sedan var det skrivföreställning. För varje ny transaktion var observatören tvungen att hämta en trendpost, uppdatera 32 datapunkter och lägga tillbaka trendposten. Trots att allt detta händer inom gränserna för en enskild region, fann vi att genomströmningen minskade från över 20 000 skrivningar per sekund till 1 000 skrivningar per sekund (per RegionServer). Denna prestanda var acceptabel på kort sikt, men skulle inte skalas för att stödja den förutspådda långsiktiga belastningen.

    Vi visste att skrivprestanda var en risk så vi hade en backupplan, och det var en slutpunktssamprocessor . Endpoint-samprocessorer liknar lagrade procedurer i ett RDBMS genom att de låter dig utföra beräkning på serversidan – på RegionServer där data finns, snarare än på klienten. Endpoints utökar effektivt HBase API.

    Istället för att förberäkna trender, beräknar slutpunkten dem direkt på serversidan. Som ett resultat kunde vi ta bort trendkolumnfamiljen från schemat och risken för felaktigheter och divergens följde med det. Att flytta bort från observatören resulterade i bra skrivprestanda, men skulle läsningen vara tillräckligt snabb? Kort sagt, ja. Med en kunds transaktioner begränsade till en enda region och sorterade efter kort och tidsstämpel, kan slutpunkten skanna och aggregeras snabbt, väl inom Spendlytics mål på 200 ms. Detta innebär också att en klientbegäran (från Spendlytics API i det här fallet) bara skickas till en enda Endpoint-instans (enkel RegionServer) och klienten kommer att få ett enda svar tillbaka med ett fullständigt resultat – det vill säga ingen klientsida bearbetning krävs för att aggregera delresultat från flera slutpunkter, vilket skulle vara fallet om en kunds transaktioner sträckte sig över flera regioner.

    Lärdomar

    Spendlytics har varit live sedan juli 2015. Sedan dess har vi övervakat åtkomstmönster noga och tittat på sätt att optimera prestandan. Vi vill ständigt förbättra användarupplevelsen och ge kunderna mer och mer insikt i deras kortutgifter. Resten av det här inlägget beskriver de lärdomar vi har dragit av att köra Spendlytics i produktion och några av de optimeringar som har införts.

    Efter den första releasen identifierade vi ett antal smärtpunkter som vi ville fokusera på att förbättra. Det första var hur man filtrerar resultat efter transaktionsattribut. Som tidigare nämnts kodas transaktionsattribut i Avro-poster, men vi fann att ett ökande antal åtkomstmönster ville filtrera efter attribut och användare tvingades göra detta på klientsidan. Den första lösningen var att implementera ett anpassat HBase ValueFilter som accepterade våra egna komplexa filteruttryck, till exempel:

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

    Uttrycket utvärderas för varje Avro-post, vilket gör att vi kan filtrera resultaten på serversidan och minska mängden data som returneras till klienten (sparar nätverksbandbredd och bearbetning på klientsidan). Filtret påverkar skanningsprestandan, men svarstiderna höll sig väl inom målet på 200 ms.

    Detta slutade med att bli en tillfällig lösning på grund av ytterligare ändringar som krävdes för att optimera skrivningar. På grund av hur kreditkortsbetalningsprocessen fungerar får vi först en auktoriserad transaktion från tidpunkten för försäljningen (i nästan realtid) och sedan en tid senare en avgjord transaktion från kreditkortsnätverket (i batch). Dessa transaktioner måste stämmas av, huvudsakligen genom att slå samman de avräknade transaktioner med de auktoriserade transaktioner som redan finns i HBase, går med på transaktions-ID. Som en del av denna process kan transaktionsattribut ändras och nya attribut kan läggas till. Detta visade sig vara smärtsamt på grund av att man måste skriva om hela Avro-poster – även när man uppdaterar enstaka attribut. Så för att göra attributen mer tillgängliga för uppdateringar organiserade vi dem i kolumner och ersatte Avro-serialiseringen.

    Vi bryr oss också bara om atomicitet på transaktionsnivå, så att buckla transaktionerna per timme gav oss ingen fördel. Dessutom avgjorda transaktioner som nu anländer i batch har endast granularitet på dagsnivå, vilket gjorde det svårt (kostsamt) att stämma av dem med befintliga auktoriserade transaktioner lagrade per timme. För att lösa detta problem flyttade vi transaktions-ID:t till radtangenten och reducerade tidsstämpeln till dagar istället för timmar. Avstämningsprocessen är nu mycket enklare eftersom vi helt enkelt kan massinläsa ändringarna i HBase och låta avvecklingen värden har företräde.

    Sammanfattningsvis:

    • Observatörssamprocessorer kan vara ett värdefullt verktyg, men använd dem på ett klokt sätt.
    • För vissa användningsfall är det ett bra alternativ att utöka HBase API med hjälp av slutpunkter.
    • Använd anpassade filter för att förbättra prestandan genom att trimma resultaten på serversidan.
    • Serialiserade värden är vettiga för rätt användningsfall, men spelar till HBases styrkor genom att gynna inbyggt stöd för fält och kolumner.
    • Det är svårt att hantera förberäknade resultat; den extra fördröjningen från datoranvändning i farten kan vara värt besväret.
    • Åtkomstmönster kommer att förändras, så var smidig och öppen för att göra ändringar i HBase-schemat för att anpassa dig och ligga steget före i spelet.

    Färdkarta

    En optimering som vi just nu utvärderar är hybridsamprocessorer. Vad vi menar med detta är kombinationen av både observatörs- och slutpunktssamprocessorer för att förberäkna trender. Men till skillnad från tidigare skulle vi inte göra detta på skrivvägen utan i bakgrunden genom att koppla in i HBases spolnings- och komprimeringsoperationer. En observatör kommer att beräkna trender under spolnings- och komprimeringshändelser baserat på avgjorda transaktioner tillgängliga vid den tidpunkten. Vi skulle sedan använda en slutpunkt för att kombinera de förberäknade trenderna med aggregering av transaktionsdeltat i farten. Genom att förberäkna trender på det här sättet hoppas vi kunna ge läsresultaten ett lyft utan att påverka skrivprestandan.

    Ett annat tillvägagångssätt som vi utvärderar för trendaggregation, och för HBase-åtkomst i allmänhet, är Apache Phoenix. Phoenix är ett SQL-skal för HBase som möjliggör åtkomst med standard JDBC API:er. Vi hoppas att det genom att använda SQL och JDBC kommer att förenkla åtkomst till HBase och minska mängden kod vi måste skriva. Vi kan också utnyttja Phoenixs intelligenta exekveringsmönster och inbyggda samprocessorer och filter för snabba aggregering. Phoenix ansågs vara för omogen för produktionsanvändning vid Spendlytics start, men med liknande användningsfall som rapporterats av sådana som eBay och Salesforce, är det nu dags att omvärdera. (Ett Phoenix-paket för CDH är tillgängligt för installation och utvärdering, men utan stöd, via Cloudera Labs.)

    Santander meddelade nyligen att det är den första banken som lanserar röstbanksteknik som gör det möjligt för kunder att prata med sin SmartBank-app och fråga om deras kortutgifter. Plattformen bakom denna teknik är Cloudera, och arkitekturen för Spendlytics – som beskrivs i den här uppsättningen inlägg – fungerade som ritningen.

    James Kinley är en huvudlösningsarkitekt på Cloudera.

    Ian Buss är senior lösningsarkitekt på Cloudera.

    Pedro Boado är en Hadoop-ingenjör vid Santander (Isban) Storbritannien.

    Abel Fernandez Alfonso är en Hadoop-ingenjör vid Santander (Isban) UK.


  1. Hur ökar man prestanda för uppdateringsoperationen i Mongo?

  2. Lagra bilder i en MongoDB-databas

  3. Hur fyller man i ett underdokument i mongoose efter att ha skapat det?

  4. Gå igenom alla Mongo-samlingar och kör en fråga