График DSE: большее количество потоков приводит к увеличению времени отклика

Я уже задавал этот вопрос. Но прошу еще раз с конкретным примером.

Итак, у меня есть график DSE, работающий локально на моем Mac. У меня самое простое создание вершины и ниже обход.

g.addV("company").property("id", companyId)
.property("name", "company_" + companyId)
.property(VertexProperty.Cardinality.list, "domainurls", "test.com", "anothertest.com")
.next();

Теперь я использую Java TinkerPop3 API для совершения звонков. А у меня DseSession получился таким образом.

dseCluster = DseCluster.builder()
        .addContactPoints(contactPoints)
        .withGraphOptions(new GraphOptions().setGraphName("profilex_dev"))
        .build();
dseSession = dseCluster.connect();
DseGraph.traversal(dseSession)

Я повторно использую этот экземпляр GraphTraversalSource в многопоточном приложении. По моим наблюдениям, чем больше количество потоков, тем медленнее время отклика.

Я измерил с помощью Java Microbenchmarking Harness, и ниже примерно то, что я нашел

  • 10 потоков — 6 мс
  • 50 потоков — 34 мс
  • 200 потоков — 146 мс.

Итак, мой вопрос: есть ли способ оптимизировать это, чтобы он работал быстрее - любые параметры пула, которые необходимо установить и т. д. В моем случае происходит гораздо больше, чем создание компании и большее количество мутаций/запросов графа (около 10 таких обходов), что общее время отклика по мере увеличения количества потоков становится субоптимальным.

Обратите внимание, что приведенное выше время отклика относится и к простым запросам графа. Таким образом, даже простое чтение выполняется медленнее по мере увеличения числа потоков. (И, конечно, очень хорошо, когда количество потоков меньше).


person Sathyakumar Seshachalam    schedule 12.03.2018    source источник
comment
Можете добавить код мутаций/запросов? Иногда проблема с производительностью могла быть решена там...   -  person Alex Ott    schedule 12.03.2018
comment
Еще вопрос - какую версию драйвера используете?   -  person Alex Ott    schedule 14.03.2018
comment
Возможно, использование источника обхода таким образом замедляет работу, вы можете попробовать переключиться на GraphStatements и использовать метод DseGraph.statementFromTraversal() вместо прямой итерации обхода и выполнять операторы через сеанс.   -  person newkek    schedule 14.03.2018
comment
Если это не изменится, вам следует проверить запросы inFlight от драйвера (docs.datastax.com/en/developer/java-driver-dse/1.5/manual/). Если inFlight увеличивается при увеличении количества потоков, это означает, что в конечном итоге это проблема производительности на стороне сервера DseGraph, которую драйвер не может обойти. Одним из решений является пакетная вставка в одном и том же обходе, например g.addV().property().addV().property()......   -  person newkek    schedule 14.03.2018
comment
@newkek: извините за задержку, мониторинг запросов в пути не помог. Запросы в полете всегда не превышают 200 потоков (200 было тем, что я запускал) и максимальная нагрузка = 1024. Обратите внимание, что это так, даже если я просто получаю одну вершину g.V(vertexId). Итак, я предполагаю, что это проблема конфигурации на стороне клиента, скорее всего   -  person Sathyakumar Seshachalam    schedule 19.03.2018
comment
Мы используем версию драйвера Java 1.4.1.   -  person Sathyakumar Seshachalam    schedule 19.03.2018
comment
@newkek: Также обратите внимание, что если я каждый раз создаю DseSession, DseCluster.connect и каждый раз закрываю его. Я получаю лучшее время отклика. Я подумал, что это анти-шаблон, и я должен был повторно использовать один экземпляр DseSession.   -  person Sathyakumar Seshachalam    schedule 19.03.2018
comment
Когда вы закрываете и открываете DseSession? Действительно, это антишаблон... мы рекомендуем использовать DseSession как долгоживущий объект. У вас есть образец кода, который вы могли бы предоставить, чтобы воспроизвести проблему?   -  person newkek    schedule 19.03.2018
comment
Большое спасибо, это очень полезно, постараюсь профилировать это   -  person newkek    schedule 21.03.2018


Ответы (1)


Не уверен, что это должно быть опубликовано как «ответ», но форматирование проще, чем в комментарии. Спасибо за предоставление полного тестового класса, было полезно отладить и поэкспериментировать.

