@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
и приложение не зависло
  • Вопрос задан
  • 490 просмотров
Решения вопроса 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 запросов одновременно.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Похожие вопросы