
Dia duit gach duine. Táimid ag forbairt táirge le haghaidh anailíse tráchta as líne. Tá tasc ag an tionscadal a bhaineann le hanailís staitistiúil ar bhealaí cuairteoirí trasna na réigiún.
Mar chuid den tasc seo, is féidir le húsáideoirí ceisteanna córais den chineál seo a leanas a chur:
- cé mhéad cuairteoir a chuaigh ó cheantar "A" go ceantar "B";
- cé mhéad cuairteoirí a chuaigh ó cheantar "A" go dtí limistéar "B" trí limistéar "C" agus ansin trí limistéar "D";
- cé chomh fada a thóg sé ar chineál áirithe cuairteora taisteal ó cheantar “A” go ceantar “B”.
agus roinnt ceisteanna anailíseacha comhchosúla.
Is graf dírithe é gluaiseacht an chuairteora thar limistéir. Tar éis dom an Idirlíon a léamh, fuair mé amach go n-úsáidtear grafanna DBMS freisin le haghaidh tuarascálacha anailíse. Bhí fonn orm a fheiceáil conas a dhéileálfadh grafanna DBMS lena leithéid de cheisteanna (TL; DR; bocht).
Roghnaigh mé an DBMS a úsáid , mar ionadaí den scoth ar ghraif foinse oscailte DBMS, a bhraitheann ar chairn de theicneolaíochtaí aibí, ar cheart dóibh (i mo thuairim) tréithe oibríochtúla réasúnta a sholáthar dó:
- Inneall stórála BerkeleyDB, Apache Cassandra, Scylla;
- is féidir innéacsanna casta a stóráil i Lucene, Elasticsearch, Solr.
Scríobhann údair JanusGraph go bhfuil sé oiriúnach do OLTP agus OLAP araon.
D'oibrigh mé le BerkeleyDB, Apache Cassandra, Scylla agus ES, agus is minic a úsáidtear na táirgí seo inár gcórais, agus mar sin bhí mé dóchasach faoin DBMS graf seo a thástáil. Chinn mé go raibh sé aisteach BerkeleyDB a roghnú thar RocksDB, ach is dócha go bhfuil sé sin mar gheall ar riachtanais an idirbhirt. In aon chás, le haghaidh úsáid táirgí inscálaithe, moltar inneall a úsáid ar Cassandra nó Scylla.
Níor mheas mé Neo4j toisc go dteastaíonn leagan tráchtála de bhraisliú, is é sin, nach bhfuil an táirge foinse oscailte.
Deir DBMSanna grafa: “Más cosúil le graf é, caith mar ghraf é!” - áilleacht!
Ar dtús, tharraing mé graf, a rinneadh go díreach de réir chanóin ghraif DBMSanna:

Tá bunúsach ann Zone, atá freagrach as an gceantar. Dá ZoneStep bhaineann leis seo Zone, ansin tagraíonn sé dó. Ar bunúsach Area, ZoneTrack, Person Ná tabhair aird, is leis an bhfearann iad agus ní mheastar iad mar chuid den tástáil. San iomlán, bheadh cuma ar cheist chuardaigh slabhra le haghaidh struchtúr graf den sórt sin:
g.V().hasLabel('Zone').has('id',0).in_()
.repeat(__.out()).until(__.out().hasLabel('Zone').has('id',19)).count().next()Cad é an rud atá i Rúisis mar seo: aimsigh Crios le ID=0, tóg na rinn go léir óna dtéann ciumhais chuige (ZoneStep), stomp gan dul ar ais go dtí go bhfaighidh tú na ZoneSteps sin óna bhfuil imeall go dtí an Crios le ID=19, comhair an líon slabhraí den sórt sin.
Ní ligim orm go bhfuil aithne agam ar na castaí ar fad a bhaineann le cuardach ar ghraif, ach cruthaíodh an cheist seo bunaithe ar an leabhar seo ().
Lódáil mé 50 míle rian idir 3 agus 20 pointe ar fhad isteach i mbunachar sonraí graf JanusGraph ag baint úsáide as inneall BerkeleyDB, chruthaigh innéacsanna de réir .
Íosluchtaigh script python
from random import random
from time import time
from init import g, graph
if __name__ == '__main__':
points = []
max_zones = 19
zcache = dict()
for i in range(0, max_zones + 1):
zcache[i] = g.addV('Zone').property('id', i).next()
startZ = zcache[0]
endZ = zcache[max_zones]
for i in range(0, 10000):
if not i % 100:
print(i)
start = g.addV('ZoneStep').property('time', int(time())).next()
g.V(start).addE('belongs').to(startZ).iterate()
while True:
pt = g.addV('ZoneStep').property('time', int(time())).next()
end_chain = random()
if end_chain < 0.3:
g.V(pt).addE('belongs').to(endZ).iterate()
g.V(start).addE('goes').to(pt).iterate()
break
else:
zone_id = int(random() * max_zones)
g.V(pt).addE('belongs').to(zcache[zone_id]).iterate()
g.V(start).addE('goes').to(pt).iterate()
start = pt
count = g.V().count().next()
print(count)D’úsáideamar VM le 4 chroílár agus 16 GB RAM ar SSD. Imlonnaíodh JanusGraph leis an ordú seo:
docker run --name janusgraph -p8182:8182 janusgraph/janusgraph:latestSa chás seo, stóráiltear na sonraí agus na hinnéacsanna a úsáidtear le haghaidh cuardaigh mheaitseála beachta in BerkeleyDB. Tar éis dom an t-iarratas a tugadh níos luaithe a fhorghníomhú, fuair mé am cothrom le roinnt deicheanna soicind.
Trí na 4 scripteanna thuas a rith ag an am céanna, d'éirigh liom an DBMS a iompú isteach i bpumpkin le sruth cheerful de stacktrackes Java (agus is breá linn go léir a bheith ag léamh rian cruachta Java) sna logaí Docker.
Tar éis roinnt machnaimh a dhéanamh, chinn mé an graf-léaráid a shimpliú chuig na nithe seo a leanas:

