ЭкспСримСнт ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ примСнимости Π³Ρ€Π°Ρ„ΠΎΠ²ΠΎΠΉ Π‘Π£Π‘Π” JanusGraph для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π·Π°Π΄Π°Ρ‡ΠΈ поиска подходящих ΠΏΡƒΡ‚Π΅ΠΉ

ЭкспСримСнт ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ примСнимости Π³Ρ€Π°Ρ„ΠΎΠ²ΠΎΠΉ Π‘Π£Π‘Π” JanusGraph для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π·Π°Π΄Π°Ρ‡ΠΈ поиска подходящих ΠΏΡƒΡ‚Π΅ΠΉ

ВсСм ΠΏΡ€ΠΈΠ²Π΅Ρ‚. ΠœΡ‹ Ρ€Π°Π·Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ для Π°Π½Π°Π»ΠΈΠ·Π° ΠΎΡ„Ρ„Π»Π°ΠΉΠ½-Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ°. Π’ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Π΅ΡΡ‚ΡŒ Π·Π°Π΄Π°Ρ‡Π°, связанная со статистичСским Π°Π½Π°Π»ΠΈΠ·ΠΎΠΌ ΠΏΡƒΡ‚Π΅ΠΉ двиТСния посСтитСлСй ΠΏΠΎ областям.

Π’ Ρ€Π°ΠΌΠΊΠ°Ρ… этой Π·Π°Π΄Π°Ρ‡ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΠΈ ΠΌΠΎΠ³ΡƒΡ‚ Π·Π°Π΄Π°Π²Π°Ρ‚ΡŒ систСмС запросы ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π³ΠΎ Π²ΠΈΠ΄Π°:

  • сколько посСтитСлСй ΠΏΡ€ΠΎΡˆΠ»ΠΎ ΠΈΠ· области "A" Π² ΠΎΠ±Π»Π°ΡΡ‚ΡŒ "Π‘";
  • сколько посСтитСлСй ΠΏΡ€ΠΎΡˆΠ»ΠΎ ΠΈΠ· области "A" Π² ΠΎΠ±Π»Π°ΡΡ‚ΡŒ "Π‘" Ρ‡Π΅Ρ€Π΅Π· ΠΎΠ±Π»Π°ΡΡ‚ΡŒ "C", Π° Π·Π°Ρ‚Π΅ΠΌ Ρ‡Π΅Ρ€Π΅Π· ΠΎΠ±Π»Π°ΡΡ‚ΡŒ "Π”";
  • сколько Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ заняло ΠΏΡ€ΠΎΡ…ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ посСтитСля ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½ΠΎΠ³ΠΎ Ρ‚ΠΈΠΏΠ° ΠΈΠ· области "А" Π² ΠΎΠ±Π»Π°ΡΡ‚ΡŒ "Π‘".

ΠΈ Π΅Ρ‰Π΅ ряд ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Ρ… аналитичСских запросов.

Π”Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ посСтитСля ΠΏΠΎ областям прСдставляСт собой Π½Π°ΠΏΡ€Π°Π²Π»Π΅Π½Π½Ρ‹ΠΉ Π³Ρ€Π°Ρ„. ΠŸΠΎΡ‡ΠΈΡ‚Π°Π² ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Ρ‹, я ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ», Ρ‡Ρ‚ΠΎ Π³Ρ€Π°Ρ„ΠΎΠ²Ρ‹Π΅ Π‘Π£Π‘Π” ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ ΠΈ для аналитичСских ΠΎΡ‚Ρ‡Π΅Ρ‚ΠΎΠ². Π£ мСня появилось ΠΆΠ΅Π»Π°Π½ΠΈΠ΅ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ ΠΊΠ°ΠΊ Π±ΡƒΠ΄ΡƒΡ‚ ΡΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒΡΡ с ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹ΠΌΠΈ запросами Π³Ρ€Π°Ρ„ΠΎΠ²Ρ‹Π΅ Π‘Π£Π‘Π” (TL;DR; ΠΏΠ»ΠΎΡ…ΠΎ).

