Приложение Laravel испытывает таймауты базы данных при использовании методов firstOrNew или firstOrCreate в рабочей среде с большим количеством записей

Я столкнулся с проблемами производительности приложения Laravel, развернутого в производственной среде. Приложение предназначено для регистрации просмотров видео и использует метод firstOrNew для получения или создания записей устройства для каждого просмотра. Однако независимо от того, является ли трафик интенсивным или умеренным, приложение сталкивается с постоянными таймаутами базы данных, особенно при взаимодействии с таблицей devices, которая содержит около 20 миллионов записей. Кроме того, бывают случаи, когда выполнение одного запроса к базе данных занимает более 30 секунд. Стоит отметить, что используется база данных MariaDB.

public function store(Request $request){
        // define message for when view is already registered
        $mssg = 'view already exist';

        try{
            // Start database transaction
            // Retry 3 times if first attempt fails (this is done to avoid db deadlock issues)
            DB::transaction(function () use ($request, $mssg) {
                // get request data
                $ip = $request->ip();
                $videoId = $request->input('video_id');
                $hostname = $request->has('embed_source') ? $request->input('embed_source') : NULL;

                // check if view already counted
                if (View::where(['ip' => $ip, 'video_id' => $videoId])->count()) throw new Exception($mssg);

                // Register new view
                // Firstly, retrieve or create device
                $agent = new Agent();
                $position = Location::get($ip);
                $device = Device::firstOrNew(
                    [
                        'type' => $agent->isDesktop() ? 'desktop' : ($agent->isTablet() ? 'tablet' : ($agent->isPhone() ? 'phone' : 'unknown')),
                        'browser' => strtolower($agent->browser()),
                        'country' => $position && $position->countryName ? strtolower($position->countryName) : null,
                        'region' => $position && $position->regionName ? strtolower($position->regionName) : null,
                        'city' => $position && $position->cityName ? strtolower($position->cityName) : null,
                        'hostname' => $hostname,
                    ],
                    [
                        'long' => $position && $position->longitude ? $position->longitude : null,
                        'lat' => $position && $position->latitude ? $position->latitude : null,
                    ]
                );
                $device->save();

                // Secondly, save new view with device ID
                $view = new View();
                $view->video_id = $videoId;
                $view->device_id = $device->id;
                $view->ip = $ip;
                $view->watched_duration = 1;
                $view->save();
                // Lastly, increment views count in videos table
                Video::where('id', $videoId)->increment('total_views');

            }, 3);
            
            return response(['viewed' => true], 200);
            
        }catch(\Throwable $e){
            if ($e->getMessage() == $mssg) return response(['message' => $mssg], 200);

            info($e->getMessage(), [
                'title' => 'COULD NOT REGISTER VIEW',
            ]);
            return response(['error' => $e->getMessage()], 500);
        }
    }

Я не уверен в основной причине этой проблемы и в том, как лучше всего ее решить.

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

Мы будем очень признательны за любые идеи, предложения или лучшие практики. Спасибо заранее за вашу помощь!

Довольно сложно предложить какую-либо оптимизацию запросов к базе данных, если мы не знаем, какие запросы к базе данных выполняются, каков план их выполнения и какова базовая структура данных.

Shadow 14.04.2024 06:10
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
1
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

ваш вопрос неясен, вы столкнулись с проблемой при получении 20 миллионов данных или вы пытаетесь вставить 20 миллионов данных за один раз, помимо этого, насколько я понимаю, ответ на ваш один запрос занимает около 30 секунд, что очень много для любой пользователь, для этого вы можете использовать Jobs/Queue в laravel, чтобы любая часть вставки данных происходила в бэкэнде, и ваш сервер также не загружал слишком много

Ответ принят как подходящий

Вот некоторые (возможные) улучшения:

  1. Не используйте count() для проверки наличия записи, используйте exists()

  2. Создайте составной уникальный индекс для столбцов type, browser, country, region, city, hostname в базе данных или создайте уникальный столбец hash и проверьте хэш этих значений.

  3. Если вы добавили отдельные индексы для этих столбцов, удалите их. Это может замедлить операции записи.

Другие вопросы по теме