Pull to refresh

Comments 30

Обязательно проверю, как найду время. По описанию — больше рассчитано на красоту Scan в структуры, без перечисления полей. Но и про оверхед минимальный пишут. Но как показывает практика, если завязываться с Reflect — то ты ничего не контролируешь.
Про Reflect не понял. Есть какой-то опыт, что получалось «ничего не контролируешь»?
У меня пока получалось, что решения с Reflect получались только на несколько процентов медленнее аналогичного решения с кодогенерацией, если стараться этот самый Reflect кэшировать.
Зависит от задачи всё. В моем примере — reflect был скрыт под капотом драйвера. И, возможно, на выборке SELECT 20-ти записей — его оверхед не заметен вообще никак на фоне миллисекунд выполнения запроса.
Но кешировать Reflect — вполне здравая идея. Это уже относится к задаче оптимизации работы с ним, которая появляется от того, что всё-таки там есть что ускорять. Я сам его использовал раз в месте которое выполнялось один раз при инициализации. Там можно не задумываться об оптимизациях. Но вот если в цикле на миллион шагов — тут уже я бы подумал про его применение.
Протестировал. Отписал в статье результат. Если кратко, то пилюли, которая сразу всем и всё улучшает не получилось, как я и предполагал по описанию из их README.
и не должно было получиться
sqle это про удобство а не про производительность
Работал в этой компании. Нагрузки действительно большие.
Но не думаю что именно эта библиоетека хоть как то решает проблемы производительности

Доброго дня, коллега!
В product service нам она дала разницы до 8x по сравнению со стандартной.

JekaMas не поделитесь в двух словах в каких случаях/запросах она дает выиграш?
У нас были большие структуры со множеством вложений друг в друга, в каждой структуре могло быть 100 и более полей различного типа, который заранее, до выполнения запроса, знать было невозможно. Базы были на десятки миллионов записей и более.
предположу что разница в 8 раз была из-за неоптимального собственного кода
Это не так. Задача писать библиотеку родилась после того, как за два года исчерпали другие возможности.
Вроде бы статья должна называться «ускоряем Go драйвер под MySQL»?
Вот драйвер я никак не ускорял, признаюсь честно. Исследовал внутренности, пробовал разные протоколы и типы данных — это да. Цель была проста: получить как можно более быструю скорость обработки SELECT c целочисленными полями. Её и достигал.
UFO just landed and posted this here
Это да, совсем не по канонам.
Однако, как я понял, эту программу использует только автор в личных целях, по этому иньекций можно не бояться
Скрипт запускается по крону раз в сутки через докер. Инъекций там быть не должно.
Мне кажется, что всё можно сделать на чистом SQL, используя count, sum и group by
Естественно, можно и таким путем пойти.
Для примера я оставил только 5 объектов. Их на самом деле больше. Есть ещё связки типа баннер-кампания. Есть связки через дополнительные поля, которые в других таблицах. В итоге если делать 10 запросов GROUP BY подряд с INSERT'ом в таблицы статистики — мы нагрузим MySQL, залочим таблицы. Плюс есть ещё особая логика подсчета дополнительных параметров — я её выбросил для чистоты експеримента. Ну и в планах делать антифрод по прошествии суток — там уже на SQL никак не провести анализ — нужны деревья.
Язык Go, как мне кажется, больше подходит для работы с большими наборами данных. Проблема в универсальных драйверах БД, которые стремясь угодить программисту, дают оверхед на конвертацию пришедшего из базы туда-сюда-обратно. Вот я и выяснил для себя какая связка дает большую скорость, далее, видимо, закодирую её в каких-то функциях, чтобы перевести остальную часть проекта на более производительные SELECT-ы. Ну и решил поделиться с сообществом, ибо такие задачи для Go должны возникать в любом серьезном проекте.