Π― Π²Ρ‹Π±Ρ€Π°Π» для использования Π‘Π£Π‘Π” JanusGraph, ΠΊΠ°ΠΊ Π²Ρ‹Π΄Π°ΡŽΡ‰Π΅Π³ΠΎΡΡ прСдставитСля Π³Ρ€Π°Ρ„ΠΎΠ²Ρ‹Ρ… open-source Π‘Π£Π‘Π”, которая опираСтся Π½Π° стСк Π·Ρ€Π΅Π»Ρ‹Ρ… Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΉ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ (ΠΏΠΎ ΠΌΠΎΠ΅ΠΌΡƒ мнСнию) Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Π»ΠΈ Π±Ρ‹ ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΡ‚ΡŒ Π΅ΠΉ ΠΏΡ€ΠΈΠ»ΠΈΡ‡Π½Ρ‹Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½Ρ‹Π΅ характСристики:

  • бэкСнд Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π° BerkeleyDB, Apache Cassandra, Scylla;
  • слоТныС индСксы ΠΌΠΎΠΆΠ½ΠΎ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ Π² Lucene, Elasticsearch, Solr.

Авторы JanusGraph ΠΏΠΈΡˆΡƒΡ‚, Ρ‡Ρ‚ΠΎ ΠΎΠ½Π° ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚ ΠΊΠ°ΠΊ для OLTP, Ρ‚Π°ΠΊ ΠΈ для OLAP.

Π― Ρ€Π°Π±ΠΎΡ‚Π°Π» с BerkeleyDB, Apache Cassandra, Scylla ΠΈ ES, ΠΊΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚Ρ‹ часто ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ Π² Π½Π°ΡˆΠΈΡ… систСмах, поэтому я с ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΌΠΎΠΌ смотрСл Π½Π° тСстированиС этой Π³Ρ€Π°Ρ„ΠΎΠ²ΠΎΠΉ Π‘Π£Π‘Π”. МнС показался странным Π²Ρ‹Π±ΠΎΡ€ BerkeleyDB, Π° Π½Π΅ RocksDB, Π½ΠΎ, вСроятно, это связано с трСбованиям ΠΊ транзакциям. Π’ любом случаС, для ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΡƒΠ΅ΠΌΠΎΠ³ΠΎ, ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΎΠ²ΠΎΠ³ΠΎ использования прСдлагаСтся ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ бэкСнд Π½Π° Cassandra ΠΈΠ»ΠΈ Scylla.

Neo4j я Π½Π΅ рассматривал, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ для кластСризации трСбуСтся коммСрчСская вСрсия, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ Π½Π΅ являСтся ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹ΠΌ.

Π“Ρ€Π°Ρ„ΠΎΠ²Ρ‹Π΅ Π‘Π£Π‘Π” говорят: "Если Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ выглядит ΠΊΠ°ΠΊ Π³Ρ€Π°Ρ„ β€” ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°ΠΉΡ‚Π΅ это ΠΊΠ°ΠΊ Π³Ρ€Π°Ρ„!" β€” красота!

Π‘Π½Π°Ρ‡Π°Π»Π° я нарисовал Π³Ρ€Π°Ρ„, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π²ΠΎΡ‚ прям ΠΏΠΎ ΠΊΠ°Π½ΠΎΠ½Π°ΠΌ Π³Ρ€Π°Ρ„ΠΎΠ²Ρ‹Ρ… Π‘Π£Π‘Π” сдСлан:

ЭкспСримСнт ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ примСнимости Π³Ρ€Π°Ρ„ΠΎΠ²ΠΎΠΉ Π‘Π£Π‘Π” JanusGraph для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π·Π°Π΄Π°Ρ‡ΠΈ поиска подходящих ΠΏΡƒΡ‚Π΅ΠΉ

