@O_Nayre

Как спастись от Out of memory во время работы GD и записи в Mysql?

Проблема кратко - Out of memory во время загрузки изображений при относительно "медленном" соединении. На стороне клиента загружается не более 3х изображений одновременно. В приложении картинка конвертится и запись о ней отправляется в таблицу где сейчас порядка 200k записей (размер таблицы 12Mb).

Подбробней.
Out of memory вылетает при очереди загрузки 50+ изображений, где-то под конец очереди на клиенте - на 40м-50м изображнеии. И следом за нехваткой памяти ложится Mysql. Судя по всему, проблема только у меня из-за "медленного" соединения через океан, так как локальные пользователи ни разу не жаловались и сервер не ложился - ошибка вылетает только у меня на некоторых подключениях.

В логах:
Symfony\Component\Debug\Exception\FatalErrorException
Out of memory (allocated 25165824) (tried to allocate 8192 bytes)
vendor/intervention/image/src/Intervention/Image/Gd/Decoder.php at line 136
$canvas = imagecreatetruecolor($width, $height);

после чего ложится мускул с писком - Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock'

На стороне клиента загруза идет через Dropzone
parallelUploads: "3",
maxFilesize: "5",


На backend-де обработка в Intervention\Image
// уже и не помню где не хватало... м.б. на старом сервере или еще раньше
         ini_set('memory_limit','256M');
        $uploadedImage = Image::make($image);
        $uploadSuccess = $uploadedImage->resize(1024, null, function ($constraint) {
            $constraint->aspectRatio();
        })->save( public_path( $img_path ), 85 );
        if( $uploadSuccess ) {
            $image = new \App\Image;
            $image->image = $filename;
            $image->width = $uploadSuccess->width();
            $image->height = $uploadSuccess->height();
            $image->position = $position;
            $dbInsert = $image->save();
            //Free up memory? Есть ли смысл?
            // $uploadedImage->destroy();
            if($dbInsert){
                return Response::json(['success'], 200);
            }else{
                return Response::json('error', 400);
            }
        } else {
            return Response::json('error', 400);
        }


Настройки php.ini
memory_limit 128M
upload_max_filesize 2M
max_file_uploads 20
max_execution_time 30


не знаю - нужно ли - нагрузка при стабильной работе
5c78228709b6a338037053.png
при загрузке и тестах сделаю ночью и добавлю

Из того что нагуглил сам - загрузку с очисткой памяти - буду тестировать ночью.
Вопросы обычные - куда копать, чего читать? Есть ли вариант слегка отделаться малой кровью слегка урезав выделенную память на Mysql и добавить на Php? Если да - то как правильно ее рассчиать?

UPD:
данный параметр оказался legacy с godaddy и его отключение как минимум не ухудшило работу
ini_set('memory_limit','256M');
при отключенном/закоментированном этом параметре
при низкой посещаемости (ночью) постановке в очередь загрузки 86и 400kb изображений
дала следующую нагрузку
5c785c0a1ea9e425151557.png
и приложение не зависло
  • Вопрос задан
  • 470 просмотров
Решения вопроса 1
@neol
У вас MySQL использует гигабайт памяти под buffer_pool при размере базы в 70 Мб:
InnoDB buffer pool / data size: 1.0G/70.8M


Этот буфер должен быть чуть больше размера базы, так что если поменять значение в конфиге MySQL на
innodb_buffer_pool_size = 80M
то ваша проблема скорее всего будет решена. Можете выделить чуть больше памяти в зависимости от прогнозируемого роста БД.

Highest usage of available connections: 8% (12/136)

Это говорит о том, что max_connections = 136 для вас сильно много.
Лучше поставить чуть больше pm.max_children. С проблемой это не связано, но mysqltuner так будет показывать более похожие на реальность цифры.

После этих изменений посмотрите сколько памяти будет потреблять php при условиях, в которых у вас сейчас возникает нехватка памяти:
ps aux --sort rss | grep php | tail
Возможно имеет смысл уменьшить pm.max_children

Ещё можно попробовать использовать imagick вместо gd, он сильно экономнее по памяти на больших разрешениях.

UPD: на скриншоте в вашем UPD видно, что процессы php потребляют по 50 Мб каждый в пике. При pm.max_children = 50 это означает, что php может использовать до 50*50 = 2500 Мб из 1876 имеющихся. Либо оптимизируйте скрипты по памяти, либо уменьшите количество параллельных процессов php. Я бы предпочёл второй вариант, так как не похоже, что ваш сервер способен эффективно обрабатывать 50 запросов одновременно.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы