ಸೂಕ್ತವಾದ ಮಾರ್ಗಗಳನ್ನು ಹುಡುಕುವ ಸಮಸ್ಯೆಯನ್ನು ಪರಿಹರಿಸಲು JanusGraph ಗ್ರಾಫ್ DBMS ನ ಅನ್ವಯಿಸುವಿಕೆಯನ್ನು ಪರೀಕ್ಷಿಸುವ ಪ್ರಯೋಗ

ಸೂಕ್ತವಾದ ಮಾರ್ಗಗಳನ್ನು ಹುಡುಕುವ ಸಮಸ್ಯೆಯನ್ನು ಪರಿಹರಿಸಲು JanusGraph ಗ್ರಾಫ್ DBMS ನ ಅನ್ವಯಿಸುವಿಕೆಯನ್ನು ಪರೀಕ್ಷಿಸುವ ಪ್ರಯೋಗ

ಎಲ್ಲರಿಗು ನಮಸ್ಖರ. ನಾವು ಆಫ್‌ಲೈನ್ ಟ್ರಾಫಿಕ್ ವಿಶ್ಲೇಷಣೆಗಾಗಿ ಉತ್ಪನ್ನವನ್ನು ಅಭಿವೃದ್ಧಿಪಡಿಸುತ್ತಿದ್ದೇವೆ. ಪ್ರದೇಶಗಳಾದ್ಯಂತ ಸಂದರ್ಶಕರ ಮಾರ್ಗಗಳ ಅಂಕಿಅಂಶಗಳ ವಿಶ್ಲೇಷಣೆಗೆ ಸಂಬಂಧಿಸಿದ ಕಾರ್ಯವನ್ನು ಯೋಜನೆಯು ಹೊಂದಿದೆ.

ಈ ಕಾರ್ಯದ ಭಾಗವಾಗಿ, ಬಳಕೆದಾರರು ಈ ಕೆಳಗಿನ ಪ್ರಕಾರದ ಸಿಸ್ಟಮ್ ಪ್ರಶ್ನೆಗಳನ್ನು ಕೇಳಬಹುದು:

  • "A" ಪ್ರದೇಶದಿಂದ "B" ಪ್ರದೇಶಕ್ಕೆ ಎಷ್ಟು ಸಂದರ್ಶಕರು ಹಾದುಹೋದರು;
  • "A" ಪ್ರದೇಶದಿಂದ "B" ಪ್ರದೇಶಕ್ಕೆ "C" ಪ್ರದೇಶದ ಮೂಲಕ ಮತ್ತು ನಂತರ "D" ಪ್ರದೇಶದ ಮೂಲಕ ಎಷ್ಟು ಸಂದರ್ಶಕರು ಹಾದುಹೋದರು;
  • "A" ಪ್ರದೇಶದಿಂದ "B" ಪ್ರದೇಶಕ್ಕೆ ಪ್ರಯಾಣಿಸಲು ನಿರ್ದಿಷ್ಟ ರೀತಿಯ ಸಂದರ್ಶಕರು ಎಷ್ಟು ಸಮಯ ತೆಗೆದುಕೊಂಡರು.

ಮತ್ತು ಇದೇ ರೀತಿಯ ಹಲವಾರು ವಿಶ್ಲೇಷಣಾತ್ಮಕ ಪ್ರಶ್ನೆಗಳು.

ಪ್ರದೇಶಗಳಾದ್ಯಂತ ಸಂದರ್ಶಕರ ಚಲನೆಯು ನಿರ್ದೇಶಿತ ಗ್ರಾಫ್ ಆಗಿದೆ. ಇಂಟರ್ನೆಟ್ ಅನ್ನು ಓದಿದ ನಂತರ, ಗ್ರಾಫ್ DBMS ಗಳನ್ನು ವಿಶ್ಲೇಷಣಾತ್ಮಕ ವರದಿಗಳಿಗಾಗಿ ಸಹ ಬಳಸಲಾಗುತ್ತದೆ ಎಂದು ನಾನು ಕಂಡುಹಿಡಿದಿದ್ದೇನೆ. ಅಂತಹ ಪ್ರಶ್ನೆಗಳನ್ನು ಗ್ರಾಫ್ DBMS ಗಳು ಹೇಗೆ ನಿಭಾಯಿಸುತ್ತವೆ ಎಂಬುದನ್ನು ನೋಡುವ ಬಯಕೆ ನನಗಿತ್ತು (ಟಿಎಲ್; ಡಿಆರ್; ಕಳಪೆ).