Π•ΡΡ‚ΡŒ ΡΡƒΡ‰Π½ΠΎΡΡ‚ΡŒ Zone, ΠΎΡ‚Π²Π΅Ρ‡Π°ΡŽΡ‰Π°Ρ Π·Π° ΠΎΠ±Π»Π°ΡΡ‚ΡŒ. Если ZoneStep ΠΏΡ€ΠΈΠ½Π°Π΄Π»Π΅ΠΆΠΈΡ‚ этой Zone, Ρ‚ΠΎ ΠΎΠ½ Π½Π° Π½Π΅Π΅ ссылаСтся. На сущности Area, ZoneTrack, Person Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π΅ ΠΎΠ±Ρ€Π°Ρ‰Π°ΠΉΡ‚Π΅, ΠΎΠ½ΠΈ ΠΏΡ€ΠΈΠ½Π°Π΄Π»Π΅ΠΆΠ°Ρ‚ Π΄ΠΎΠΌΠ΅Π½Ρƒ ΠΈ Π² Ρ€Π°ΠΌΠΊΠ°Ρ… тСста Π½Π΅ Ρ€Π°ΡΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°ΡŽΡ‚ΡΡ. Π˜Ρ‚ΠΎΠ³ΠΎ, ΠΊ Ρ‚Π°ΠΊΠΎΠΉ Π³Ρ€Π°Ρ„ΠΎΠ²ΠΎΠΉ структурС запрос поиска Ρ†Π΅ΠΏΠΎΡ‡Π΅ΠΊ выглядСл Π±Ρ‹ ΠΊΠ°ΠΊ:

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

Π§Ρ‚ΠΎ Π½Π° русском ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ Ρ‚Π°ΠΊ: Π½Π°ΠΉΠ΄ΠΈ Zone с ID=0, возьми всС Π²Π΅Ρ€ΡˆΠΈΠ½Ρ‹, ΠΎΡ‚ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΊ Π½Π΅ΠΉ ΠΈΠ΄Π΅Ρ‚ Ρ€Π΅Π±Ρ€ΠΎ (ZoneStep), Ρ‚ΠΎΠΏΠ°ΠΉ Π±Π΅Π· Π²ΠΎΠ·Π²Ρ€Π°Ρ‚Π° Π½Π°Π·Π°Π΄ ΠΏΠΎΠΊΠ° Π½Π΅ найдСшь Ρ‚Π°ΠΊΠΈΠ΅ ZoneStep, ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΈΠ΄Π΅Ρ‚ Ρ€Π΅Π±Ρ€ΠΎ Π² Zone с ID=19, посчитай количСство Ρ‚Π°ΠΊΠΈΡ… Ρ†Π΅ΠΏΠΎΡ‡Π΅ΠΊ.

Π― Π½Π΅ ΠΏΡ€Π΅Ρ‚Π΅Π½Π΄ΡƒΡŽ Π½Π° Π·Π½Π°Π½ΠΈΠ΅ всС тонкостСй поиска Π½Π° Π³Ρ€Π°Ρ„Π°Ρ…, Π½ΠΎ Π΄Π°Π½Π½Ρ‹ΠΉ запрос Π±Ρ‹Π» сгСнСрирован Π½Π° основС Π²ΠΎΡ‚ этой ΠΊΠ½ΠΈΠ³ΠΈ (https://kelvinlawrence.net/book/Gremlin-Graph-Guide.html).

Π― Π·Π°Π³Ρ€ΡƒΠ·ΠΈΠ» 50 тысяч Ρ‚Ρ€Π΅ΠΊΠΎΠ² Π΄Π»ΠΈΠ½ΠΎΠΉ ΠΎΡ‚ 3 Π΄ΠΎ 20 Ρ‚ΠΎΡ‡Π΅ΠΊ Π² Π³Ρ€Π°Ρ„ΠΎΠ²ΡƒΡŽ Π±Π°Π·Ρƒ JanusGraph, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‰ΡƒΡŽ бэкСнд BerkeleyDB, создал индСксы согласно руководству.

Π‘ΠΊΡ€ΠΈΠΏΡ‚ для Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Π½Π° 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)

Использовалась VM c 4 ядрами ΠΈ 16 GB RAM Π½Π° SSD. JanusGraph Π±Ρ‹Π» Ρ€Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π²ΠΎΡ‚ Ρ‚Π°ΠΊΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹:

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

