I jobbsammenheng jobber jeg mye med modellering av data i RDF.
Vi har lenge lagret RDF-grafer som filer på disk, men har den siste tida undersøkt flere RDF-databaser.
Felles for de fleste grafdatabaser er at de trives best i RAM.
Store installasjoner som skryter av å lagre milliarder av tripler består enten av servere med masse minne, eller av mange maskiner i et kluster som tilsammen innehar mye minne – da snakker vi størrelsesorden hundrevis av GB RAM.
Tidligere tester av RDF-databaser
Vi har tidligere testet Jena Fuseki og OpenLink Virtuoso, men begge har sine irriterende problemer.
Jena Fuseki TDB
Jena Fuseki med TDB blir fort ubrukelig når databasen blir større enn Java heap-size, og den feiler ofte med OutOfMemoryError.
Testet med 4 GB Java-heap.
OpenLink Virtuoso
OpenLink Virtuoso har vi testet i versjon 6 og versjon 7.
Versjon 6, som følger med i pakkesystemet til Ubuntu, støttet ikke SPARQL UPDATE.
Versjon 7 feiler når vi prøver å laste inn grafer som inneholder mange blank nodes, selv om vi klarte å hacke oss til en løsning ved å splitte opp insertene i mindre deler.
Generelt sett har Virtuoso flere irriterende problemer, blant annet at den ikke forstår “INSERT DATA” fra SPARQL UPDATE – her måtte vi bruke “INSERT INTO”.
Bigdata
Jeg har i noen dager testet Bigdata, en server for lagring og spørring over RDF-data.
Bigdata-serveren er en Java-applikasjon som kjører i en standard Servlet applikasjons-container.
Innlasting av data
Datagrunnlaget er oppdelt i 525K (525.000) grafer – dokumenter i RDF/TURTLE-format.
Grafene inneholder mange blank nodes, ressurser som ikke er navngitte og globalt identifisérbare med IRI.
Grunnen til at vi bruker blank nodes er fordi vi konstruerer ressurser sammensatt av data fra kildesystemer som ikke tilordner ID-er til disse konseptene.
Dersom vi skulle konstruert ID-er for disse ressursene må vi bruke mye energi og mange kodelinjer for å holde de ID-ene stabile, for å tilordne de samme ID-ene ved neste eksport fra kildesystemet – da er det mye enklere å konstruerer blank nodes.
Innlasting av data ble gjort graf for graf i form av SPARQL UPDATE-meldinger over HTTP, hver delt i to seksjoner – først sletting av eksisterende tripler i grafen, for så innsetting av nye tripler i samme graf.
Sletting av grafer er en viktig funksjon i vårt tilfelle, da vi ønsker å bytte ut alle tripler fra en eventuell gammel versjon av grafen med tripler fra en ny versjon.
Utskiftingen av hele grafer ønsker vi å gjøre som en atomisk operasjon for å unngå at en graf fremstår som tom før nye data lastes inn.
Serveroppsett
Jeg startet med å kjøre Bigdata i en Jetty-container på en server med 4 GB RAM og 100 GB disk – dbdev01.
Med unntak av lokasjonen til journal-fila (databasen til Bigdata), kjørte jeg med standardinnstillinger og 2 GB Java-heap.
Datamengder
Mot denne lastet jeg inn ca 450K (450.000) grafer.
Dette utgjorde i overkant av 84M (84.000.000) tripler.
Journal-fila vokste til 14 GB.
Ytelse ved innlasting av data
I starten klarte jeg å laste inn ca 10 grafer per sekund.
Det er ikke spesielt imponerende hastighet, men siden dette var en test så lot jeg prosessen fortsette.
Etter ett døgn var hastigheten nede i 2-3 grafer per sekund.
Etter to døgn var hastigheten nede i 1-2 grafer per sekund.
Å gjøre SPARQL-spørringer mot databasen samtidig som importen pågikk var bortimot ubrukelig, selv enkle spørringer som å hente ut navn på 60 ressurser.
Samtidig sakt importhastigheten ned til ca 1 graf per 10 sekunder.
Da var 450.000 grafer importert, 85% av datasettet vårt, som for tiden øker med ca 100.000 grafer per år.
Dette skalerer ikke.
Bestemte meg for å avbryte importen.
Ny server med mer minne
Jeg fikk en ny server til rådighet, med 32 GB RAM og 100 GB disk – dbdev02.
Serveren kjørte allerede noen tjenester, så jeg hadde ca 28 GB RAM ledig.
Ellers var det lite last på serveren.
Jeg kopierte journal-fila på 14GB fra dbdev01 til dbdev02, og starta opp Bigdata på begge servere, samme konfigurasjon, og med 1 GB Java heap.
Ytelsestester spørringer
Gjorde samme tester mot begge serverne.
Test #1
Første spørringen mot serveren er en SPARQL-spørring med datofiltrering og dybde på 5 (Vedlegg 1).
Resultatet fra spørringen er ca 80K (80.000) løsninger, i SPARQL RESULTS XML-format på 75 MB.
Ved kald, nyoppstartet Bigdata var dbdev02 (24s) vesentlig raskere enn dbdev01 (2m31s).
Ved gjentatte utføringer av samme spørring var begge servere nokså like raske, også etter kjøring av andre relativt enklere spørringer i mellomtiden.
Test #2
En annen spørring som ga store utslag var med dybde på 4, uten filtrering og med LIMIT 100.000 (Vedlegg 2).
Resultatet fra spørringen er ca 53K (53.000) løsninger, i SPARQL RESULTS XML-format på 22 MB.
Resultatet var altså mindre enn den angitte begrensningen på 100K.
dbdev02 (1m19s) var mye raskere enn dbdev01 (12m23s).
Dette var med varm database.
Innlasting av data mot 32 GB minne
En ny test av import av data, som inkluderer overskriving av gamle grafer, denne gang gjort mot dbdev02 med 32 GB minne, viser en importhastighet på ca 5 grafer i sekundet.
Analyse av testresultatene
Den største forskjellen er rett etter Bigdata nettopp har starta opp.
Mange av spørringene som ga store utslag etter kald oppstart returnerte omtrent like raskt etter gjentagende spørringer.
Dataene i resultatet blir muligens cachet et sted.
Minne
dbdev01 har etter noen spørringer lite ledig RAM, mens dbdev02 har mye ledig RAM.
Det kan tyde mot at Bigdata har det bedre når mye av datafila ligger i minnet.
Diskytelse
Hjelper nok også om disken er rask.
Virker som at disken er raskere på dbdev02 enn dbdev01.
En test lokalt på hver server viser at kopiering av fil på 1 GB tok 14.5s på dbdev01 og 6.2s på dbdev02.
Ingen prosesser på noen av boksene som bruker nevneverdig mye disk-IO når Bigdata idler.
Swapping
Kanskje swapping på dbdev01 kan være årsak, selv om swap-aktiviteten for tiden er lav.
Munin-graf over minne for dbdev02 viser en økning på 16GB i cache (fil-cache) etter oppstart av Bigdata.
Null swapping.
Munin-graf over minne for dbdev01 er fullstendig mongo, går opp og ned som en jojo i den perioden Bigdata har kjørt (2 dager).
Viser også mye swapping, spesielt under inserts.
Konklusjon
Bigdata trives veldig godt med mye RAM og bruker operativsystemets filcache for å få rask aksess til dataene.
Bigdata feiler ikke like dramatisk som Jena Fuseki TDB når minnet går fullt.
Selv om vi endte opp med å teste Bigdata med mer minne enn vi testet med Jena Fuseki TDB, så taklet Bigdata med 4 GB minne mye større datasett enn Jena Fuseki TDB klarte med det samme.
Det er ikke så viktig med stor Java heap under innlasting av data.
Heap-størrelsen øker når det gjøres spørringer, så større heap har kanskje en positiv effekt ved mange og tunge spørringer.
Den nye innlastingstesten viser at Bigdata er mye raskere på innlasting av data dersom den har nok minne til databasen.
Vedlegg
Vedlegg 1: SPARQL Test #1
SPARQL-spørring som lister ut avspilt musikk i en tidsperiode på én måned:
PREFIX dct: <http://purl.org/dc/terms/>
PREFIX ebuccdm: <http://www.ebu.ch/metadata/ontologies/ebuccdm#>
PREFIX digas: <http://id.nrk.no/2013/digas/class/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT * WHERE {
GRAPH ?g {
?part a digas:Music .
?timeline ebuccdm:hasTimelineTrackPart ?part .
?prog ebuccdm:hasTimelineTrack ?timeline .
?trans ebuccdm:playsOut ?prog .
?part dct:title ?partTitle .
?prog dct:title ?progTitle .
?trans ebuccdm:publicationStartDateTime ?startTime .
FILTER ( ?startTime >= "2014-01-01T00:00:00+01:00"^^xsd:dateTime ) .
FILTER ( ?startTime < "2014-02-01T00:00:00+01:00"^^xsd:dateTime ) .
}
}
ORDER BY ?startTime
Vedlegg 2: SPARQL Test #2
SPARQL-spørring som lister ut medvirkende på programmer med en begrensning på 100.000 resultater:
PREFIX nrk: <http://gluon.nrk.no/dataordbok.xml#>
PREFIX dct: <http://purl.org/dc/terms/>
PREFIX ebuccdm: <http://www.ebu.ch/metadata/ontologies/ebuccdm#>
SELECT ?obj ?title ?contactName ?roleName
WHERE {
?obj a nrk:programme .
?obj dct:title ?title .
?obj ebuccdm:hasContributor ?contributor .
?contributor ebuccdm:contactName ?contactName .
?contributor ebuccdm:hasRole ?role .
?role ebuccdm:roleName ?roleName .
} LIMIT 100000