ನಾನು DBMS ಅನ್ನು ಬಳಸಲು ಆಯ್ಕೆ ಮಾಡಿದ್ದೇನೆ ಜಾನಸ್ ಗ್ರಾಫ್, ಪ್ರಬುದ್ಧ ತಂತ್ರಜ್ಞಾನಗಳ ಸ್ಟಾಕ್ ಅನ್ನು ಅವಲಂಬಿಸಿರುವ ಗ್ರಾಫ್ ಓಪನ್-ಸೋರ್ಸ್ DBMS ನ ಅತ್ಯುತ್ತಮ ಪ್ರತಿನಿಧಿಯಾಗಿ, ಇದು (ನನ್ನ ಅಭಿಪ್ರಾಯದಲ್ಲಿ) ಯೋಗ್ಯವಾದ ಕಾರ್ಯಾಚರಣೆಯ ಗುಣಲಕ್ಷಣಗಳೊಂದಿಗೆ ಒದಗಿಸಬೇಕು:

  • ಬರ್ಕ್ಲಿಡಿಬಿ ಶೇಖರಣಾ ಬ್ಯಾಕೆಂಡ್, ಅಪಾಚೆ ಕಸ್ಸಂದ್ರ, ಸ್ಕಿಲ್ಲಾ;
  • ಸಂಕೀರ್ಣ ಸೂಚ್ಯಂಕಗಳನ್ನು ಲುಸೀನ್, ಎಲಾಸ್ಟಿಕ್ಸರ್ಚ್, ಸೋಲ್ರ್ನಲ್ಲಿ ಸಂಗ್ರಹಿಸಬಹುದು.

JanusGraph ನ ಲೇಖಕರು OLTP ಮತ್ತು OLAP ಎರಡಕ್ಕೂ ಸೂಕ್ತವಾಗಿದೆ ಎಂದು ಬರೆಯುತ್ತಾರೆ.

ನಾನು BerkeleyDB, Apache Cassandra, Scylla ಮತ್ತು ES ಜೊತೆಗೆ ಕೆಲಸ ಮಾಡಿದ್ದೇನೆ ಮತ್ತು ಈ ಉತ್ಪನ್ನಗಳನ್ನು ನಮ್ಮ ಸಿಸ್ಟಂಗಳಲ್ಲಿ ಹೆಚ್ಚಾಗಿ ಬಳಸಲಾಗುತ್ತದೆ, ಆದ್ದರಿಂದ ನಾನು ಈ ಗ್ರಾಫ್ DBMS ಅನ್ನು ಪರೀಕ್ಷಿಸುವ ಬಗ್ಗೆ ಆಶಾವಾದಿಯಾಗಿದ್ದೆ. RocksDB ಗಿಂತ BerkeleyDB ಅನ್ನು ಆಯ್ಕೆ ಮಾಡುವುದು ನನಗೆ ಬೆಸವಾಗಿದೆ, ಆದರೆ ಅದು ಬಹುಶಃ ವಹಿವಾಟಿನ ಅಗತ್ಯತೆಗಳ ಕಾರಣದಿಂದಾಗಿರಬಹುದು. ಯಾವುದೇ ಸಂದರ್ಭದಲ್ಲಿ, ಸ್ಕೇಲೆಬಲ್, ಉತ್ಪನ್ನ ಬಳಕೆಗಾಗಿ, ಕಸ್ಸಂದ್ರ ಅಥವಾ ಸ್ಕಿಲ್ಲಾದಲ್ಲಿ ಬ್ಯಾಕೆಂಡ್ ಅನ್ನು ಬಳಸಲು ಸೂಚಿಸಲಾಗುತ್ತದೆ.