Π’ этом случаС Π΄Π°Π½Π½Ρ‹Π΅ ΠΈ индСксы, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ для поиска ΠΏΠΎ Ρ‚ΠΎΡ‡Π½ΠΎΠΌΡƒ совпадСнию хранятся Π² BerkeleyDB. Π’Ρ‹ΠΏΠΎΠ»Π½ΠΈΠ² запрос, ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½Ρ‹ΠΉ Ρ€Π°Π½Π΅Π΅, я ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ» врСмя Ρ€Π°Π²Π½ΠΎΠ΅ нСскольким дСсяткам сСкунд.

Запустив 4 Π²Ρ‹ΡˆΠ΅ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½Ρ‹Ρ… скрипта Π² ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒ, ΠΌΠ½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΠΏΡ€Π΅Π²Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ Π‘Π£Π‘Π” Π² Ρ‚Ρ‹ΠΊΠ²Ρƒ с вСсСлым ΠΏΠΎΡ‚ΠΎΠΊΠΎΠΌ стСктрСйсов Java (Π° ΠΌΡ‹ всС любим Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ стСктрСйсы Java) Π² Π»ΠΎΠ³Π°Ρ… Docker.

ΠŸΠΎΡ€Π°Π·ΠΌΡ‹ΡˆΠ»ΡΠ², я Ρ€Π΅ΡˆΠΈΠ» ΡƒΠΏΡ€ΠΎΡΡ‚ΠΈΡ‚ΡŒ схСму Π³Ρ€Π°Ρ„Π° Π΄ΠΎ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ:

ЭкспСримСнт ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ примСнимости Π³Ρ€Π°Ρ„ΠΎΠ²ΠΎΠΉ Π‘Π£Π‘Π” JanusGraph для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π·Π°Π΄Π°Ρ‡ΠΈ поиска подходящих ΠΏΡƒΡ‚Π΅ΠΉ

РСшив, Ρ‡Ρ‚ΠΎ поиск ΠΏΠΎ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Π°ΠΌ сущности Π±ΡƒΠ΄Π΅Ρ‚ быстрСС, Ρ‡Π΅ΠΌ поиск ΠΏΠΎ Ρ€Π΅Π±Ρ€Π°ΠΌ. Π’ ΠΈΡ‚ΠΎΠ³Π΅ ΠΌΠΎΠΉ запрос прСвратился Π² ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ:

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

Π§Ρ‚ΠΎ Π½Π° русском ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ Ρ‚Π°ΠΊ: Π½Π°ΠΉΠ΄ΠΈ ZoneStep с ID=0, Ρ‚ΠΎΠΏΠ°ΠΉ Π±Π΅Π· Π²ΠΎΠ·Π²Ρ€Π°Ρ‚Π° Π½Π°Π·Π°Π΄ ΠΏΠΎΠΊΠ° Π½Π΅ найдСшь ZoneStep с ID=19, посчитай количСство Ρ‚Π°ΠΊΠΈΡ… Ρ†Π΅ΠΏΠΎΡ‡Π΅ΠΊ.

Π‘ΠΊΡ€ΠΈΠΏΡ‚ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ, ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½Ρ‹ΠΉ Π²Ρ‹ΡˆΠ΅, я Ρ‚ΠΎΠΆΠ΅ упростил, для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ Π»ΠΈΡˆΠ½ΠΈΡ… связСй, ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΠ²ΡˆΠΈΡΡŒ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Π°ΠΌΠΈ.

Запрос всС Π΅Ρ‰Π΅ выполнялся нСсколько сСкунд, Ρ‡Ρ‚ΠΎ Π±Ρ‹Π»ΠΎ ΡΠΎΠ²Π΅Ρ€ΡˆΠ΅Π½Π½ΠΎ Π½Π΅ΠΏΡ€ΠΈΠ΅ΠΌΠ»Π΅ΠΌΠΎ для нашСй Π·Π°Π΄Π°Ρ‡ΠΈ, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ для Ρ†Π΅Π»Π΅ΠΉ AdHoc запросов ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠ³ΠΎ Π²ΠΈΠ΄Π° это совсСм Π½Π΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΠ»ΠΎ.

