Progress28.ru

IT Новости
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Asp phpsessid gari neo

Начинаем работать с графовой базой данных Neo4j

В нашем проекте возникла следующая задача — есть база с большим количеством товаров, на уровне сотен тысяч. У каждого товара есть сотни динамически создаваемых характеристик. Необходимо обеспечить быструю фильтрацию по товарам по набору различных характеристик. Время формирования ответа должно быть не более 0.3 секунды, нужно поддерживать сложную логику в стиле.

У нас все реализовано в рамках MySQL + Symfony2/Doctrine, скорость неудовлетворительная — ответы формируются в течении 1-10 секунд. Мои попытки оптимизировать все это хозяйство — под катом.

Терминология задачи по фильтрации товаров (в упрощенном виде)

  • характеристика — определенное свойство товара. Например, объем памяти.
  • шаблон товара — набор всех возможных характеристик однотипных товаров, например — перечень возможных характеристик компьютерных мышек. При добавлении нового товара администратор может выбирать характеристики в рамках шаблона. Добавить новую характеристику для одного товара невозможно — нужно добавить характеристику в шаблон для этого товара. Одновременно эта характеристика будет доступна для всех товаров, использующих этот шаблон
  • группа товаров — товары на основе одного шаблона. Например, компьютерные мышки. Фильтрация делается только для товаров из одной группы
  • критерий — логическое правило, которое состоит из набора формальных требований к характеристикам товара. Например, «геймерская мышка» — это набор требований к характеристикам (размер не миниатюрный) AND (сенсор лазерный) AND (разрешение сенсора не менее 1500)
  • фильтр — группа критериев для однотипных товаров. В зависимости от критериев, они могут комбинироваться через AND или OR

У hotline реализован более продвинутый вариант — с подсказкой, сколько товаров останется после активации критерия. Например, если выбрать фильтр «Bluetooth», то после загрузки страницы возле фильтра «Тип сенсора мыши — оптический» будет цифра 17. Фактически, для такой реализации нужно не просто делать выборку по критериям, но и предварительно для каждого оставшегося фильтра подсчитывать количество товаров при его активации.

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

Терминология Neo4j и графовых баз данных в целом.