ನಾನು Neo4j ಅನ್ನು ಪರಿಗಣಿಸಲಿಲ್ಲ ಏಕೆಂದರೆ ಕ್ಲಸ್ಟರಿಂಗ್‌ಗೆ ವಾಣಿಜ್ಯ ಆವೃತ್ತಿಯ ಅಗತ್ಯವಿರುತ್ತದೆ, ಅಂದರೆ ಉತ್ಪನ್ನವು ಮುಕ್ತ ಮೂಲವಲ್ಲ.

ಗ್ರಾಫ್ DBMS ಗಳು ಹೇಳುತ್ತವೆ: "ಇದು ಗ್ರಾಫ್‌ನಂತೆ ಕಂಡುಬಂದರೆ, ಅದನ್ನು ಗ್ರಾಫ್‌ನಂತೆ ಪರಿಗಣಿಸಿ!" - ಸೌಂದರ್ಯ!

ಮೊದಲಿಗೆ, ನಾನು ಗ್ರಾಫ್ ಅನ್ನು ಚಿತ್ರಿಸಿದೆ, ಅದನ್ನು ನಿಖರವಾಗಿ ಗ್ರಾಫ್ DBMS ನ ನಿಯಮಗಳ ಪ್ರಕಾರ ಮಾಡಲಾಗಿದೆ:

ಸೂಕ್ತವಾದ ಮಾರ್ಗಗಳನ್ನು ಹುಡುಕುವ ಸಮಸ್ಯೆಯನ್ನು ಪರಿಹರಿಸಲು JanusGraph ಗ್ರಾಫ್ DBMS ನ ಅನ್ವಯಿಸುವಿಕೆಯನ್ನು ಪರೀಕ್ಷಿಸುವ ಪ್ರಯೋಗ

ಸತ್ವವಿದೆ 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()

ರಷ್ಯನ್ ಭಾಷೆಯಲ್ಲಿ ಏನೆಂದರೆ: ID = 0 ನೊಂದಿಗೆ ವಲಯವನ್ನು ಹುಡುಕಿ, ಅದರ ಅಂಚುಗೆ ಹೋಗುವ ಎಲ್ಲಾ ಶೃಂಗಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಿ (ZoneStep), ವಲಯಕ್ಕೆ ಅಂಚಿನಲ್ಲಿರುವ ಆ ZoneSteps ಅನ್ನು ನೀವು ಕಂಡುಕೊಳ್ಳುವವರೆಗೆ ಹಿಂತಿರುಗದೆ ಸ್ಟಾಂಪ್ ಮಾಡಿ ID=19, ಅಂತಹ ಸರಪಳಿಗಳ ಸಂಖ್ಯೆಯನ್ನು ಎಣಿಸಿ.

ಗ್ರಾಫ್‌ಗಳಲ್ಲಿ ಹುಡುಕುವ ಎಲ್ಲಾ ಜಟಿಲತೆಗಳನ್ನು ನಾನು ತಿಳಿದಿರುವಂತೆ ನಟಿಸುವುದಿಲ್ಲ, ಆದರೆ ಈ ಪ್ರಶ್ನೆಯನ್ನು ಈ ಪುಸ್ತಕದ ಆಧಾರದ ಮೇಲೆ ರಚಿಸಲಾಗಿದೆ (https://kelvinlawrence.net/book/Gremlin-Graph-Guide.html).

ನಾನು ಬರ್ಕ್ಲಿಡಿಬಿ ಬ್ಯಾಕೆಂಡ್ ಅನ್ನು ಬಳಸಿಕೊಂಡು 50 ರಿಂದ 3 ಪಾಯಿಂಟ್‌ಗಳ ಉದ್ದದ 20 ಸಾವಿರ ಟ್ರ್ಯಾಕ್‌ಗಳನ್ನು ಜಾನಸ್‌ಗ್ರಾಫ್ ಗ್ರಾಫ್ ಡೇಟಾಬೇಸ್‌ಗೆ ಲೋಡ್ ಮಾಡಿದ್ದೇನೆ, ಪ್ರಕಾರ ಇಂಡೆಕ್ಸ್‌ಗಳನ್ನು ರಚಿಸಿದೆ ನಾಯಕತ್ವ.

ಪೈಥಾನ್ ಡೌನ್‌ಲೋಡ್ ಸ್ಕ್ರಿಪ್ಟ್:


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)