Π― ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Π» Ρ€Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ JanusGraph с использованиСм Scylla, ΠΊΠ°ΠΊ Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ быстрой Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Cassandra, Π½ΠΎ это Ρ‚ΠΎΠΆΠ΅ Π½Π΅ ΠΏΡ€ΠΈΠ²Π΅Π»ΠΎ ΠΊΠ°ΠΊΠΈΠΌ-Ρ‚ΠΎ сущСствСнным измСнСниям ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ.

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, нСсмотря Π½Π° Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ "это выглядит ΠΊΠ°ΠΊ Π³Ρ€Π°Ρ„", ΠΌΠ½Π΅ Π½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π·Π°ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Π³Ρ€Π°Ρ„ΠΎΠ²ΡƒΡŽ Π‘Π£Π‘Π” ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ это быстро. Π’ΠΏΠΎΠ»Π½Π΅ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°ΡŽ, Ρ‡Ρ‚ΠΎ я Ρ‡Π΅Π³ΠΎ-Ρ‚ΠΎ Π½Π΅ знаю ΠΈ ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ JanusGraph Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ этот поиск Π·Π° Π΄ΠΎΠ»ΠΈ сСкунды, ΠΎΠ΄Π½Π°ΠΊΠΎ, ΠΌΠ½Π΅ это Π½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ.

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Ρ€Π΅ΡˆΠΈΡ‚ΡŒ Π·Π°Π΄Π°Ρ‡Ρƒ всС Ρ€Π°Π²Π½ΠΎ Π±Ρ‹Π»ΠΎ Π½ΡƒΠΆΠ½ΠΎ, я Π½Π°Ρ‡Π°Π» Π΄ΡƒΠΌΠ°Ρ‚ΡŒ ΠΎ JOIN-Π°Ρ… ΠΈ Pivot-Π°Ρ… Ρ‚Π°Π±Π»ΠΈΡ†, Ρ‡Ρ‚ΠΎ Π½Π΅ Π²Π½ΡƒΡˆΠ°Π»ΠΎ ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΌΠ° с Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния изящности, Π½ΠΎ ΠΌΠΎΠ³Π»ΠΎ Π±Ρ‹Ρ‚ΡŒ Π²ΠΏΠΎΠ»Π½Π΅ Ρ€Π°Π±ΠΎΡ‡ΠΈΠΌ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠΌ Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ΅.

Π’ нашСм ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ ΡƒΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Apache ClickHouse, поэтому я Ρ€Π΅ΡˆΠΈΠ» ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ свои изыскания Π½Π° этой аналитичСской Π‘Π£Π‘Π”.

Π Π°Π·Π²Π΅Ρ€Π½ΡƒΠ» ClickHouse ΠΏΠΎ простому Ρ€Π΅Ρ†Π΅ΠΏΡ‚Ρƒ:

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

Π‘ΠΎΠ·Π΄Π°Π» Π² Π½Π΅ΠΌ Π‘Π” ΠΈ Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Π²ΠΈΠ΄Π°:

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

Π—Π°ΠΏΠΎΠ»Π½ΠΈΠ» Π΅Π΅ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ скриптом:

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
    )

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ вставки ΠΈΠ΄ΡƒΡ‚ Π±Π°Ρ‚Ρ‡Π°ΠΌΠΈ, Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π±Ρ‹Π»ΠΎ Π³ΠΎΡ€Π°Π·Π΄ΠΎ быстрСС, Ρ‡Π΅ΠΌ для JanusGraph.

Бконструировал Π΄Π²Π° запроса с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ JOIN. Для ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π° ΠΈΠ· Ρ‚ΠΎΡ‡ΠΊΠΈ A Π² Ρ‚ΠΎΡ‡ΠΊΡƒ Π‘:

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

Для ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π° Ρ‡Π΅Ρ€Π΅Π· 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

Запросы, ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ, выглядят довольно ΡΡ‚Ρ€Π°ΡˆΠ½ΠΎ, для Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ использования трСбуСтся Π΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΡƒΡŽ обвязку-Π³Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€. Однако, ΠΎΠ½ΠΈ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ ΠΈ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ быстро. И ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ ΠΈ Π²Ρ‚ΠΎΡ€ΠΎΠΉ запросы Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡŽΡ‚ΡΡ ΠΌΠ΅Π½Π΅Π΅ Ρ‡Π΅ΠΌ Π·Π° 0.1 сСк. Π’ΠΎΡ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ исполнСния запроса для count(*) ΠΏΡ€ΠΎΡ…ΠΎΠ΄Π° ΠΏΠΎ 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.)

