Fanandramana mitsapa ny fampiharana ny DBMS grafika JanusGraph amin'ny famahana ny olan'ny fitadiavana lalana mety.

Fanandramana mitsapa ny fampiharana ny DBMS grafika JanusGraph amin'ny famahana ny olan'ny fitadiavana lalana mety.

Salama daholo. Mamolavola vokatra ho an'ny famakafakana fifamoivoizana ivelan'ny aterineto izahay. Ny tetikasa dia manana asa mifandraika amin'ny famakafakana statistika ny lalan'ny mpitsidika manerana ny faritra.

Ao anatin'ity asa ity, ny mpampiasa dia afaka mametraka fanontaniana momba ny rafitra amin'ireto karazana manaraka ireto:

  • firy ny mpitsidika nandalo tao amin'ny faritra "A" mankany amin'ny faritra "B";
  • firy ny mpitsidika nandalo avy amin'ny faritra "A" mankany amin'ny faritra "B" amin'ny faritra "C" ary avy eo amin'ny faritra "D";
  • hafiriana no nandehanan'ny karazana mpitsidika iray avy amin'ny faritra "A" mankany amin'ny faritra "B".

ary fanontaniana famakafakana maromaro mitovy.

Ny fihetsiky ny mpitsidika manerana ny faritra dia kisary mivantana. Rehefa avy namaky ny Internet aho dia hitako fa ny DBMS dia ampiasaina amin'ny tatitra famakafakana. Naniry ny hahita ny fomba hiatrehana ny fanontaniana toy izany ny DBMS amin'ny grafika (TL; DR; ratsy).

Nisafidy ny hampiasa ny DBMS aho JanusGraph, amin'ny maha-solontena miavaka amin'ny DBMS open-source amin'ny grafika, izay miantehitra amin'ny teknolojia matotra, izay (raha ny hevitro) dia tokony hanome azy io amin'ny toetran'ny fiasa mendrika:

  • Backend fitahirizana BerkeleyDB, Apache Cassandra, Scylla;
  • azo tehirizina ao Lucene, Elasticsearch, Solr ny indeksa sarotra.

Ny mpanoratra ny JanusGraph dia manoratra fa mety amin'ny OLTP sy OLAP izy io.

Niara-niasa tamin'ny BerkeleyDB, Apache Cassandra, Scylla ary ES aho, ary matetika ireo vokatra ireo no ampiasaina ao amin'ny rafitra misy anay, ka nanantena ny hanandrana ity DBMS ity aho. Hitako fa hafahafa ny misafidy BerkeleyDB noho ny RocksDB, saingy mety noho ny fepetra takian'ny fifanakalozana izany. Na izany na tsy izany, ho an'ny fampiasana vokatra azo ekena, dia soso-kevitra ny hampiasa backend amin'ny Cassandra na Scylla.

Tsy nieritreritra ny Neo4j aho satria ny clustering dia mitaky dikan-teny ara-barotra, izany hoe tsy loharano misokatra ny vokatra.

Graph DBMSs dia miteny hoe: "Raha toa ka sahala amin'ny grafika ilay izy, ataovy toy ny graph!" - hatsarana!

Voalohany, nanao sary aho, izay natao araka ny kanon'ny DBMSs grafika:

Fanandramana mitsapa ny fampiharana ny DBMS grafika JanusGraph amin'ny famahana ny olan'ny fitadiavana lalana mety.

Misy essence Zone, tompon'andraikitra amin'ny faritra. RAHA ZoneStep an'ity Zone, dia resahiny izany. Amin'ny maha Area, ZoneTrack, Person Aza mihaino, an'ny sehatra izy ireo ary tsy raisina ho anisan'ny fitsapana. Amin'ny fitambarany, ny fangatahana fikarohana rojo ho an'ny firafitry ny grafika toy izany dia toa:

g.V().hasLabel('Zone').has('id',0).in_()
       .repeat(__.out()).until(__.out().hasLabel('Zone').has('id',19)).count().next()

Ny amin'ny teny rosiana dia toy izao: mitadiava Zone misy ID=0, alaivo ny vertices rehetra izay alehan'ny sisiny iray (ZoneStep), manitsaka tsy miverina mandra-pahitanao ireo ZoneSteps izay misy sisiny mankany amin'ny Zone misy ID=19, isao ny isan'ny rojo toy izany.