ನಾವು SSD ನಲ್ಲಿ 4 ಕೋರ್‌ಗಳು ಮತ್ತು 16 GB RAM ಹೊಂದಿರುವ VM ಅನ್ನು ಬಳಸಿದ್ದೇವೆ. ಈ ಆಜ್ಞೆಯನ್ನು ಬಳಸಿಕೊಂಡು JanusGraph ಅನ್ನು ನಿಯೋಜಿಸಲಾಗಿದೆ:

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

ಈ ಸಂದರ್ಭದಲ್ಲಿ, ನಿಖರವಾದ ಹೊಂದಾಣಿಕೆ ಹುಡುಕಾಟಗಳಿಗಾಗಿ ಬಳಸಲಾಗುವ ಡೇಟಾ ಮತ್ತು ಸೂಚಿಕೆಗಳನ್ನು ಬರ್ಕ್ಲಿಡಿಬಿಯಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾಗುತ್ತದೆ. ಹಿಂದೆ ನೀಡಲಾದ ವಿನಂತಿಯನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸಿದ ನಂತರ, ನಾನು ಹಲವಾರು ಹತ್ತಾರು ಸೆಕೆಂಡುಗಳಿಗೆ ಸಮಾನವಾದ ಸಮಯವನ್ನು ಸ್ವೀಕರಿಸಿದ್ದೇನೆ.

ಮೇಲಿನ 4 ಸ್ಕ್ರಿಪ್ಟ್‌ಗಳನ್ನು ಸಮಾನಾಂತರವಾಗಿ ಚಲಾಯಿಸುವ ಮೂಲಕ, ಡಾಕರ್ ಲಾಗ್‌ಗಳಲ್ಲಿ ಜಾವಾ ಸ್ಟಾಕ್‌ಟ್ರೇಸ್‌ಗಳ (ಮತ್ತು ನಾವೆಲ್ಲರೂ ಜಾವಾ ಸ್ಟ್ಯಾಕ್‌ಟ್ರೇಸ್‌ಗಳನ್ನು ಓದುವುದನ್ನು ಇಷ್ಟಪಡುತ್ತೇವೆ) ಹರ್ಷಚಿತ್ತದಿಂದ DBMS ಅನ್ನು ಕುಂಬಳಕಾಯಿಯನ್ನಾಗಿ ಮಾಡಲು ನಿರ್ವಹಿಸುತ್ತಿದ್ದೆ.

ಸ್ವಲ್ಪ ಆಲೋಚನೆಯ ನಂತರ, ನಾನು ಗ್ರಾಫ್ ರೇಖಾಚಿತ್ರವನ್ನು ಈ ಕೆಳಗಿನಂತೆ ಸರಳೀಕರಿಸಲು ನಿರ್ಧರಿಸಿದೆ:

ಸೂಕ್ತವಾದ ಮಾರ್ಗಗಳನ್ನು ಹುಡುಕುವ ಸಮಸ್ಯೆಯನ್ನು ಪರಿಹರಿಸಲು JanusGraph ಗ್ರಾಫ್ DBMS ನ ಅನ್ವಯಿಸುವಿಕೆಯನ್ನು ಪರೀಕ್ಷಿಸುವ ಪ್ರಯೋಗ

ಅಸ್ತಿತ್ವದ ಗುಣಲಕ್ಷಣಗಳ ಮೂಲಕ ಹುಡುಕುವಿಕೆಯು ಅಂಚುಗಳ ಮೂಲಕ ಹುಡುಕುವುದಕ್ಕಿಂತ ವೇಗವಾಗಿರುತ್ತದೆ ಎಂದು ನಿರ್ಧರಿಸುವುದು. ಪರಿಣಾಮವಾಗಿ, ನನ್ನ ವಿನಂತಿಯು ಈ ಕೆಳಗಿನಂತೆ ತಿರುಗಿತು:

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

