Вставка 29000 строк через Laravel API в TimescaleDB занимает 22 минуты, тогда как вставка через Go в Influx1.7 занимает 256 мс.

Мне нужно вставить 29000 строк в 2 DB: TimescaleDB и Influx на моем локальном компьютере (Ubuntu 20.04 8GB Ram)

Когда я вставляю в приток, это довольно быстро. Я делаю это с помощью службы Golang, которая разбивает данные на части из 10000 строк и вставляет их в поток. это занимает 256 мс

Но вставить в TimescaleDB совсем другое дело. Сначала я использую Laravel 8 / PHP8. Может быть, не самый быстрый, но я застрял с ним.

Затем я вставляю куски по 100 строк с функцией upsert().

Это занимает 22 минуты для 29000 строк. Я использую linux-valet, который использует сервер nginx, php8 и postgres / timescaleDB 12.

Я попытался воспроизвести свои производственные настройки, в моем .env я установил

APP_ENV=production
APP_DEBUG=false

И я казнил php artisan optimize

Еще 22 мин.

В производстве у меня нет этой проблемы, потому что я вставляю только 5648 строк, и это занимает 123 мс для inlux и 1 секунду для TimescaleDB. Единственная разница, я полагаю, заключается в том, что в производственной среде я все еще использую PHP7.4

Как для локальной, так и для производственной среды конфигурация timescaleDB является конфигурацией по умолчанию.

Я понимаю, что Laravel медленна для массовой вставки и что Go намного быстрее для этой задачи.

Почему такая разница и что мне сделать, чтобы она стала быстрее?

РЕДАКТИРОВАТЬ: добавлен фрагмент кода Laravel для вставки в TimescaleDB:

public function store(Request $request)
    {
        ini_set('max_execution_time', -1);
        $body = $request->getContent();
        $meter = json_decode($body, true);
        $chunkSize = 100;
        $measures = $meter['Measures'];
        $chunkedRaws = collect($measures)->chunk($chunkSize);
        $ok = 0;
        $nok = 0;
        foreach ($chunkedRaws as $raws) {
            $raws = $raws->map(function ($raw) use ($meter) {
                if (!isset($raw['s'])) {
                    $source = "Pamela";
                } else {
                    $source = $raw['s'];
                }
                return new \App\Models\Raw([
                    'operation_id' => $meter['OperationID'],
                    'meter_id' => $meter['ID'],
                    'conso_prod' => $meter['ConsoProd'],
                    'timestep' => $meter['Timestep'],
                    'unit' => $meter['Unit'],
                    'source' => $source,
                    'time' => Carbon::parse($raw['t'])->toIso8601ZuluString(),
                    'delta' => $raw['d'],
                ]);
            })->toArray();
            try {
                DB::disableQueryLog();
                $raws = collect($raws)->unique(function ($item) { // eliminate duplicates
                    return $item['time'] . $item['operation_id'] . $item['meter_id'] . $item['conso_prod'];
                })->toArray();
                DB::table('raws')->upsert($raws, ['time', 'operation_id', 'meter_id', 'conso_prod'], ['delta']);
//                DB::table('raws')->insert($raws);
                $ok += $chunkSize;
            } catch (PDOException $e) {
                $nok += $chunkSize;
                Log::warning($e->getMessage());
            }
        }
        return response()->json(['message' => $ok . " Raws has been inserted,\n " . $nok . " Failed"], 200);
    }

person Juliatzin    schedule 03.02.2021    source источник
comment
Почему куски по 100, а не по 10000? Есть какой-нибудь код, которым вы можете поделиться с нами? Может быть, есть что-то, что можно улучшить.   -  person Felippe Duarte    schedule 03.02.2021
comment
Когда я его кодировал, я не видел разницы между кусками по 100 и кусками по 1000. Да, я могу показать вам код, но также есть много мест, где я мог бы внести изменения, которые в конечном итоге могут иметь большее влияние, чем код: Конфигурация шкалы времени, конфигурация Nginx, конфигурация PHP, конфигурация Laravel и т. Д. Я все равно покажу вам код! Может быть, здесь есть что-то, что я могу оптимизировать!   -  person Juliatzin    schedule 03.02.2021
comment
Мне кажется, это нормально. Не думаю, что смогу вам сильно помочь. Что я бы сделал в вашей ситуации, так это echo time() вдоль вашего кода (до и после ->map, до и после ->unique, до и после ->upsert) и проверьте, какой шаг занимает больше всего времени, и если он почти одинаков для каждого фрагмента, чтобы изолировать проблему. Laravel и Nginx, вероятно, не являются частью проблемы. Шансы выше в конфигурации шкалы времени, проблеме с драйвером БД, ограничении сети или машины базы данных (оперативная память / диск).   -  person Felippe Duarte    schedule 03.02.2021


