Adminer и «Session expired»

Суть проблемы

При входе в Adminer выкидывается ошибка «Session expired, please login again.»

Решение

Создать файл с именем, например, `adminer0.php` со следующим кодом

<?php
ini_set('session.save_path',  '/tmp/');
require_once __DIR__.'/adminer-4.7.2.php';

Оптимизация скорости работы сайтов

Разделим оптимизацию на несколько этапов

  • Бэкенд и запросы к бд
  • Конфигурация и производительность сервера
  • Фронтенд — работа со скриптами, стилями, статикой, изображениями и пр.

Подробнее по шагам

1. Ищем узкие места в производительности

В зависимости от фреймворка / цмс существуют разные варианты отладочных консолей / дебаг-панелей.

На примере bitrix протестировать производительность можно в админке — /bitrix/admin/perfmon_panel.php?lang=ru
Результат запуска теста предоставит нам список наиболее загруженных страниц, среднее время их загрузки и количество запросов на каждый хит.

Оптимизируем каждую из нагруженных страниц, сортируя по количеству запросов и времени их выполнения следующим образом:

  1. Убираем запросы в цикле (пример ниже)
  2. В запрашиваемых данных оставляем только нужные. Т.е. в массиве select должны быть указаны только те поля, с котороми работает скрипт
  3. Если время запроса превышает 0.001 секунду — оптимизируем запрос, думаем над созданием индексов
  4. Проверяем, настроено ли кэширование и параметры, от которого оно зависит.
  5. В случае Битрикса можно использовать их запатентованную технологию Автокомпозит, которая сохраняет созданые копии страниц в директорию /bitrix/html_pages/ и при загрузке страницы отдает их, подгружая динамический контент в фоновом режиме.
    В результате пользователю страница отдается моментально
Примеры

Плохо

foreach ($rsObjects as $obj) {
    $params = [
        'filter' => [
            'OBJECT_ID' => $obj['ID']
        ]
    ];
    while ($news = NewsTable::getList($params)->fetchAll()) {
        $arMenu[] = $news['LINK'];
    }
}

Лучше

$objects = [];
foreach ($rsObjects as $obj) {
    $objects[] =  $obj['ID'];
}

$params = [
    'select' => ['LINK'],
    'filter' => ['OBJECT_ID' => $objects]
];
while ($news = NewsTable::getList($params)->fetchAll()) {
    $arMenu[] = $news['LINK'];
}

Далее анализируем лог sql запросов /bitrix/admin/perfmon_sql_list.php?PAGEN_1=1&SIZEN_1=20&lang=ru&by=QUERY_TIME&order=desc,
если готового функционала в системе нет, включаем slowlog (как это сделать можно почитать здесь) и работаем с ним.
Выделяем самые медленные запросы и оптимизируем их.

Подробно про оптимизацию запросов написано здесь

2. Работаем с настройками сервера

  1. Версия Mysql должна быть не ниже выше 5.7, версия PHP — не ниже 7.0
  2. В случае битрикса проверка /bitrix/admin/site_checker.php?lang=ru не должна выдавать ошибок
  3. Желательно наличие акселератора PHP (OPcache, XCache, APC и другого). Лучше всего OPcache

3. Оптимизируем клиентскую часть

Проверяем сайт через:

Что должно быть реализовано:

  1. Статическая информация должна кэшироваться браузером (проверить настройки в .htaccess)
  2. Для загрузки картинок, не входящих в первый экран, желательно использовать lazy-load. Это так же касается вторых, третьих и тд картинок на слайдерах
  3. Картинки должны быть оптимизированы для веба и отресайзены на нужные размеры
  4. Использовать для картинок тэг picture
  5. Минификация / обфускация js скриптов
  6. Количество подгружаемых на странице js-скриптов должно быть ограничего, т.к. стандартно браузеры могут загружать файлы не более чем в 6-10 потоков с одного хоста.
    Т.е. работать будет быстрее, если собрать несколько мелких js в один большой, чем загружать 40 маленьких отдельно.
    В этом очень большой минус require.js, который есть на старых проектах.
    В идеале сайты нужно переводить на webpack.
    Также как решение мы можем разнести js-скрипты на разные хосты, тогда ограничение будет от 17 до 60ти одновременного загружаемых файлов. То же самое касается и картинок.
  7. Сторонние стандартные библиотеки лучше подгружать извне, т.к. существует вероятность, что браузер пользователя уже их выкачал и закэшировал