Тупиковый путь, имхо. Забирать все данные из базы, что бы потом обработать их на Go. И пересчет статистики ночью — тоже. Часто статистика нужна в реальном времени (с задержкой в пять минут, а не ночь). Хорошо, если вам пока хватает раз в сутки.

Я же не описывал весь движок — статья не про это. Статистика за сегодня из сервера сбрасывается раз в минуту в базу. Плюс фронт-енд может запросить напрямую у сервера json с актуальными полями по любому объекту. Так что у нас полный риалтайм касаемо сегодняшних данных.
Но допустим вам надо вычесть из статистики фрод-показы и актуализировать вчерашние данные. Как тогда быть?

Для сложной логики вам виднее. Если без получения полных данных никак, значит никак. Но и здесь направление "раз в сутки" выглядит не очень хорошо — ведь сутки у всех заканчиваются в разное время. То, что для вас — ночь, для других — разгар дня.


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

В итоге если делать 10 запросов GROUP BY подряд с INSERT'ом в таблицы статистики — мы нагрузим MySQL, залочим таблицы.

Зачем? Просто выбрать с GROUP BY, а дальше то же самое, просто не someCounter.Inc(someId), а someCounter.Add(someId, someCount).


И скорость выборки 36 секунд для почти 56 миллионов записей.

56 миллионов записей, по 5 4-байтовых интов в каждой, 56M*20 = 1120M, больше гигабайта. Мне кажется, основное время уходит на передачу данных, а конвертация занимает копейки.

Если делать GROUP BY campaignID — как это поможет посчитать кол-во по bannerID или siteID? Делать потом ещё 10 запросов?
Плюс GROUP BY на MySQL не такой шустрый, например
SELECT campaignID, COUNT(*) FROM hit_20180507 GROUP BY campaignID
Занимает 8,6 секунд на этом сервере на прогретых данных. А группировка по двум полям уже 12,9 секунд.

36 секунд я путем манипуляций превратил в 27 секунд. И судя по профилировщику — там можно выжать ещё пару секунд, убрав ненужные конвертации через строку. Данные находятся на localhost-сервере, 5 Гб на диске SSD, ещё и в кеше. Думаю сама передача занимает секунд 10.
Проверил — оставил цикл
for rows.Next() {
c++
}
итог — 11.9 секунд. Остальное время тратится на доступ к хеш-мапам, ну и конечно же на Scan.
36 секунд я путем манипуляций превратил в 27 секунд.

9 секунд на 56 миллионов итераций. Если итераций будет меньше, то и экономия будет не такая заметная. Но согласен, я слишком преувеличил.


Делать потом ещё 10 запросов?

Да. Можно сделать один с GROUP BY по всем параметрам, остальное в приложении досчитать. Счет строк будет на тысячи, а не на миллионы. Но судя по цифрам это не подходит. Хотя все равно многовато как-то.

Go как раз предназначен для обработки миллионов строк. Не надо стесняться его применять для этого. За 26 секунд обработать результат работы целых суток — по-моему хорошее соотношение, которое оставляет задел на стократный рост. Хотя на С++ думаю в 2 раза быстрей было бы по скорости обработки. Но есть ещё такой параметр как время на разработку, ту же компиляцию, отладку и поддержку. И тут Go для меня выигрывает. Плюс приятно в проекте иметь один язык — и для сервера и для скриптов обработки.
Если бы я писал обработку этого запроса на PHP, к примеру, я бы тоже постарался уменьшить число строк с помощью БД. Там цикл на сто тысяч уже может показаться «вечным» и оптимизировать что-то как здесь — не представляется даже возможным.

В некоторых случаях после group by количество строк будет примерно равно количеству до (может в разы сократится, но не в десять). Рекламная сеть, как раз такой случай там каждый второй клик может быть уникален с точки зрения набора группировочных параметров.

Кликхаус будет следующий объект для тестов. Сейчас вставляю параллельно в 2 базы — пока длится процесс перехода. А какой библиотекой вы вычитываете в Go миллионы строк?
Sign up to leave a comment.

Articles