ರಷ್ಯನ್ ಭಾಷೆಯಲ್ಲಿ ಏನೆಂದರೆ: ID = 0 ನೊಂದಿಗೆ ZoneStep ಅನ್ನು ಹುಡುಕಿ, ID = 19 ನೊಂದಿಗೆ ZoneStep ಅನ್ನು ಕಂಡುಹಿಡಿಯುವವರೆಗೆ ಹಿಂತಿರುಗದೆ ಸ್ಟಾಂಪ್ ಮಾಡಿ, ಅಂತಹ ಸರಪಳಿಗಳ ಸಂಖ್ಯೆಯನ್ನು ಎಣಿಸಿ.

ಅನಾವಶ್ಯಕ ಸಂಪರ್ಕಗಳನ್ನು ರಚಿಸದಿರಲು ನಾನು ಮೇಲೆ ನೀಡಲಾದ ಲೋಡಿಂಗ್ ಸ್ಕ್ರಿಪ್ಟ್ ಅನ್ನು ಸರಳೀಕರಿಸಿದ್ದೇನೆ, ಗುಣಲಕ್ಷಣಗಳಿಗೆ ನನ್ನನ್ನು ಸೀಮಿತಗೊಳಿಸಿದೆ.

ವಿನಂತಿಯು ಪೂರ್ಣಗೊಳ್ಳಲು ಇನ್ನೂ ಹಲವಾರು ಸೆಕೆಂಡುಗಳನ್ನು ತೆಗೆದುಕೊಂಡಿತು, ಇದು ನಮ್ಮ ಕಾರ್ಯಕ್ಕೆ ಸಂಪೂರ್ಣವಾಗಿ ಸ್ವೀಕಾರಾರ್ಹವಲ್ಲ, ಏಕೆಂದರೆ ಇದು ಯಾವುದೇ ರೀತಿಯ AdHoc ವಿನಂತಿಗಳ ಉದ್ದೇಶಗಳಿಗೆ ಸೂಕ್ತವಲ್ಲ.

ನಾನು ಜಾನಸ್‌ಗ್ರಾಫ್ ಅನ್ನು ಸ್ಕಿಲ್ಲಾವನ್ನು ವೇಗವಾದ ಕಸ್ಸಾಂಡ್ರಾ ಅಳವಡಿಕೆಯಾಗಿ ಬಳಸಿಕೊಳ್ಳಲು ಪ್ರಯತ್ನಿಸಿದೆ, ಆದರೆ ಇದು ಯಾವುದೇ ಗಮನಾರ್ಹ ಕಾರ್ಯಕ್ಷಮತೆ ಬದಲಾವಣೆಗಳಿಗೆ ಕಾರಣವಾಗಲಿಲ್ಲ.

ಆದ್ದರಿಂದ "ಇದು ಗ್ರಾಫ್‌ನಂತೆ ಕಾಣುತ್ತದೆ" ಎಂಬ ವಾಸ್ತವದ ಹೊರತಾಗಿಯೂ, ಅದನ್ನು ತ್ವರಿತವಾಗಿ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಗ್ರಾಫ್ DBMS ಅನ್ನು ಪಡೆಯಲು ನನಗೆ ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ನನಗೆ ಗೊತ್ತಿಲ್ಲದ ವಿಷಯವಿದೆ ಮತ್ತು ಈ ಹುಡುಕಾಟವನ್ನು ಒಂದು ಸೆಕೆಂಡಿನ ಭಾಗದಲ್ಲಿ ಮಾಡಲು JanusGraph ಅನ್ನು ಮಾಡಬಹುದು ಎಂದು ನಾನು ಸಂಪೂರ್ಣವಾಗಿ ಊಹಿಸುತ್ತೇನೆ, ಆದಾಗ್ಯೂ, ನನಗೆ ಅದನ್ನು ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ.