Ещё информация о клиентской стороне оптимизации здесь

Источник

1. Оптимизация структуры таблиц

1 Просмотрите как выполняется ваш запрос с помощью синтаксиса EXPLAIN.

Посмотреть, как MySQL выполняет ваш запрос можно с помощью синтаксиса EXPLAIN. Его использование может помочь определить слабые места в производительности запроса, а так же в структуре таблиц. В качестве результата запроса EXPLAIN возвратит данные, которые покажут, какие используются индексы, каким образом выбираются данные из таблиц, как сортируются, и т.д. Для этого достаточно добавить вначале SELECT-запроса ключевое слово EXPLAIN, после чего будет показана таблица, с данными.

1.2 Индексируйте поля по которым производится поиск

Под индексом в частном случае подразумевается индекс полей, по которым вы производите поиск, это позволит улучшить скорость поиска. Кстати обычный индекс не может срабатывать с условиями в виде регулярных выражений:

// тут сработает индекс
city LIKE ‘shym%’

// тут же индекс задействован не будет
city LIKE ‘%shymkent%’

Чтобы сделать индекс для условий с регулярными выражениями вам следует воспользоваться полнотекстовым индексом, либо подумать над своей системой индекса.

1.3 Индексируйте поля по которым объединяются таблицы

Если вы используйте множество объединений таблиц, то вам стоит задуматься о том, чтобы поля, участвующих в объединении были проиндексированы в обеих таблицах. Это дело влияет на то, как MySQL будет производить внутреннюю оптимизацию объединений полей таблицы. Поля объединения должны быть одного типа и одной кодировки. Т.е. к примеру, если одно поле будет иметь тип DECIMAL, а другое INT, то MySQL не сможет воспользоваться индексом.

1.4 Добавляйте поле ID для всех таблиц

Каждая таблица в хорошем её исполнении должна иметь поле id типа INT, которое является первичным ключом (PRIMARY_KEY), и AUTO_INCREMENT. Кроме того, для поля нужно указать параметр UNSIGNED, который означает то, что значение всегда будет положительным.
В MySQL есть внутренние операции, которые могут использовать первичный ключ, это играет роль для сложных конфигураций баз данных, таких как кластеры, распараллеливание, и т.д.
Кроме того, если есть несколько таблиц, и необходимо выполнить объединенный запрос, то тут ID таблиц окажется весьма кстати.
ENUM как альтернатива VARCHAR

Давайте представим, вы хотите добавить поле в таблице, которое должно содержать определенный набор значений. Традиционно многие программисты выставляют тип VARCHAR для полей. Однако есть и другой тип поля, который гораздо быстрей и компактнее. Значения в данном типе хранятся так же, как и TINYINT, но отображаются как в строковом типе.

1.5 Используйте значение NOT NULL вместо NULL

Поля NULL занимают больше места в записи, из-за того что возникает необходимость отмечать это NULL значение. Таблицы MyISAM, поля с NULL хранятся таким образом, что каждое поле занимает 1 дополнительный бит, который округляется до ближайшего байта. Если использование NULL в поле не принципиально, то рекомендуется использовать NOT NULL.

1.6 Вы можете хранить IP-адреса в поле с типом INT (UNSIGNED)

Для хранения IP-адресов в привычном виде многие хранят в таблице с полем типа VARCHAR(15), и лишь не многие используют целочисленный тип для этого. Плюсы в том, тип INT занимает 4 байта и имеет фиксированный размер поля. Поле типа INT должно быть UNSIGNED, т.е. целочисленным, в запросе следует использовать функцию INET_ATON(), которая будет конвертировать IP-адрес в число. Обратное преобразование выполняется с помощью функции INET_NTOA().

$res = "UPDATE hosts SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE id = $host_id";