Cinneadh a dhéanamh go mbeadh cuardach de réir tréithe aonáin níos tapúla ná cuardach de réir imill. Mar thoradh air sin, is iad seo a leanas m'iarratas:
g.V().hasLabel('ZoneStep').has('id',0).repeat(__.out().simplePath()).until(__.hasLabel('ZoneStep').has('id',19)).count().next()Cad é an rud i Rúisis mar seo: aimsigh ZoneStep le ID=0, stomp gan dul ar ais go dtí go bhfaighidh tú ZoneStep le ID=19, comhair líon na slabhraí den sórt sin.
Shimpligh mé an script lódála a thugtar thuas freisin ionas nach gcruthófaí naisc neamhriachtanacha, rud a chuir srian orm féin le tréithe.
Thóg sé roinnt soicind fós chun an t-iarratas a chur i gcrích, rud a bhí go hiomlán do-ghlactha dár tasc, ós rud é nach raibh sé oiriúnach ar chor ar bith chun críocha iarratais AdHoc de chineál ar bith.
Rinne mé iarracht JanusGraph a imscaradh ag baint úsáide as Scylla mar an cur i bhfeidhm Cassandra is tapúla, ach níor tháinig aon athruithe suntasacha feidhmíochta as seo freisin.
Mar sin, in ainneoin "go bhfuil cuma graf air", ní raibh mé in ann an graf DBMS a fháil chun é a phróiseáil go tapa. Glacaim go hiomlán leis go bhfuil rud éigin nach bhfuil a fhios agam agus gur féidir JanusGraph a dhéanamh chun an cuardach seo a dhéanamh i gcodán soicind, áfach, ní raibh mé in ann é a dhéanamh.
Ós rud é go raibh gá fós leis an bhfadhb a réiteach, thosaigh mé ag smaoineamh ar JOINs and Pivots of tables, rud nár spreag dóchas i dtéarmaí elegance, ach a d'fhéadfadh a bheith ina rogha go hiomlán inoibrithe go praiticiúil.
Úsáideann ár dtionscadal Apache ClickHouse cheana féin, agus mar sin chinn mé mo thaighde a thástáil ar an DBMS anailíseach seo.
Imscaradh ClickHouse ag baint úsáide as oideas simplí:
sudo docker run -d --name clickhouse_1
--ulimit nofile=262144:262144
-v /opt/clickhouse/log:/var/log/clickhouse-server
-v /opt/clickhouse/data:/var/lib/clickhouse
yandex/clickhouse-serverChruthaigh mé bunachar sonraí agus tábla mar seo ann:
CREATE TABLE
db.steps (`area` Int64, `when` DateTime64(1, 'Europe/Moscow') DEFAULT now64(), `zone` Int64, `person` Int64)
ENGINE = MergeTree() ORDER BY (area, zone, person) SETTINGS index_granularity = 8192Líon mé é le sonraí ag baint úsáide as an script seo a leanas:
from time import time
from clickhouse_driver import Client
from random import random
client = Client('vm-12c2c34c-df68-4a98-b1e5-a4d1cef1acff.domain',
database='db',
password='secret')
max = 20
for r in range(0, 100000):
if r % 1000 == 0:
print("CNT: {}, TS: {}".format(r, time()))
data = [{
'area': 0,
'zone': 0,
'person': r
}]
while True:
if random() < 0.3:
break
data.append({
'area': 0,
'zone': int(random() * (max - 2)) + 1,
'person': r
})
data.append({
'area': 0,
'zone': max - 1,
'person': r
})
client.execute(
'INSERT INTO steps (area, zone, person) VALUES',
data
)Ós rud é go dtagann ionsáigh i mbaisceanna, bhí an líonadh i bhfad níos tapúla ná mar a rinneadh do JanusGraph.
Tógadh dhá cheist ag baint úsáide as JOIN. Chun bogadh ó phointe A go pointe B:
SELECT s1.person AS person,
s1.zone,
s1.when,
s2.zone,
s2.when
FROM
(SELECT *
FROM steps
WHERE (area = 0)
AND (zone = 0)) AS s1 ANY INNER JOIN
(SELECT *
FROM steps AS s2
WHERE (area = 0)
AND (zone = 19)) AS s2 USING person
WHERE s1.when <= s2.whenChun dul trí 3 phointe:
SELECT s3.person,
s1z,
s1w,
s2z,
s2w,
s3.zone,
s3.when
FROM
(SELECT s1.person AS person,
s1.zone AS s1z,
s1.when AS s1w,
s2.zone AS s2z,
s2.when AS s2w
FROM
(SELECT *
FROM steps
WHERE (area = 0)
AND (zone = 0)) AS s1 ANY INNER JOIN
(SELECT *
FROM steps AS s2
WHERE (area = 0)
AND (zone = 3)) AS s2 USING person
WHERE s1.when <= s2.when) p ANY INNER JOIN
(SELECT *
FROM steps
WHERE (area = 0)
AND (zone = 19)) AS s3 USING person
WHERE p.s2w <= s3.whenTá cuma scanrúil ar na hiarratais, ar ndóigh; le húsáid fíor, ní mór duit úim gineadóir bogearraí a chruthú. Mar sin féin, oibríonn siad agus oibríonn siad go tapa. Críochnaítear an chéad agus an dara iarratas i níos lú ná 0.1 soicind. Seo sampla d’am feidhmithe na gceisteanna don chomhaireamh(*) ag dul trí 3 phointe:
SELECT count(*)
FROM
(
SELECT
s1.person AS person,
s1.zone AS s1z,
s1.when AS s1w,
s2.zone AS s2z,
s2.when AS s2w
FROM
(
SELECT *
FROM steps
WHERE (area = 0) AND (zone = 0)
) AS s1
ANY INNER JOIN
(
SELECT *
FROM steps AS s2
WHERE (area = 0) AND (zone = 3)
) AS s2 USING (person)
WHERE s1.when <= s2.when
) AS p
ANY INNER JOIN
(
SELECT *
FROM steps
WHERE (area = 0) AND (zone = 19)
) AS s3 USING (person)
WHERE p.s2w <= s3.when
┌─count()─┐
│ 11592 │
└─────────┘1 rows in set. Elapsed: 0.068 sec. Processed 250.03 thousand rows, 8.00 MB (3.69 million rows/s., 117.98 MB/s.)Nóta faoi IOPS. Nuair a bhí sonraí á gcomhlíonadh, ghin JanusGraph líon measartha ard IOPS (1000-1300 do cheithre snáithe daonra sonraí) agus bhí IOWAIT sách ard. Ag an am céanna, ghin ClickHouse ualach íosta ar an bhfochóras diosca.
Conclúid
Shocraigh muid ClickHouse a úsáid chun an cineál iarratais seo a sheirbhísiú. Is féidir linn i gcónaí fiosrúcháin a bharrfheabhsú trí úsáid a bhaint as radharcanna ábhartha agus comhthreomhaireacht trí shruth na hócáide a réamhphróiseáil ag baint úsáide as Apache Flink sula lódáiltear iad i ClickHouse.
Tá an fheidhmíocht chomh maith sin gur dócha nach mbeidh orainn fiú smaoineamh ar tháblaí pivoting go ríomhchláraithe. Roimhe seo, bhí orainn maighdeog sonraí a aisghabháil a fuarthas ó Vertica trí uaslódáil chuig Apache Parquet a dhéanamh.
Ar an drochuair, níor éirigh le hiarracht eile chun graf DBMS a úsáid. Ní bhfuair mé amach go raibh éiceachóras cairdiúil ag JanusGraph a d'fhág go raibh sé éasca dul suas chun dáta leis an táirge. Ag an am céanna, chun an freastalaí a chumrú, úsáidtear an bealach traidisiúnta Java, rud a fhágann go mbeidh daoine nach bhfuil eolach ar Java ag caoineadh deora fola:
host: 0.0.0.0
port: 8182
threadPoolWorker: 1
gremlinPool: 8
scriptEvaluationTimeout: 30000
channelizer: org.janusgraph.channelizers.JanusGraphWsAndHttpChannelizer
graphManager: org.janusgraph.graphdb.management.JanusGraphManager
graphs: {
ConfigurationManagementGraph: conf/janusgraph-cql-configurationgraph.properties,
airlines: conf/airlines.properties
}
scriptEngines: {
gremlin-groovy: {
plugins: { org.janusgraph.graphdb.tinkerpop.plugin.JanusGraphGremlinPlugin: {},
org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},
org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]},
org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/airline-sample.groovy]}}}}
serializers:
# GraphBinary is here to replace Gryo and Graphson
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1, config: { serializeResultToString: true }}
# Gryo and Graphson, latest versions
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
# Older serialization versions for backwards compatibility:
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { serializeResultToString: true }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0, config: {ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV2d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }}
- { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }}
processors:
- { className: org.apache.tinkerpop.gremlin.server.op.session.SessionOpProcessor, config: { sessionTimeout: 28800000 }}
- { className: org.apache.tinkerpop.gremlin.server.op.traversal.TraversalOpProcessor, config: { cacheExpirationTime: 600000, cacheMaxSize: 1000 }}
metrics: {
consoleReporter: {enabled: false, interval: 180000},
csvReporter: {enabled: false, interval: 180000, fileName: /tmp/gremlin-server-metrics.csv},
jmxReporter: {enabled: false},
slf4jReporter: {enabled: true, interval: 180000},
gangliaReporter: {enabled: false, interval: 180000, addressingMode: MULTICAST},
graphiteReporter: {enabled: false, interval: 180000}}
threadPoolBoss: 1
maxInitialLineLength: 4096
maxHeaderSize: 8192
maxChunkSize: 8192
maxContentLength: 65536
maxAccumulationBufferComponents: 1024
resultIterationBatchSize: 64
writeBufferHighWaterMark: 32768
writeBufferHighWaterMark: 65536
ssl: {
enabled: false}D’éirigh liom leagan BerkeleyDB de JanusGraph a “chur” de thaisme.
Tá an doiciméadú cam go leor i dtéarmaí innéacsanna, ós rud é go n-éilíonn bainistiú innéacsanna duit roinnt shamanism aisteach a dhéanamh i Groovy. Mar shampla, ní mór innéacs a chruthú trí chód a scríobh sa chonsól Gremlin (nach n-oibríonn, dála an scéil, as an mbosca). Ó dhoiciméadú oifigiúil JanusGraph:
graph.tx().rollback() //Never create new indexes while a transaction is active
mgmt = graph.openManagement()
name = mgmt.getPropertyKey('name')
age = mgmt.getPropertyKey('age')
mgmt.buildIndex('byNameComposite', Vertex.class).addKey(name).buildCompositeIndex()
mgmt.buildIndex('byNameAndAgeComposite', Vertex.class).addKey(name).addKey(age).buildCompositeIndex()
mgmt.commit()
//Wait for the index to become available
ManagementSystem.awaitGraphIndexStatus(graph, 'byNameComposite').call()
ManagementSystem.awaitGraphIndexStatus(graph, 'byNameAndAgeComposite').call()
//Reindex the existing data
mgmt = graph.openManagement()
mgmt.updateIndex(mgmt.getGraphIndex("byNameComposite"), SchemaAction.REINDEX).get()
mgmt.updateIndex(mgmt.getGraphIndex("byNameAndAgeComposite"), SchemaAction.REINDEX).get()
mgmt.commit()Afterword
Ar bhealach, is comparáid é an turgnamh thuas idir te agus bog. Má cheapann tú faoi, déanann DBMS graf oibríochtaí eile chun na torthaí céanna a fháil. Mar chuid de na tástálacha, áfach, rinne mé turgnamh freisin le hiarratas mar:
g.V().hasLabel('ZoneStep').has('id',0)
.repeat(__.out().simplePath()).until(__.hasLabel('ZoneStep').has('id',1)).count().next()a léiríonn achar siúil. Mar sin féin, fiú ar shonraí den sórt sin, léirigh an graf DBMS torthaí a chuaigh níos faide ná cúpla soicind... Tá sé seo, ar ndóigh, mar gheall ar an bhfíric go raibh cosáin mar 0 -> X -> Y ... -> 1, a sheiceáil an t-inneall graf freisin.
Fiú le fiosrúchán mar:
g.V().hasLabel('ZoneStep').has('id',0).out().has('id',1)).count().next()Ní raibh mé in ann freagra táirgiúil a fháil le ham próiseála níos lú ná soicind.
Is é morálta an scéil ná nach n-eascraíonn smaoineamh álainn agus samhaltú paradigmatic an toradh inmhianaithe, rud a léirítear le héifeachtacht i bhfad níos airde ag baint úsáide as sampla ClickHouse. Is frithphatrún soiléir é an cás úsáide a chuirtear i láthair san Airteagal seo le haghaidh DBMSanna graif, cé gur cosúil go bhfuil sé oiriúnach le haghaidh samhaltú ina bparaidím.
Foinse: will.com
