@hesy

Как эффективно хранить canvas попискельно в БД с последующим отображением?

Стек: PHP (Laravel), JS, MySQL, Nginx, Websockets.

giphy.gif?cid=790b76115c04ec043355dadc40b70cee4ff942c07c48d2c0&rid=giphy.gif&ct=g

Есть canvas размером 2000х1000 (~2 млн. пикселей суммарно).
На этом полотне в реалтайме рисуют N подключенных клиентов по веб-сокетам.
Данные о пикселях лежат в MySQL.
Таблица
Schema::create('pixels', function (Blueprint $table) {
    $table->id();
    $table->unsignedBigInteger('user_id')->nullable();
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    $table->smallInteger('x');
    $table->smallInteger('y');
    $table->string('color', 18);
    $table->timestamps();
    $table->index('id');
});

При каждом клике на стороне клиента, информация записывается в таблицу и разносится по подключенным клиентам.

Вопрос: как при открытии сайта, быстрее и эффективнее всего отобразить полотно со всеми пикселями из БД?

Как сейчас реализовано

Загрузка по чанкам: при открытии сайта делает N ajax запросов к апи, который возвращает чанк с пикселями. Использовал вариант с offset-limit, с where id > N and where id < N, эффект одинаковый.
Еще проблема в том, что запросы занимают большое кол-во времени, при этом блокируя другие открытые вкладки которые ждут пока завершатся предыдущие запросы с других вкладок (nginx php-fpm, очевидно завершать запросы, чтобы принять следующие плохая идея, т. к. нагрузка на сервер в эти моменты и так растет, а ожидаемое кол-во одновременно подключенных клиентов десятки тысяч (*в моих влажных мечтах)).


Вообще, идея с подобным подходом мне вообще не нравится, и я понимаю что это неправильно.
Может у вас есть идеи как подобное можно реализовать? И годится ли MySQL для таких целей?
  • Вопрос задан
  • 411 просмотров
Пригласить эксперта
Ответы на вопрос 2
FanatPHP
@FanatPHP
Чебуратор тега РНР
Ну как минимум за $table->string('color', 18); надо руки сразу отрывать
То есть на координатах наэкономили, 4 байта в сумме, а потом хоба - в 4 раза больше на несчастный цвет. при том что цветов там явно не больше десятка. И это не говоря уже про нормализацию.

По уму надо придумать простой бинарный протокол, который получает поток байт выравненными кусками
2 байта х
2 байта у
1 байт цвет
4 байта юзер (и нечего жадничать, никаких BigInteger. половины населения земного шара вполне хватит)
то есть всего 9 байт.
а не под сотню, как сейчас - ещё и завернутое в скобочки/кавычечки джейсона

Если подумать, то можно юзера сразу не передавать. Всех юзеров никто смотреть не будет, а при наведении можно и отдельный запрос послать. Тогда можно и BigInteger оставить.
Получится всего 5 байт на пиксель, то есть 10 метров на всю карту. Дофига, но подъемно.

По пагинации это дурь какая-то. Зачем "офсет-лимиты" если уже есть четкая разбивка.
Кто мешает запрашивать тупо построчно? Скажем, по 100 строк картинки? 10 запросов по мегабайту.
id в этой таблице по сути вообще не нужно, только если лара без него не сможет. Но по уму первичный ключ - это ху.

Из БД получать 2 лимона строк конечно тоже не сахар
Но можно наверное увеличить строки, хотя бы виртуально.
Вью или процедура, которая комбинирует скажем сто строк в одну

Насчет других хранилищ я не уверен. Там же наверняка нужна будет выборка обновлений, по таймстампу.
Но в целом с Редисом поэкспериментировать можно.
Ответ написан
mayton2019
@mayton2019
Bigdata Engineer
Для оптимизации загрузки landing page, можно хранить копии этой картинки в уменьшенном виде. 2000х1000 => 1000x500 => 500x250 e.t.c. Так игроделы делают для быстрого рендеринга текстур. Mip-уровни кажется называется.
Обновлять эти mip уровни можно не спеша. Через минутку.

База mysql вам вобщем-то не нужна. Картинку можно в реальном времени править atomic операциями и хранить в raw формате. Не знаю делают ли такое на PHP. Возможно вам нужен рукастый програмист на C++ или еще на каком-то языке чтобы подружить PHP с С++ и сделать сервис для этой картинки.
Ответ написан
Комментировать
Ваш ответ на вопрос

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

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