1.7 Используйте статичные таблицы

Статичная таблица это обычная таблица в базе, за исключеним того, что каждое поле в таблице имеет фиксированный размер. Если в таблице есть колонки, не фиксированной длины, к примеру, это могут быть: VARCHAR, TEXT, BLOB, она перестает быть статичной, и будет обрабатываться MySQL немного иначе. Статичные таблицы, или их можно ещё назвать таблицами фиксированного размера работают быстрее не статичных. Записи из таких таблицах будут просматриваться быстрее, при необходимости выбора нужной строки MySQL быстро вычислит её позицию. Если поле имеет не фиксированный размер, то в этом случае поиск производиться по индексу. Есть и другие плюсы использования статических таблиц, дело в том, что эти таблицы проще кэшируются, а так же восстанавливаются после падения базы данных.

1.8 Используйте вертикальное разделение

Вертикальное разделение – подразумевает разделение таблицы по столбцам, в целях увеличения производительности таблице. К примеру, если у вас в таблице есть поля, которые используются очень редко, либо это поля с переменной длиной, то их можно вынести в отдельную таблицу, таким образом, вы разгружаете таблицу, увеличивая тем самым скорость работы с ней.

1.9 Стремитесь использовать поля небольшого размера

Как известно данные базы хранятся на жестком диске, это зачастую это может оказаться одним из слабых мест в веб-приложении. Дело в том, что записи небольшого размера являются более предпочтительными, т.к. использование их уменьшает работу с жестким диском. Если вы уверенны, что конкретная таблица будет хранить мало строк, то рациональным решением будет использование типов полей, с минимальными возможными значениями. К примеру, если основной ключ имеет тип INT, и вы будете хранить в таблице лишь небольшое кол-во данных, то лучше сделать его типа MEDIUMINT, SMALLINT или даже TINYINT.

В MySQL Docs прописан ряд требований к хранению разных типов данных. Если ожидается, что таблица не будет содержать слишком большое количество записей, то нет причин хранить первичный ключ в полях типа INT, MEDIUMINT, SMALLINT, а в отдельных случаях даже TINYINT. Если в формате даты вам не нужны составляющие времени (часы : минуты), то используйте поля типа DATE вместо DATETIME.

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

1.10 Выбирайте тип таблиц под свои задачи

Два широко известных типа таблиц на сегодняшний день, это MyISAM и InnoDB, каждый из них имеет свои положительные и отрицательные стороны. К примеру, MyISAM хорошо считывает данные из таблиц в большом объеме, одно он более медлителен при записи. Он так же хорошо выполняет запросы вида SELECT COUNT(*).
Механизм хранения данных у InnoDB более сложный, чем у MyISAM, однако, он поддерживает блокировку строк, что является положительной стороной при масштабировании. Поэтому сказать, что одно лучше другого нельзя, да и не правильно, нужно выбирать тип исходя из своих потребностей.

1.11 Используйте подсказки от PROCEDURE ANALYSE()

PROCEDURE ANALYSE() анализирует структуру вашей таблицы и данные в ней, и выдает возможные советы по оптимизации. Это возможно только при наличии реальных данных в таблице, т.к. анализ делается в основном на их основе.
Например, если вы создали первичный ключ типа INT, а записей не очень много, MySQL может предложить заменить его на MEDIUMINT. Или, если используется VARCHAR в котором есть несколько уникальных значений, будет предложен ENUM.
В phpmyadmin в структуре таблице есть ссылка «Анализ структуры таблицы», результат которой может быть, например, следующим:

Учтите, что это только советы. Если вы добавите еще записей, они могут стать не актуальными. В конечном итоге вам решать — использовать их или нет.

2 Оптимизация запросов

2.1 Делайте запросы MySQL удобными для кэширования

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

// этот запрос MySQL закэшировать не сможет
$res = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");

// сделать можно иначе
$today = date("Y-m-d");
$res = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