Π—Π°ΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅ ΠΏΡ€ΠΎ IOPS. ΠŸΡ€ΠΈ Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ Π΄Π°Π½Π½Ρ‹Ρ…, JanusGraph Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Π» довольно высокоС количСство IOPS (1000-1300 для Ρ‡Π΅Ρ‚Ρ‹Ρ€Π΅Ρ… ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² заполнСния Π΄Π°Π½Π½Ρ‹ΠΌΠΈ), Π° IOWAIT Π±Ρ‹Π» довольно высоким. Π’ Ρ‚ΠΎ ΠΆΠ΅ врСмя, ClickHouse Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Π» ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½ΡƒΡŽ Π½Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ Π½Π° Π΄ΠΈΡΠΊΠΎΠ²ΡƒΡŽ подсистСму.

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

ΠœΡ‹ Ρ€Π΅ΡˆΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ClickHouse для обслуТивания запросов этого Ρ‚ΠΈΠΏΠ°. ΠœΡ‹ всСгда ΠΌΠΎΠΆΠ΅ΠΌ Π΅Ρ‰Π΅ большС ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ запросы, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΌΠ°Ρ‚Π΅Ρ€ΠΈΠ°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½Ρ‹Π΅ прСдставлСния ΠΈ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ, выполняя ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΡƒΡŽ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ΠΏΠΎΡ‚ΠΎΠΊΠ° событий с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Apache Flink ΠΏΠ΅Ρ€Π΅Π΄ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΎΠΉ ΠΈΡ… Π² ClickHouse.

ΠŸΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ Π½Π°ΡΡ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ…ΠΎΡ€ΠΎΡˆΠ°, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ, вСроятно, Π΄Π°ΠΆΠ΅ Π½Π΅ придСтся Π΄ΡƒΠΌΠ°Ρ‚ΡŒ ΠΎ pivot-Π°Ρ… Ρ‚Π°Π±Π»ΠΈΡ† ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½Ρ‹ΠΌΠΈ срСдствами. Π Π°Π½Π΅Π΅, Π½Π°ΠΌ ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΠ»ΠΎΡΡŒ Π΄Π΅Π»Π°Ρ‚ΡŒ pivot-Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ…, ΠΈΠ·Π²Π»Π΅ΠΊΠ°Π΅ΠΌΡ‹Ρ… ΠΈΠ· Vertica Ρ‡Π΅Ρ€Π΅Π· Π²Ρ‹Π³Ρ€ΡƒΠ·ΠΊΡƒ Π² Apache Parquet.

К соТалСнию, очСрСдная ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ° использования Π³Ρ€Π°Ρ„ΠΎΠ²ΠΎΠΉ Π‘Π£Π‘Π” Π½Π΅ ΡƒΠ²Π΅Π½Ρ‡Π°Π»Π°ΡΡŒ успСхом. Π― Π½Π΅ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ», Ρ‡Ρ‚ΠΎ Ρƒ JanusGraph Π΄Ρ€ΡƒΠΆΠ΅Π»ΡŽΠ±Π½Π°Ρ экосистСма, которая позволяСт быстро ΠΎΡΠ²ΠΎΠΈΡ‚ΡŒΡΡ с ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΎΠΌ. ΠŸΡ€ΠΈ этом для конфигурирования сСрвСра примСняСтся Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½Ρ‹ΠΉ Java-way, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ людСй с Java Π½Π΅ Π·Π½Π°ΠΊΠΎΠΌΡ‹Ρ… заставит ΠΏΠ»Π°ΠΊΠ°Ρ‚ΡŒ ΠΊΡ€ΠΎΠ²Π°Π²Ρ‹ΠΌΠΈ слСзами:

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}