ಸಮಸ್ಯೆಯನ್ನು ಇನ್ನೂ ಪರಿಹರಿಸಬೇಕಾಗಿರುವುದರಿಂದ, ನಾನು ಸೇರ್ಪಡೆಗಳು ಮತ್ತು ಕೋಷ್ಟಕಗಳ ಪಿವೋಟ್‌ಗಳ ಬಗ್ಗೆ ಯೋಚಿಸಲು ಪ್ರಾರಂಭಿಸಿದೆ, ಇದು ಸೊಬಗಿನ ವಿಷಯದಲ್ಲಿ ಆಶಾವಾದವನ್ನು ಪ್ರೇರೇಪಿಸಲಿಲ್ಲ, ಆದರೆ ಆಚರಣೆಯಲ್ಲಿ ಸಂಪೂರ್ಣವಾಗಿ ಕಾರ್ಯಸಾಧ್ಯವಾದ ಆಯ್ಕೆಯಾಗಿರಬಹುದು.

ನಮ್ಮ ಯೋಜನೆಯು ಈಗಾಗಲೇ Apache ClickHouse ಅನ್ನು ಬಳಸುತ್ತದೆ, ಆದ್ದರಿಂದ ನಾನು ಈ ವಿಶ್ಲೇಷಣಾತ್ಮಕ DBMS ನಲ್ಲಿ ನನ್ನ ಸಂಶೋಧನೆಯನ್ನು ಪರೀಕ್ಷಿಸಲು ನಿರ್ಧರಿಸಿದೆ.

ಸರಳ ಪಾಕವಿಧಾನವನ್ನು ಬಳಸಿಕೊಂಡು ಕ್ಲಿಕ್‌ಹೌಸ್ ಅನ್ನು ನಿಯೋಜಿಸಲಾಗಿದೆ:

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
    )

ಒಳಸೇರಿಸುವಿಕೆಯು ಬ್ಯಾಚ್‌ಗಳಲ್ಲಿ ಬರುವುದರಿಂದ, ಭರ್ತಿ ಮಾಡುವುದು ಜಾನಸ್‌ಗ್ರಾಫ್‌ಗಿಂತ ಹೆಚ್ಚು ವೇಗವಾಗಿತ್ತು.

JOIN ಅನ್ನು ಬಳಸಿಕೊಂಡು ಎರಡು ಪ್ರಶ್ನೆಗಳನ್ನು ರಚಿಸಲಾಗಿದೆ. A ಬಿಂದುವಿನಿಂದ 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

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 ಸೆಕೆಂಡುಗಳಿಗಿಂತ ಕಡಿಮೆ ಅವಧಿಯಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತವೆ. ಎಣಿಕೆ (*) 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 ಸಾಕಷ್ಟು ಹೆಚ್ಚಿತ್ತು. ಅದೇ ಸಮಯದಲ್ಲಿ, ಕ್ಲಿಕ್‌ಹೌಸ್ ಡಿಸ್ಕ್ ಉಪವ್ಯವಸ್ಥೆಯಲ್ಲಿ ಕನಿಷ್ಠ ಲೋಡ್ ಅನ್ನು ಉತ್ಪಾದಿಸುತ್ತದೆ.

ತೀರ್ಮಾನಕ್ಕೆ

ಈ ರೀತಿಯ ವಿನಂತಿಯನ್ನು ಒದಗಿಸಲು ನಾವು ಕ್ಲಿಕ್‌ಹೌಸ್ ಅನ್ನು ಬಳಸಲು ನಿರ್ಧರಿಸಿದ್ದೇವೆ. ಕ್ಲಿಕ್‌ಹೌಸ್‌ಗೆ ಲೋಡ್ ಮಾಡುವ ಮೊದಲು ಅಪಾಚೆ ಫ್ಲಿಂಕ್ ಬಳಸಿ ಈವೆಂಟ್ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಪೂರ್ವ-ಪ್ರಕ್ರಿಯೆಗೊಳಿಸುವ ಮೂಲಕ ನಾವು ಯಾವಾಗಲೂ ವಸ್ತುನಿಷ್ಠ ವೀಕ್ಷಣೆಗಳು ಮತ್ತು ಸಮಾನಾಂತರೀಕರಣವನ್ನು ಬಳಸಿಕೊಂಡು ಪ್ರಶ್ನೆಗಳನ್ನು ಮತ್ತಷ್ಟು ಆಪ್ಟಿಮೈಜ್ ಮಾಡಬಹುದು.