Дело в том, что в первом запросе была использована функция CURDATE(), особенность её работы не позволяет помещать результаты запроса в кэш. Значение даты можно предварительно записать в строку запроса, это позволит исключить использование функции CURDATE() в запросе.
По аналогии есть и другие функции, которые не кэшируются самим сервером MySQL, среди них RAND(), NOW() а так же другие функции, результат которых недетерминирован.

2.2 Просмотрите как выполняется ваш запрос с помощью синтаксиса EXPLAIN

Посмотреть, как MySQL выполняет ваш запрос можно с помощью синтаксиса EXPLAIN. Его использование может помочь определить слабые места в производительности запроса, а так же в структуре таблиц. В качестве результата запроса EXPLAIN возвратит данные, которые покажут, какие используются индексы, каким образом выбираются данные из таблиц, как сортируются, и т.д. Для этого достаточно добавить вначале SELECT-запроса ключевое слово EXPLAIN, после чего будет показана таблица, с данными.

2.3 Когда вам нужна одна запись, выставляйте LIMIT 1

Не мало случаев, когда из таблицы вам требуется проверить наличие хотябы одной записи, в этом случае рекомендуется добавить к запросу параметр LIMIT 1. Это сделает его более оптимальным, т.к. механизм базы данных после нахождения первой записи остановит выборку данных, вместо того чтобы выбирать все данные. Вы экономите ресурсы.

// запрос города с кодом Shymkent из базы
$res = mysql_query("SELECT * FROM location WHERE city = 'Shymkent'");
if (mysql_num_rows($res) > 0) {
...
}

// добавляем LIMIT 1 для оптимизации запроса
$res = mysql_query("SELECT * FROM location WHERE city = 'Shymkent' LIMIT 1");
if (mysql_num_rows($res) > 0) {
...
}

2.2 Найдите альтернативу вместо ORDER BY RAND()

Использование рандомной сортировки действительно является весьма удобным, и об этом такого же мнения многие начинающие программисты. Однако тут есть подводные камни, и очень весомые, используя подобный метод выборки в своих запросах, вы оставляете узкое место в производительности. Здесь же рекомендуется прибегнуть к дополнительному коду вместо использования ORDER BY RAND(), в качестве альтернативы, чтобы избавиться от слабого места в производительности, которое напомнит о себе при увеличении объема данных.

// какой код НЕ следует использовать:
$r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");

// правильнее будет использовать следующий код:
$r = mysql_query("SELECT count(*) FROM user");
$d = mysql_fetch_row($r);
$rand = mt_rand(0,$d[0] - 1);

$r = mysql_query("SELECT username FROM user LIMIT $rand, 1");

2.3 Используйте выборку конкретных полей, вместо SELECT *

Не ленитесь указывать конкретные нужные поля в запросе при выборке, вместо использования «*» — выборка всех полей, дело в том, что чем больше данных считывается из таблицы, тем медленнее становиться ваш запрос.

2.4 Разделяйте объемные запросы INSERT и DELETE

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

Пример:

while (1) {
    mysql_query("DELETE FROM logs WHERE log_date <= '2015-07-20' LIMIT 1000");
    if (mysql_affected_rows() == 0) {
        // записи удалены успешно
        break;
    }
    usleep(50000); // делаем небольшую паузу
}
Источник

v4l2loopback — Стримминг рабочего стола через софтовую web-камеру

Установка

Из репозитария

sudo apt install v4l2loopback-dkms v4l2loopback-utils

Из GIT

sudo apt install libelf-dev
git clone https://github.com/umlaeute/v4l2loopback.git
cd v4l2loopback/
make
make install
depmod -a

Запуск модуля ядра

sudo modprobe v4l2loopback video_nr=15 card_label="Fake webcam by v4l2loopback"

Проверка запуска устройства

v4l2-ctl --list-devices

Установка настроек

v4l2loopback-ctl set-fps 15 /dev/video15

Проверка стримминга с FFMpeg

ffmpeg -probesize 333M -video_size 1920x1080 -r 15 -threads 9\
 -f x11grab -i :0.0  -vf "scale=1280:720" -vcodec rawvideo -pix_fmt yuv420p\
 -f v4l2 /dev/video15

Выгрузка модуля

rmmod v4l2loopback