МнС ΡƒΠ΄Π°Π»ΠΎΡΡŒ случайно "ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚ΡŒ" BerkeleyDB Π²Π΅Ρ€ΡΠΈΡŽ JanusGraph.

ДокумСнтация довольно кривая Π² части индСксов, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΏΡ€ΠΈ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠΈ индСксами трСбуСтся Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ довольно странныС ΡˆΠ°ΠΌΠ°Π½ΡΡ‚Π²Π° Π½Π° Groovy. НапримСр, созданиС индСкса Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ написания ΠΊΠΎΠ΄Π° Π² консоли Gremlin (ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅, кстати, Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΈΠ· ΠΊΠΎΡ€ΠΎΠ±ΠΊΠΈ). Из ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠΉ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ 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()

ПослСсловиС

Π’ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ смыслС ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½Ρ‹ΠΉ Π²Ρ‹ΡˆΠ΅ экспСримСнт β€” это сравнСниС Ρ‚Π΅ΠΏΠ»ΠΎΠ³ΠΎ с мягким. Если Π²Π΄ΡƒΠΌΠ°Ρ‚ΡŒΡΡ, Ρ‚ΠΎ графовая Π‘Π£Π‘Π” выполняСт Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ для получСния Ρ‚Π΅Ρ… ΠΆΠ΅ самых Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ². Однако, Π² Ρ€Π°ΠΌΠΊΠ°Ρ… тСстов я ΠΏΡ€ΠΎΠ²ΠΎΠ΄ΠΈΠ» ΠΈ экспСримСнт с запросом Π²ΠΈΠ΄Π°:

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

ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΎΡ‚Ρ€Π°ΠΆΠ°Π΅Ρ‚ ΡˆΠ°Π³ΠΎΠ²ΡƒΡŽ Π΄ΠΎΡΡ‚ΡƒΠΏΠ½ΠΎΡΡ‚ΡŒ. Однако ΠΈ Π½Π° Ρ‚Π°ΠΊΠΈΡ… Π΄Π°Π½Π½Ρ‹Ρ… графовая Π‘Π£Π‘Π” ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π»Π° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π²Ρ‹Ρ…ΠΎΠ΄ΠΈΠ» Π·Π° ΠΏΡ€Π΅Π΄Π΅Π»Ρ‹ Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… сСкунд… Π­Ρ‚ΠΎ, ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ, ΠΆΠ΅ связано с Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ Π±Ρ‹Π»ΠΈ ΠΏΡƒΡ‚ΠΈ Π²ΠΈΠ΄Π° 0 -> X -> Y ... -> 1, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π³Ρ€Π°Ρ„ΠΎΠ²Ρ‹ΠΉ Π΄Π²ΠΈΠΆΠΎΠΊ Ρ‚ΠΎΠΆΠ΅ провСрял.

Π”Π°ΠΆΠ΅ для запроса Π²ΠΈΠ΄Π°:

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

ΠΌΠ½Π΅ Π½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΡ‚Π²Π΅Ρ‚ с Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ мСньшС сСкунды.

ΠœΠΎΡ€Π°Π»ΡŒ басни Ρ‚Π°ΠΊΠΎΠ²Π°, Ρ‡Ρ‚ΠΎ красивая идСя ΠΈ ΠΏΠ°Ρ€Π°Π΄ΠΈΠ³ΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅ Π²Π΅Π΄ΡƒΡ‚ ΠΊ ΠΆΠ΅Π»Π°Π΅ΠΌΠΎΠΌΡƒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρƒ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ со Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π±ΠΎΠ»Π΅Π΅ высокой ΡΡ„Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΎΡΡ‚ΡŒΡŽ дСмонстрируСтся Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ClickHouse. ΠŸΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½Ρ‹ΠΉ Π² этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ использования β€” явный Π°Π½Ρ‚ΠΈΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½ для Π³Ρ€Π°Ρ„ΠΎΠ²Ρ‹Ρ… Π‘Π£Π‘Π”, хотя ΠΈ выглядит ΠΊΠ°ΠΊ подходящий для модСлирования Π² ΠΈΡ… ΠΏΠ°Ρ€Π°Π΄ΠΈΠ³ΠΌΠ΅.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com