Просматривая вашу тестовую реализацию, я действительно смог заметить снижение пропускной способности по мере увеличения уровня параллелизма.

Я считаю, что то, что вы видите, является побочным эффектом того, что ваш локальный узел сервера изначально не использовался, а кэши сервера были холодными и недостаточно активными. Вам нужны большие фазы прогрева, чтобы ваш локальный узел мог начать отвечать достаточно быстро и увидеть, как при более высоком уровне параллелизма время отдельных запросов не увеличивается.

Я провел ваши тесты (только односессионные), когда моя система мало загружена, и когда я запускаю ваши тесты, результаты выглядят точно так же, как ваши.

Однако, если поставить дополнительные тесты перед выполнением тестов с 10-20-50 запросами, например так:

    dseVertexQueryPerformance.testSingleSession(3000);
    dseVertexQueryPerformance.testSingleSession(6000);
    dseVertexQueryPerformance.testSingleSession(6000);
    dseVertexQueryPerformance.testSingleSession(6000);
    dseVertexQueryPerformance.testSingleSession(6000);
    dseVertexQueryPerformance.testSingleSession(9000);
    dseVertexQueryPerformance.testSingleSession(9000);
    dseVertexQueryPerformance.testSingleSession(9000);
    dseVertexQueryPerformance.testSingleSession(10);
    dseVertexQueryPerformance.testSingleSession(20);
    dseVertexQueryPerformance.testSingleSession(50);
    dseVertexQueryPerformance.testSingleSession(100);
    dseVertexQueryPerformance.testSingleSession(200);

В итоге я получаю такие результаты, как:

End Test ::SingleSession, Average Time: 2.6, Total execution time: 5.891593 ms
For 10 threads, ::SingleSession took 2.6
End Test ::SingleSession, Average Time: 4.4, Total execution time: 7.830533 ms
For 20 threads, ::SingleSession took 4.4
End Test ::SingleSession, Average Time: 1.86, Total execution time: 20.378055 ms
For 50 threads, ::SingleSession took 1.86
End Test ::SingleSession, Average Time: 1.98, Total execution time: 47.487505 ms
For 100 threads, ::SingleSession took 1.98
End Test ::SingleSession, Average Time: 2.295, Total execution time: 92.793991 ms
For 200 threads, ::SingleSession took 2.295

Мы можем эффективно видеть, что конфликт возник из-за того, что DSE Graph или система в целом были недостаточно прогретыми.


Теперь, оглядываясь назад на те более длинные прогоны, о которых я упоминал выше, я наблюдаю, что при использовании подключенного источника обхода возникает больше конфликтов или больше работы.

Я бы рекомендовал вместо прямой итерации источника обхода создать GraphStatement из обхода, как описано здесь: https://docs.datastax.com/en/developer/java-driver-dse/1.5/manual/tinkerpop/#data-stax-drivers-execution-compatibility

Итак, в ваших тестах я изменил getAVertex(GraphTraversalSource g) на:

public class DSEVertexQueryPerformance {
    ...

    private GraphTraversalSource traversalSource = DseGraph.traversal();

    ....

    public long getAVertex(DseSession dseSession) {
        Instant begin = Instant.now();
        dseSession.executeGraph(DseGraph.statementFromTraversal(
            traversalSource.V(vertexId))
        );
        return Duration.between(begin, Instant.now()).toMillis();
    }

Изменив этот метод, я смог перейти с общего времени выполнения ~1900 мс, скажем, для 9000 запросов (dseVertexQueryPerformance.testSingleSession(9000);) с вашей текущей версией g.V().next(), до общего времени выполнения ~1400 мс с использованием метода statementFromTraversal().

Наконец, я также рекомендую использовать асинхронные методы выполнения запросов (DseSession.executeGraphAsync()), так как это позволит вам распараллелить все ваши запросы без необходимости использования пулов потоков на стороне клиента, что в конечном итоге снизит нагрузку на клиентское приложение.

person newkek    schedule 22.03.2018
comment
Спасибо за подробный ответ. Однако, у меня есть вопрос. Можно ли заранее разогреть сервер и нарастить кеш. Я буду продолжать смотреть, если несколько виртуальных машин. Одна виртуальная машина прогревает кеш, а другая выполняет обычный тест 10, 20, 50, 100. - person Sathyakumar Seshachalam; 23.03.2018