34 биллиона

  • node label, метка ноды — используется как условный «тип ноды». Например, ноды типа movie могут быть связанны с нодами типа actor. Метки нод — регистрозависимые, причем Cypher не выдает ошибок, если набрать не в том регистре название.
  • relation, связь — связь между двумя нодами, ребро графа. Количество связей ограниченно 2 в степени 35

    34 биллиона

  • relation identirfier, тип связи — в Neo4j у связей. Максимальное количество типов связей 32767
  • properties, свойства ноды — набор данных, которые можно назначить ноде. Например, если нода — это товар, то в свойствах ноды можно хранить id товара из базы MySQL
  • node ID, ID нода — уникальный идентификатор ноды. По умолчанию, при просмотрах результата отображается именно этот ID. как его использовать в Cypher запросах я не нашел
  • Схема решения задачи

    Для каждого товара создать отдельную ноду, в свойствах ноды хранить id товара в базе MySQL. Для каждого критерия создать свою ноду, в свойствах хранить id критерия. Дальше, связать все ноды товаров с нодами критериев, которые подходят для товара. При изменении характеристик товара или свойств критериев обновлять связи между нодами.

    Первый вариант решения — с Neo4j

    Учитывая, что я с графовыми базами данных никогда не работал — я решил развернуть локально Neo4j, изучить на базовом уровне Cypher и попробовать реализовать требуемую логику. Если все получиться — провести тестирование скорости работы для базы из 1 миллиона товаров, у каждого 500 характеристик.

    Разворачивание системы достаточно простое — скачиваем дистрибутив и устанавливаем его.

    У Neo4j сервера есть RestAPI, для php есть библиотека neo4jphp. Также есть bundle для интеграции с Symfony2 — klaussilveira/neo4j-ogm-bundle.

    В дистрибутив входит веб сервер и приложение для работы с ним, по умолчанию http://localhost:7474/
    Есть еще старая версия клиента, с другим функционалом.

    В качестве документации удобно использовать краткую документацию. Примеры кода есть в graphgist. По идее, они должны там выполнятся онлайн, но сейчас это не работает. Чтобы посмотреть код нужно перейти по ссылке из graphgist (например, сюда) и там нажать кнопку Page Source.

    Для экспериментов с Neo4j очень удобно использовать встроенный веб клиент Там можно выполнять запросы Cypher и просматривать ответ на запросы вместе со связями и характеристиками нод.

    Простые Cypher команды

    Создание ноды с меткой

    Выбрать все ноды

    Создать 2 связанные ноды

    Связать 2 существующие ноды

    Удалить все связанные ноды

    Удалить все несвязанные ноды — если попробовать запустить эту команду в базе, где есть связанные ноды — она не пройдет. Нужно удалить вначале связанные ноды.

    Выбрать товары, которым подходит критерий 3

    Сразу несколько Cypher команд веб клиент выполнять не умеет. Тут говорят, что старый клиент это умеет, но я не нашел такой возможности. Поэтому, нужно копировать по 1 строке.

    Можно выполнить создание множества нод со связями одной командой, нужно давать разные имена нодам, связям можно не давать имя

    Получится такая структура. Если у вас выглядит менее понятно — можно переставить мышкой ноды.

    Промежуточные тесты скорости Neo4j

    Пришло время протестировать скорость заполнения базы и простых выборок из большой базой.

    Для этого клонируем neo4jphp

    Базовое описание этой библиотеки есть в этом посте, поэтому я сразу выложу код для заполнения тестовой базы еxamples/test_fill_1.php

    Скрипт заполнения базы я оставил на ночь. Примерно спустя 4 часа скрипт перестал добавлять данные и сервис Neo4j начал грузить сервер на 100%. Утром по итогу работы было вставлено 78300 товаров из 8 категорий товаров.
    Результаты тестового заполнения базы — примерно 20 товаров в секунду с 200-400 связями. Не очень высокий результат — Mysql и Cassandra выдавали около 10-20 тысяч вставок в секунду (10 полей, 1 primary index, 1 индекс). Но скорость вставки для нас не критична — мы можем обновлять граф данных в фоновом режиме после редактирования товара. А вот скорость выборки данных — критична.

    Размер тестовой базы данных на диске — 1781 мегабайт. В ней храниться 78300 товаров, 4000 критериев, 15660000-31320000 связей. Общее количество объектов (нодов и связей) менее 32 миллионов — в среднем по 55 байт на сущность. Многовато, как по мне, но главное требование все же скорость выборок, а не размер базы.

    Первая попытка протестировать скорость выборки провалилась — сервер Neo4j опять «ушел» в режим 100% загрузки процессора и за несколько минут так и не выдал ответ на запрос.

    Чтобы двигаться дальше нужно разобраться, как оптимизировать запрос в Neo4j. Вначале я хотел ограничить стартовый набор нод в выборке с помощью инструкции START

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

    Добавить индексы можно командой

    Уникальный индекс можно создать командой

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

    The indexes created via Cypher are called Schema indexes, and are not to be used in the START clause. The START clause index lookups are reserved for the legacy indexes that you create via autoindexing or through the non-Cypher APIs.

    In order to use the :user index you’ve created, you can do this:

    match n:user
    where n.name=«aapo»
    return n;

    START is optional. If you do not specify explicit starting points, Cypher will try and infer starting points from your query. This is done based on node labels and predicates contained in your query. See Chapter 14, Schema for more information. In general, the START clause is only really needed when using legacy indexes.

    Так родился первый рабочий запрос

    В нашей тестовой базе индексов не обнаружено, поэтому мы создадим еще одну базу для теста другим способом. Возможности создать независимые наборы данных (аналог базы данных в MySQL) в Neo4j я не нашел. Поэтому для тестирования я просто менял путь к хранилищу данных в настройках Neo4j Community (Database location)

    Внимательные читатели возможно обнаружили пару комментариев в коде test_fill_1.php, а именно

    Читать еще:  Php for iis

    В batch режиме в Neo4jphp у меня не получилось добавить метки к нодам, а индексы почему то не сохранились. Учитывая, что Cypher перестал для меня быть китайской грамотой, я решил заполнять базу хардкорно — на чистом Cypher. Так получился test_fill_2.php

    Скорость добавления данных оказалась предсказуемо большей, чем в первом варианте.
    Тестовый скрипт с добавлением 30000 нодов и 500000 — 1000000 связей на cypher отработал за 140 секунд, база заняла на диске 62 мегабайта. При попытке запустить скрипт c $waresCount=1000 (не говоря уже о 10000 товаров) я получил ошибку «Stack overflow error». Я переписал скрипт c использованием.

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

    Скрипт выше отработал за 0.02 секунды. В целом — это вполне приемлемо, но проблема как быстро сохранять большое количество связей между нодами при апдейте свойств товара — осталась.

    Альтернативное решение

    Я решил «для очистки совести» опробовать MySQL в качестве хранилища. Связи между нодами будут храниться в отдельной таблице без дополнительной информации.

    Тестовый скрипт для заполнения базы ниже

    Заполнение базы заняло 12 секунд. Размер таблицы — 37 мегабайт. Поиск по 2 критериям занимает 0.0007 секунд

    Еще один вариант

    Под mysql есть полноценное графовое хранилище данных — но я его не тестировал. Судя по документации, он гораздо примитивнее Neo4j.

    Выводы

    Neo4j — очень крутая штука. Запрос наподобие «Выбрать контакты пользователей, которые лайкнули киноактерам, которые снялись в фильмах, в которых звучали саунтдтреки, которые были написаны музыкантами, которым я поставил лайк» в Neo4j решается тривиально. Примерно так

    Для SQL это гораздо более хлопотное занятие.

    Сравнивать полноценную графовую базу с голой таблицей индексов в MySQL — некорректно, но в рамках решения моей задачи — использование Neo4j никаких плюсов не дало.

    UPDATE. Изменил url’ы картинок, по идее должны у всех загрузаться.

    UPDATE 2. Предложили еще несколько вариантов — MongoDB, elasticsearch, solr, sphinx, OrientDB. Планирую протестировать MongoDB, результаты тестов выложу тут же.

    Neo4j

    Neo4j – графовая система управления базами данных с открытым исходным кодом, реализованная на Java. По состоянию на 2017 год считается самой распространённой графовой СУБД . Разработчик — американская компания Neo Technology, разработка ведётся с 2003 года. С 2007 года стала публично доступной. В Neo4j присутствуют все характеристики баз данных, включая соблюдение AC >

    Общие сведения

    Основные транзакционные возможности — поддержка ACID и соответствие спецификациям JTA, JTS и XA. Интерфейс программирования приложений для СУБД реализован для многих языков программирования, включая Java, Python, Clojure, Ruby, PHP, также реализовано API в стиле REST. Расширить программный интерфейс можно как с помощью серверных плагинов, так и с помощью неуправляемых расширений (unmanaged extensions); плагины могут добавлять новые ресурсы к REST-интерфейсу для конечных пользователей, а расширения позволяют получить полный контроль над программным интерфейсом, и могут содержать произвольный код, поэтому их следует использовать с осторожностью.

    В СУБД используется собственный язык запросов — Cypher, но запросы можно делать и другими способами, например, напрямую через Java API и на языке Gremlin, созданном в проекте с открытым исходным кодом TinkerPop. Cypher является не только языком запросов, но и языком манипулирования данными, так как предоставляет функции CRUD для графового хранилища.

    Архитектура

    Данные хранит в собственном формате, специализированно приспособленном для представления графовой информации, такой подход в сравнении с моделированием графовой базы данных средствами реляционной СУБД позволяет применять дополнительную оптимизацию в случае данных с более сложной структурой. Также утверждается о наличии специальных оптимизаций для SSD-накопителей, при этом для обработки графа не требуется его помещение целиком в оперативную память вычислительного узла, таким образом, возможна обработка достаточно больших графов.

    Компоненты графовой базы данных — узлы и ребра. Они могут быть дополнены собственным набором полей. Модель такой БД схематично изображена на рисунке.

    Терминология Neo4j и графовых баз данных в целом

    • graph database, графовая база данных — база данных построенная на графах — узлах и связях между ними
    • Cypher — язык для написания запросов к базе данных Neo4j (примерно, как SQL в MYSQL)
    • node, нода — объект в базе данных, узел графа. Количество узлов ограниченно 2 в степени 35

    34 биллиона

  • node label, метка ноды — используется как условный «тип ноды». Например, ноды типа movie могут быть связанны с нодами типа actor. Метки нод — регистрозависимые, причем *Cypher не выдает ошибок, если набрать не в том регистре название.
  • relation, связь — связь между двумя нодами, ребро графа. Количество связей ограниченно 2 в степени 35

    Сохранение данных в Neo4j

    Файл nodestore.db содержит определенный размер записей, содержащих информацию о ноде :

    1. Метка, которая показывает, запись активна;
    2. Указатель на первое отношение, которое содержит данная нода;
    3. Указатель на первую свойство, которое содержит данная нода.

    Нода не содержит собственного идентификатора. Так как каждая запись в nodestore.db занимает одинаковое количество места, можно рассчитать указатель на ноду.

    Файл relationshipstore.db также содержит записи одинакового размера, которые описывают отношения, но они состоят из следующих элементов:

    1. Метка, которая показывает, запись активна;
    2. Указатель на ноду, которая содержит это отношение;
    3. Указатель на ноду, к которой это отношение направлено;
    4. Вид отношения;
    5. Указатель на отношение, которое стоит впереди (в пределах данной ноды);
    6. Указатель на отношение, которое стоит сзади (в пределах данной ноды);
    7. Указатель на отношение, которое стоит впереди (в пределах Ноды, в которой это отношение направлено);
    8. Указатель на отношение, которое стоит сзади (в пределах Ноды, в которой это отношение направлено);
    9. Указатель на первое свойство данного отношения.

    Установка Neo4j

    Инструкция по установке Neo4j на разные операционные системы подробно написана на официальном сайте.

    Для наглядного примера разберем установку Neo4j для Ubuntu .

    1. Установка JDK

    Для того, чтобы установить Neo4j, необходимо сначала выполнить установку Java Development Kit JDK. Для этого обновим список пакетов:

    Далее установим (JDK)

    2.Добавление репозитория

    Вносим ключ Neo4j в менеджер пакетов apt:

    Добавляем Neo4j в список исходных кодов apt:

    Обновим менеджер пакетов

    3. Установка Neo4j (community edition)

    4. Разрешить удалённый доступ

    Откроем файл, который необходимо изменить

    Затем раскомментируем необходимые строчки

    Видео по установке

    Более детально и наглядно можно рассмотреть установку Neo4j по видео

    Язык запросов Cypher

    Язык запросов Cypher — самый распространенный язык запросов к графовым базам данных, что обусловлено его использованием в СУБД Neo4j . Cypher является декларативным языком и позволяет создавать, обновлять и удалять вершины, ребра, метки и свойства, а также управлять индексами и ограничениями. Для извлечения данных из хранилища используется запрос, содержащий шаблон фильтрации, позволяющий получать:

    • (n)—>(m) — все направленные ребра из вершины n в вершину m;
    • (n:Person) — все вершины с меткой Person;
    • (n:Person:Russian) — все вершины, имеющие обе метки Person и Russian;
    • (n:Person >) — все вершины с меткой Person и отфильтрованные по дополнительному свойству;
    • (n:Person)—>(m) — ребра между вершинами n с меткой Person и m;
    • (n)—(m) — все ненаправленные ребра между вершинами n и m;

    Выполнение простых запросов

    Разберем на примере простые запросы, такие как, создание/удаление вершины, создание/удаление ребер, добавление и изменение свойства вершины и тд. Все это подробно показано в видео ниже.

    Читайте также

    Как регламентировать перекуры в течение рабочего дня? Можно ли разрешать опаздывать к началу рабочего дня? Можно ли чатится во время…

    Я в своей жизни ещё ни разу не встречал проекта, где бы всё было сделано по правилам проектирования архитектуры, с…

    Вам нравится, когда у маркетинга и продаж развязаны руки? Когда они жгут по полной и продажи прут? Когда целевая аудитория…

    Asp phpsess > 22.01.2020 от leonius / 0

    Здравствуйте, понадобилось реализовать свой велосипед а-ля сессий на Go. Решил взять пример [ой, как неприятно это говорить] с PHP.

    Мне нужно лишь узнать, как он отделяет пользователей друг от друга? Если по IP, то тогда у людей с одинаковыми IP будет одинаковые данные в сессиях. Мне нужно узнать, какие данные он суёт в PHPSESSID, перед тем, как хешировать?

    • Вопрос задан более трёх лет назад
    • 3342 просмотра

    Кука обычно. Можно и GET-параметром, но вряд ли вас этот способ интересует.
    SID генерируется как-то так: habrahabr.ru/company/pt/blog/149746

    Но если вам даже говорить неприятно, что берёте пример с некоторого инструмента — зачем с него брать пример? И судя по вопросу про IP ничего о оном инструменте не зная?

    UPDATE ON THE PROBLEM:

    • On some browsers, we have two PHPSESSIDs.
    • One PHPSESSID is not set by me anywhere in my script
    • It has HOST (instead of DOMAIN for the PHPSESSID I set) as www.mywebsite.com
    • I have tried deleting it using setcookie: setcookie («PHPSESSID», $_COOKIE[‘PHPSESSID’], time() — 864000, ‘/’, ‘www.mywebsite.com’); but this fails.
    • An attempt to delete cookie using: setcookie («PHPSESSID», $_COOKIE[‘PHPSESSID’], time() — 864000, ‘/’); results in the PHPSESSID I set being deleted.
    • I have tried using session_name to rename the SESSION I set. This works but crashed my server severally after some minutes.
    • I am out of options.

    I am working with PHP sessions on my website.

    The session path was /folder, later on I changed to / to fit the new purpose.

    Now, old users cant login.

    It seems they now have two PHPSESSIDs stored on their browsers — one with path /folder and the other /.

    What can I do to ensure that old users can login while ensuring that the session is sitewide with «/».

    MORE INFORMATION

    When I said two phpsessionid, refer to the image

    1. The login works if I use

    A. session_set_cookie_params(864000, ‘/cv’, ‘.website.com’, 0, 1);

    but fails to work if I use:

    B. session_set_cookie_params(864000, ‘/’, ‘.website.com’, 0, 1);

    • If I use Version 2A above, the session will only be available in /cv and not be available in other website folders eg. /folder.

    UPDATE ON DELETING PHPSESSID WITH JAVASCRIPT

    • When I run alert(document.cookie), it shows all cookies except the PHPSESSID
    • Hence all attempts to delete the PHPSESSID cookie fails, whereas other cookies can be deleted.

    UPDATE ON DELETING PHPSESSID WITH PHP

      When I var_dump($_COOKIE[‘PHPSESS >13 Answers 13

    I think you are mixing up things or you should go into more detail about your setup/problem.

    PHP’s session path is the location where session data is stored on your server, not the client. See the documentation: https://secure.php.net/manual/en/session.configuration.php#ini.session.save-path

    You can move these files and replace/keep in case of collisions how you see fit. This is pretty much only restricted by read/write-permissions you have when accessing/moving stuff and your webserver-user (e.g. apache or nginx) or php-user has for reading/writing them from/to the new location.

    If by «PHPSESSID in their browser» you mean the session id is part of your urls, that is a different PHP-setting, that should be disabled anyway, see notice in the documentation: https://secure.php.net/manual/en/session.configuration.php#ini.session.use-trans-sid

    edit based on your updated question:

    There already is a nice JS-based solution for expiring the old cookie. I would go with that. if you can’t just do that, you could do a redirect to /cv have a php-script there that reads the cookie and stores the data somewhere (a database for example based on the user_id) and expire the cookie. Then you can redirect to the old page, look for the «/»-cookie and restore the data. It’s a very ugly hack, but I don’t think you can get the cookie for each path in PHP, since it’s server side and based on the session id provided by the client (but I might be wrong).

    If you are a .NET developer, this guide provides an overview of options for connecting to Neo4j. While this guide is not comprehensive it will introduce the different drivers and link to the relevant resources.

    You should be familiar with graph database concepts and the property graph model. You should have installed Neo4j and made yourself familiar with our Cypher Query language.

    Neo4j for .NET Developers

    The .NET platform allows developers to create fascinating applications utilizing graph concepts with Neo4j.

    Neo4j aims to have a great experience on Windows with an desktop installer and dedicated PowerShell modules.

    With the Neo4j 3.0 release we are happy to provide an officially supported driver for .NET.

    Neo4jDotNetDriver

    The Neo4j .NET driver is officially supported by Neo4j and connects to the database using the binary protocol. It aims to be minimal, while being idiomatic to .NET.

    The Example Project

    The Neo4j example project is a small, one page webapp for the movies database built into the Neo4j tutorial. The front-end page is the same for all drivers: movie search, movie details, and a graph visualization of actors and movies. Each backend implementation shows you how to connect to Neo4j from each of the different languages and drivers.

    You can learn more about our small, consistent example project across many different language drivers here. You will find the implementations for all drivers as individual GitHub repositories, which you can clone and deploy directly.

    Neo4j Community Drivers

    Members of the each programming language community have invested a lot of time and love to develop each one of the community drivers for Neo4j, so if you use any one of them, please provide feedback to the authors.

    The community drivers have been graciously contributed by the Neo4j community. Many of them are fully featured and well-maintained, but some may not be. Neo4j does not take any responsibility for their usability.

    Neo4jClient

    A .NET client for Neo4j, which makes it easy to write Cypher queries in C# with IntelliSense. It also supports basic CRUD and legacy indexing.

    Cypher Query Optimisations

    Make your slow cypher queries running fast

    In this post I will explain about common causes of query performance degradation in the Neo4j server. This is a follow up on the Meet the Query Log Analyzer story earlier this week.

    The Query Log Analyzer App helps you to find quickly the slow queries on the Neo4j server.

    Important Things To Remember About Cypher Execution

    Caching and Disk IO
    The Neo4j database maps the data files from disk to the Page Cache. When you do a query and the data is not in Cache the data will be loaded from disk. The second time the query is executed the data can be read from Cache, which will result in a faster query. Any query that needs the same data will now benefit from having the data in cache.
    If possible make sure that your Page Cache can contain the database.

    Query Planning
    When the Cypher engine receives a query string it compiles an execution plan for it. This execution plan is stored in the Query Cache and can be reused when the same query is fired again. The plan will be removed from that cache if the cache exceeds its limits or the data in the database has changed too much, so that estimates made during planning don’t hold true anymore. Using parameters instead of literal values will allow use of that cache, otherwise the query has to be re-parsed time and again.

    First Query Execution
    Running a query for the first time, will always be slower than running the query for the second time due to cache misses, and query planning as described above.

    Checking the Query Plan
    You can check queries by prefixing the query with the EXPLAIN or PROFILE keyword. When you find a slow query you can profile/explain a query by copying the query and the eventual parameters from the Query Log tab to the Neo4j Browser and use explain/profile to check how the query will be/is executed.

    Query Checks

    In the following sections we handle common causes which slow down the query performance. Note that there can be multiple causes which slow down the query performance, and even good queries can slow down because bad queries taking all the system resources.

    Query Parameters

    Are query parameters used for the queries where they should be? When you do not use query parameters every time a value changes the database will make a new plan for the query.

    Example queries without parameters, each time a new plan is generated:

    Example queries with parameters, the same plan is used again:

    Index Usage

    When a query is unexpectedly slow it may be caused by a typo in the query which make the query planner dec >explain in the Neo4j browser to see if all the correct indexes are used in the query and change the query if needed.

    Big Result Sets (and slow networks)

    When your query is returning a lot of data, for instance megabytes of data, then the query will take a longer time to finish. This will be worse when you have also a slow network connection between the client and the server or the client is not consuming the results quickly enough.

    These may be valid queries, however returning a lot of data in one query triggers me always to do a functional check on why you need these big result sets. Maybe a functional redesign is needed.

    Note that when you load a lot of data to populate a cache in your application layer, you may do things ‘double’. The database is also caching the data.

    Locking

    The database places lock’s on nodes and relationships when you change Node values (lock on Node) or add and remove relationships (lock on start and end Node and Relationships) or change Relationship values (lock on Relationship). The Lock will be released when the transaction is committed or rolled back.

    When at the same time multiple write queries are executed on the database and they are writing on the same Node/Relationship structure than the writes have to wait for the other writes to complete.

    This is desired behaviour of a database but how can we optimise the writes to avoid unnecessary lock’s.

    An example model can be as follows:

    Assume that in your application you want to add 50 new persons to a specific Group. You will then fire 50 calls to the database to add each person to that specific group in the same time frame (In this example calls as are send parallel to the database each having their own transaction). The first call will place a lock, and while the first call is being executed the other calls have to wait until the lock is removed. Then you will get the following pattern (the green is executing the query, with a lock, blocking the other queries):

    This can be avo >UNWIND .

    Example (parameters: personUuid list, groupUuid )

    This will be only one transaction on the server to process all the 50 members. Which is fast, and it has not to wait for locks.

    With the Query Log Analyzer you can find this scenario by using the ‘ Highlight’ function on a query. This will mark the lines in the query log file yellow for this query, and they tend to form ‘yellow’ blocks, with almost the same log time.

    Query Load

    Bes >wait for cpu-resources for execution because the server has too many queries to process at the same time.

    With the Query Log Analyzer you can use the ‘ Highlight’ function and inspect the query log lines around the same log date time. Another option can be the Timeline which shows all the queries in the log file over time.

    The amount of queries which can be handled sufficiently on a server is dependent on the single query execution time and the functional requirements. When you have a lot long running queries than the amount of queries per second the server can handle will be lower. Therefore it is very important that all the queries in the application are tuned to be as fast as possible, and that the server has enough capacity to handle the queries.

    Tuning Queries

    Queries may be slow because the cypher statement is not optimal. Therefore it is important to analyse the query log when you develop your application. Then in an early state you can find inefficient queries. The goal of query tuning is to get the lowest possible amount of db hits. In the following non-exhaustive list I mention some tips to improve your queries:

    Errors in variable names
    When you have an error in the variable names in the cypher statement, you can have unexpected results. If there is in the query a reference to a variable which is not initialised like ‘ …MATCH (a)-[: ’ or ‘ …MERGE (a)-[: ’ than this may result in a full node scan, which can slow down your query a lot. Use explain to check the query plan for your query.

    Missing colon ‘:’ for a Label or Relationship Type
    This is easy to check, if you miss the colon then the label or relationship type will be seen as a variable name, which results in full node scans etc. as described in the previous section, e.g. MATCH (Person) . instead of MATCH (:Person) . .

    Try to reduce the query working set as soon as possible
    There is a lot to say about this depending on the query. In general you can ask yourself the following things:

    • Can I move a distinct to an earlier point in the query?
    • Can I move a limit to an earlier point in the query?
    • Can I use collect on places in the query to reduce the amount of rows to be processed during execution of the query?
    • Do I use order by on the right place in the query?

    Multiple UNWIND statements
    Multiple UNWIND statements after each other will lead to cartesian products of the contents of the arrays you are unwinding. This is shown in the cypher statement below.

    Multiple OPTIONAL MATCH statements
    OPTIONAL MATCH is a power full possibility in Cypher, however it should be handled with care. When there are multiple OPTIONAL MATCH statements in one query then there may be a cartesian product which gives the database a lot of work. Assume we have the following model and cypher statement:

    Each OPTIONAL MATCH results in a stream of paths. So when for one (a) the (a)->(b)->(c ) path produced 10 entries, the (a)->(d)->(e) path produces 10 entries and the (a)->(f) path produces 10 entries this will result in 10 * 10 * 10 = 1000 intermediate rows per (a) node.

    Depending on the requirements there are several ways to handle this properly:

    • Use WITH and COLLECT and DISTINCT to reduce the intermediate results
    • Use Pattern Comprehension
      When a nested structure needs to be returned then you should use Pattern Comprehension (3.2.11.3) where you can ‘execute’ cypher patterns to build up the a tree after the RETURN statement. This is very fast:

    Instead of returning the full nodes, you can also use map-projections to only return a subset of properties:
    RETURN a < .name, b: b < .*, c: [(b)-->(c) | c ]>>

    DISTINCT and Returning tree structures
    When you return properties or create a tree structure with Pattern Comprehension then you should not use that in combination with DISTINCT or aggregation like this:

    It is much more efficient for the database to do the DISTINCT operation on a node or a simple variable before building the JSON structure:

    Links

    If you have questions regarding the query performance, you can always head to the #help-cypher channel on the Neo4j Users Slack or on the neo4j community.

  • Ссылка на основную публикацию
    Adblock
    detector