Ответы (2)


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

Если вы посмотрите на массовые вставки с помощью стандартного инструмента тестирования (TSBS), вы увидите, что Timescale превосходит Influx, значительно, когда ваша мощность больше:

Любопытно: почему вы застряли на Laravel 8 / PHP8 для TimescaleDB, но можете использовать бегун Go для InfluxDB?

Тем не менее, несколько предложений:

  • Даже при использовании php одно из предложений состоит в том, чтобы вы оценили фактическую производительность вставки, в отличие от накладных расходов на маршаллинг php (что может быть значительным. Один из способов сделать это - обернуть фактический вызов вставки / SQL в таймер, а не фактически помещая ваш ORM-маршаллинг в этот цикл.Это, по крайней мере, поможет вам лучше понять, является ли узким местом TimescaleDB или Laravel.

  • Попробуйте сделать то же самое, что и вставка, по сравнению с UPSERT.

  • Сам менее знаком с Laravel, но убедитесь, что вы используете пул соединений с БД между вставками, чтобы ORM не устанавливал новое соединение для каждой вставки. Я думаю

person Mike Freedman    schedule 03.02.2021
comment
Что вы имеете в виду, когда сравниваете яблоко с апельсином? Застрял с Laravel, потому что это тот стек, который мы используем. Мы переходим с Influx / Go на Timescale / Php. Я мог бы закодировать конкретную службу на Go для вставки в Timescale, но это займет у меня время, которого у меня сейчас нет. - person Juliatzin; 04.02.2021
comment
После измерения времени для каждого моего кода я могу сказать, что это $ raws- ›map (), который занимает 60 секунд. Если я разделю 29000 на 60, у меня будет 29 минут, что примерно соответствует тому, что я получаю. Я посмотрю, как я могу оптимизировать функцию карты - person Juliatzin; 04.02.2021

Я согласен с Майком, что это скорее яблоки к апельсинам по ряду причин. Из вашего описания кажется, что с транзакциями должна происходить какая-то блокировка, действительно за пределами TimescaleDB. У меня также нет опыта работы с Laravel, но я уверен, что TimescaleDB / Postgres не ваша проблема в этом масштабе. Недавно я проводил множество сравнительных тестов приема с TimescaleDB с использованием TSBS (написанного на Golang), и даже на экземпляре 2vCPU / 8GB я постоянно достигал 75-85K строк в секунду в таблице с 10 значениями (столбцами), упакованными в 10000 строк, используя 24 воркера (соединения)

Я предполагаю, что вы используете плагин laravel-upsert (https://github.com/staudenmeir/laravel-upsert), который, похоже, преобразует ваши запросы в INSERT INTO... ON CONFLICT DO UPDATE SET.... Хотя это само по себе не является конкретной проблемой (как правило), вы можете легко получить несколько подключений / транзакций, пытающихся выбрать, а затем обновить строки на одной странице (принимая эксклюзивные блокировки), вызывая блокировку и устраняя ее. Это может быть особой проблемой, если у вас нет временного порядка для ваших данных и несколько пакетов выполняются одновременно (несколько потоков / соединений).

Вы видите какие-либо доказательства чего-то подобного в своих журналах Postgres?

person Ryan    schedule 03.02.2021
comment
Я использовал laravel-upsert, но тогда он был закодирован внутри laravel, так что теперь, используя встроенную функцию laravel. Все еще делаю то же самое НА КОНФЛИКТНОМ ОБНОВЛЕНИИ. Я ничего не вижу в журнале postgres :( - person Juliatzin; 04.02.2021
comment
В ПОРЯДКЕ. Похоже, вы обнаружили проблему с функцией карты. Довольно безумно, что это заняло бы столько времени! - person Ryan; 05.02.2021