Tsy mody mahalala ny saro-takarina rehetra amin'ny fikarohana amin'ny grafika aho, fa ity fanontaniana ity dia noforonina avy amin'ity boky ity (https://kelvinlawrence.net/book/Gremlin-Graph-Guide.html).

Nametraka lalamby 50 arivo aho tamin'ny teboka 3 ka hatramin'ny 20 ny halavany tao anaty angon-drakitra grafika JanusGraph tamin'ny fampiasana ny backend BerkeleyDB, namorona index araka ny mpitarika.

Python download script:


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)

Nampiasa VM misy cores 4 sy RAM 16 GB amin'ny SSD izahay. JanusGraph dia napetraka tamin'ny fampiasana ity baiko ity:

docker run --name janusgraph -p8182:8182 janusgraph/janusgraph:latest

Amin'ity tranga ity dia voatahiry ao amin'ny BerkeleyDB ny angona sy ny tondro ampiasaina amin'ny fikarohana mifanandrify tsara. Rehefa vitako ilay fangatahana nomena teo aloha, dia nahazo fotoana mitovy amin'ny segondra am-polony aho.

Amin'ny alΓ lan'ny fampandehanana ireo sora-baventy 4 etsy ambony dia vitako ny namadika ny DBMS ho lasa voatavo miaraka amin'ny renirano falifaly Java stacktraces (ary tia mamaky stacktraces Java isika rehetra) ao amin'ny logs Docker.

Taorian'ny fieritreretana dia nanapa-kevitra ny hanatsotra ny diagrama aho amin'ireto manaraka ireto:

Fanandramana mitsapa ny fampiharana ny DBMS grafika JanusGraph amin'ny famahana ny olan'ny fitadiavana lalana mety.

Manapa-kevitra fa ny fikarohana amin'ny toetran'ny enti-manana dia ho haingana kokoa noho ny fikarohana amin'ny sisiny. Vokatr'izany dia nivadika ho toy izao ny fangatahako:

g.V().hasLabel('ZoneStep').has('id',0).repeat(__.out().simplePath()).until(__.hasLabel('ZoneStep').has('id',19)).count().next()

Inona no atao amin'ny teny Rosiana: tadiavo ny ZoneStep miaraka amin'ny ID=0, manitsaka tsy miverina mandra-pahitanao ny ZoneStep misy ID=19, isao ny isan'ny rojo toy izany.

Nohamafisiko ihany koa ny script famenoana nomena etsy ambony mba tsy hamoronana fifandraisana tsy ilaina, mametra ny tenako amin'ny toetra.

Mbola naharitra segondra maromaro ny fangatahana, izay tsy azo ekena tanteraka amin'ny asantsika, satria tsy mety amin'ny fangatahana AdHoc na inona na inona.

Nanandrana nampiasa an'i JanusGraph aho tamin'ny fampiasana Scylla ho fampiharana Cassandra haingana indrindra, saingy tsy nitondra fiovana lehibe teo amin'ny fampisehoana izany.

Ka na dia eo aza ny hoe "tahaka ny grafika" dia tsy azoko ny DBMS mba handaminana azy haingana. Mihevitra tanteraka aho fa misy zavatra tsy fantatro ary azo atao ny JanusGraph hanaovana an'io fikarohana io ao anatin'ny segondra vitsy, saingy tsy vitako izany.

Koa satria mbola mila voavaha ny olana, dia nanomboka nieritreritra momba ny JOINs sy Pivots of tables aho, izay tsy nanentana ny fanantenana amin'ny lafiny kanto, fa mety ho safidy azo ampiasaina tanteraka amin'ny fampiharana.

Efa mampiasa Apache ClickHouse ny tetikasanay, ka nanapa-kevitra ny hanandrana ny fikarohana nataoko momba ity DBMS analytical ity aho.

Nametraka ClickHouse tamin'ny fomba tsotra:

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-server

Namorona database sy tabilao ao aminy toy izao aho:

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 = 8192

Nofenoiko angon-drakitra tamin'ny fampiasana ity script manaraka ity:

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
    )

Satria tonga maromaro ny fampidirana dia haingana kokoa noho ny an'i JanusGraph ny famenoana.

Nanamboatra fanontaniana roa tamin'ny fampiasana JOIN. Raha hifindra avy amin'ny teboka A mankany amin'ny teboka 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.when

Mandalo teboka 3:

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.when