ಕಾರ್ಯಕ್ಷಮತೆಯು ತುಂಬಾ ಉತ್ತಮವಾಗಿದೆ, ನಾವು ಬಹುಶಃ ಕೋಷ್ಟಕಗಳನ್ನು ಪ್ರೋಗ್ರಾಮಿಕ್ ಆಗಿ ಪಿವೋಟಿಂಗ್ ಮಾಡುವ ಬಗ್ಗೆ ಯೋಚಿಸಬೇಕಾಗಿಲ್ಲ. ಹಿಂದೆ, ನಾವು ಅಪಾಚೆ ಪ್ಯಾರ್ಕ್ವೆಟ್‌ಗೆ ಅಪ್‌ಲೋಡ್ ಮಾಡುವ ಮೂಲಕ ವರ್ಟಿಕಾದಿಂದ ಹಿಂಪಡೆಯಲಾದ ಡೇಟಾದ ಪಿವೋಟ್‌ಗಳನ್ನು ಮಾಡಬೇಕಾಗಿತ್ತು.

ದುರದೃಷ್ಟವಶಾತ್, ಗ್ರಾಫ್ DBMS ಅನ್ನು ಬಳಸುವ ಮತ್ತೊಂದು ಪ್ರಯತ್ನವು ವಿಫಲವಾಗಿದೆ. ಉತ್ಪನ್ನದೊಂದಿಗೆ ವೇಗವನ್ನು ಪಡೆದುಕೊಳ್ಳಲು ಸುಲಭವಾಗಿಸುವ ಸ್ನೇಹಪರ ಪರಿಸರ ವ್ಯವಸ್ಥೆಯನ್ನು ಹೊಂದಲು JanusGraph ಅನ್ನು ನಾನು ಕಂಡುಕೊಂಡಿಲ್ಲ. ಅದೇ ಸಮಯದಲ್ಲಿ, ಸರ್ವರ್ ಅನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡಲು, ಸಾಂಪ್ರದಾಯಿಕ ಜಾವಾ ವಿಧಾನವನ್ನು ಬಳಸಲಾಗುತ್ತದೆ, ಇದು ಜಾವಾ ಪರಿಚಯವಿಲ್ಲದ ಜನರನ್ನು ರಕ್ತದ ಕಣ್ಣೀರು ಹಾಕುವಂತೆ ಮಾಡುತ್ತದೆ:

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}

ನಾನು ಆಕಸ್ಮಿಕವಾಗಿ JanusGraph ನ ಬರ್ಕ್ಲಿಡಿಬಿ ಆವೃತ್ತಿಯನ್ನು "ಹಾಕಲು" ನಿರ್ವಹಿಸುತ್ತಿದ್ದೆ.

ದಸ್ತಾವೇಜನ್ನು ಸೂಚ್ಯಂಕಗಳ ವಿಷಯದಲ್ಲಿ ಸಾಕಷ್ಟು ವಕ್ರವಾಗಿದೆ, ಏಕೆಂದರೆ ಸೂಚ್ಯಂಕಗಳನ್ನು ನಿರ್ವಹಿಸುವುದರಿಂದ ನೀವು ಗ್ರೂವಿಯಲ್ಲಿ ಕೆಲವು ವಿಚಿತ್ರವಾದ ಶಾಮನಿಸಂ ಅನ್ನು ನಿರ್ವಹಿಸಬೇಕಾಗುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಗ್ರೆಮ್ಲಿನ್ ಕನ್ಸೋಲ್‌ನಲ್ಲಿ ಕೋಡ್ ಬರೆಯುವ ಮೂಲಕ ಸೂಚ್ಯಂಕವನ್ನು ರಚಿಸಬೇಕು (ಇದು ಬಾಕ್ಸ್‌ನಿಂದ ಹೊರಗೆ ಕೆಲಸ ಮಾಡುವುದಿಲ್ಲ). ಅಧಿಕೃತ 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()

ನಂತರದ

ಒಂದರ್ಥದಲ್ಲಿ, ಮೇಲಿನ ಪ್ರಯೋಗವು ಬೆಚ್ಚಗಿನ ಮತ್ತು ಮೃದುವಾದ ನಡುವಿನ ಹೋಲಿಕೆಯಾಗಿದೆ. ನೀವು ಅದರ ಬಗ್ಗೆ ಯೋಚಿಸಿದರೆ, ಅದೇ ಫಲಿತಾಂಶಗಳನ್ನು ಪಡೆಯಲು ಗ್ರಾಫ್ DBMS ಇತರ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ನಿರ್ವಹಿಸುತ್ತದೆ. ಆದಾಗ್ಯೂ, ಪರೀಕ್ಷೆಗಳ ಭಾಗವಾಗಿ, ನಾನು ಈ ರೀತಿಯ ವಿನಂತಿಯೊಂದಿಗೆ ಪ್ರಯೋಗವನ್ನು ಸಹ ನಡೆಸಿದೆ:

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

ಇದು ವಾಕಿಂಗ್ ದೂರವನ್ನು ಪ್ರತಿಬಿಂಬಿಸುತ್ತದೆ. ಆದಾಗ್ಯೂ, ಅಂತಹ ಡೇಟಾದಲ್ಲಿ ಸಹ, ಗ್ರಾಫ್ DBMS ಕೆಲವು ಸೆಕೆಂಡುಗಳನ್ನು ಮೀರಿದ ಫಲಿತಾಂಶಗಳನ್ನು ತೋರಿಸಿದೆ ... ಇದು ಸಹಜವಾಗಿ, ಅಂತಹ ಮಾರ್ಗಗಳು ಇದ್ದವು ಎಂಬ ಅಂಶದಿಂದಾಗಿ 0 -> X -> Y ... -> 1, ಇದು ಗ್ರಾಫ್ ಎಂಜಿನ್ ಅನ್ನು ಸಹ ಪರಿಶೀಲಿಸಿದೆ.

ಅಂತಹ ಪ್ರಶ್ನೆಗೆ ಸಹ:

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

ಒಂದು ಸೆಕೆಂಡ್‌ಗಿಂತ ಕಡಿಮೆ ಅವಧಿಯ ಸಂಸ್ಕರಣಾ ಸಮಯದೊಂದಿಗೆ ಉತ್ಪಾದಕ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಪಡೆಯಲು ನನಗೆ ಸಾಧ್ಯವಾಗಲಿಲ್ಲ.

ಕಥೆಯ ನೈತಿಕತೆಯೆಂದರೆ, ಸುಂದರವಾದ ಕಲ್ಪನೆ ಮತ್ತು ಮಾದರಿ ಮಾಡೆಲಿಂಗ್ ಅಪೇಕ್ಷಿತ ಫಲಿತಾಂಶಕ್ಕೆ ಕಾರಣವಾಗುವುದಿಲ್ಲ, ಇದು ಕ್ಲಿಕ್‌ಹೌಸ್‌ನ ಉದಾಹರಣೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಹೆಚ್ಚಿನ ದಕ್ಷತೆಯೊಂದಿಗೆ ಪ್ರದರ್ಶಿಸಲಾಗುತ್ತದೆ. ಈ ಲೇಖನದಲ್ಲಿ ಪ್ರಸ್ತುತಪಡಿಸಲಾದ ಬಳಕೆಯ ಸಂದರ್ಭವು ಗ್ರಾಫ್ DBMS ಗಳಿಗೆ ಸ್ಪಷ್ಟವಾದ ವಿರೋಧಿ ಮಾದರಿಯಾಗಿದೆ, ಆದರೂ ಇದು ಅವರ ಮಾದರಿಯಲ್ಲಿ ಮಾಡೆಲಿಂಗ್‌ಗೆ ಸೂಕ್ತವಾಗಿದೆ.

ಮೂಲ: www.habr.com

ಕಾಮೆಂಟ್ ಅನ್ನು ಸೇರಿಸಿ