Ny fangatahana, mazava ho azy, dia toa mampatahotra tokoa; ho an'ny tena fampiasana dia mila mamorona harness mpamorona rindrambaiko ianao. Na izany aza, miasa izy ireo ary miasa haingana. Samy vita ao anatin'ny 0.1 segondra latsaka ny fangatahana voalohany sy faharoa. Ity misy ohatra amin'ny fotoana fanatanterahana ny fangatahana ho an'ny isa (*) mandalo teboka 3:

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.)

Fanamarihana momba ny IOPS. Rehefa mameno angon-drakitra, JanusGraph dia namokatra IOPS (1000-1300 ho an'ny kofehy efatra mponina) ary IOWAIT dia avo be. Nandritra izany fotoana izany, ny ClickHouse dia niteraka entana kely indrindra tamin'ny subsystem kapila.

famaranana

Nanapa-kevitra ny hampiasa ClickHouse izahay mba hanompoana an'io karazana fangatahana io. Azontsika atao ny manatsara kokoa ny fanontaniana amin'ny fampiasana fomba fijery mivaingana sy fampitoviana amin'ny alΓ lan'ny fanodinana mialoha ny fizotran'ny hetsika amin'ny alΓ lan'ny Apache Flink alohan'ny hampidirana azy ireo ao amin'ny ClickHouse.

Tena tsara ny zava-bita ka mety tsy hieritreritra momba ny fanodinkodinana tabilao amin'ny programa. Teo aloha dia tsy maintsy nanao pivots ny angon-drakitra nalaina tao amin'ny Vertica tamin'ny alΓ lan'ny fampiakarana any amin'ny Apache Parquet izahay.

Indrisy anefa fa tsy nahomby ny andrana iray hafa hampiasa DBMS grafika. Tsy nahita an'i JanusGraph aho manana tontolo iainana sariaka izay nanamora ny fahazoana haingana ny vokatra. Amin'izay fotoana izay ihany koa, mba handrindrana ny mpizara dia ampiasaina ny fomba nentim-paharazana Java, izay hahatonga ny olona tsy zatra Java hitomany ranomason-dra:

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}

Nahavita "nametraka" tsy nahy ny dikan-tenin'ny JanusGraph BerkeleyDB aho.

Ny antontan-taratasy dia somary miolakolaka amin'ny lafiny fanondroana, satria ny fitantanana ny indeks dia mitaky anao hanao shamanisma hafahafa ao Groovy. Ohatra, ny famoronana index dia tsy maintsy atao amin'ny fanoratana kaody ao amin'ny console Gremlin (izay, raha ny marina, dia tsy mandeha ivelan'ny boaty). Avy amin'ny antontan-taratasy ofisialy 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

Amin'ny lafiny iray, ny fanandramana etsy ambony dia fampitahana ny mafana sy malefaka. Raha mieritreritra an'izany ianao, dia manao asa hafa ny DBMS iray mba hahazoana vokatra mitovy. Na izany aza, ao anatin'ny fitsapana dia nanao fanandramana niaraka tamin'ny fangatahana toy ny:

g.V().hasLabel('ZoneStep').has('id',0)
    .repeat(__.out().simplePath()).until(__.hasLabel('ZoneStep').has('id',1)).count().next()

izay taratry ny halaviran-dalana. Na izany aza, na dia amin'ny angon-drakitra toy izany aza, ny grafika DBMS dia nampiseho valiny mihoatra ny segondra vitsy... Mazava ho azy fa noho ny fisian'ny lalana toy ny 0 -> X -> Y ... -> 1, izay nojeren'ny motera graph ihany koa.

Na dia ho an'ny fanontaniana toy ny:

g.V().hasLabel('ZoneStep').has('id',0).out().has('id',1)).count().next()

Tsy afaka nahazo valiny mamokatra aho tamin'ny fotoana fanodinana iray segondra latsaka.

Ny fitondran-tenan'ny tantara dia ny hevitra tsara tarehy sy ny modely paradigmatika dia tsy mitondra any amin'ny vokatra tadiavina, izay aseho amin'ny fahombiazana ambony kokoa amin'ny fampiasana ny ohatra ClickHouse. Ny tranga fampiasa aseho ato amin'ity lahatsoratra ity dia anti-pattern mazava ho an'ny grafika DBMSs, na dia toa mety ho modely amin'ny paradigmany aza.

Source: www.habr.com

Add a comment