Задать вопрос
Tkreks
@Tkreks
Системный инженер

Большое кол-во записей в секунду в MySQL. Как читать последний порядковый номер столбца и добавлять к нему +1 при записи?

Здравствуйте, не знаю как правильно сформулировать заголовок запроса. Постараюсь отразить в деталях.
Есть некая абстрактная БД на mysql, в которых я по сути заполняю лишь одну колонку - result.
Схема таблицы
66b6149a94c0a648318794.png

Где id = Auto Increment , остальные колонки заполняются инсертами.
Есть некоторое устройство (на самом деле таких устройств может быть десятки), которое может генерировать данные с периодичностью 0.001сек. Периодичность меняется, но я закладываю максимальную скорость генерации.
C устройства передаются данные, которые заполняются в колонки (sessid и result)
Само устройство пишет не напрямую в базу, а передаёт данные в обработчик, на котором я реализовал следующую логику -
->Поступают данные с устройства ->

->выполняется проверка наличия sessid в базе
SELECT
	table.serialnumb
FROM
	table
WHERE
	sessid = $sess.id$
ORDER BY
	base.table DESC
LIMIT 1

Таким образом я получаю наибольшее значение serialnumb для данного sessid->

->если не найдено выполняется -
INSERT INTO `base`.`table`(`sessid`, `serialnumb`, `result`, `numnum`) VALUES ('$sess.id', 0, '0', $numnum)
и отправляется заново на проверку наличия sessid в базе->

->Если найдено - выполняю проверку уникальность result в данном sessid (не должно быть двух одинаковых result для одного sessid). Повторные значения result отбрасываю. ->

->Уникальное значение - беру serialnumb +1 и делаю инсерт
INSERT INTO `base`.`table`(`sessid`, `serialnumb`, `result`, `numnum`) VALUES ('$sess.id$', $sess.newlastnum$, '$rand$', $numnum$)

Собственно, я продебажил на разном железе, но в рамках ВМ (на вм стоит mysql и на отдельной вм стоит обработчик на node) у меня получилось так
Intel Platinum 8380 + ddr4
Самый толстый запрос усреднённо 0.04 сек занимает (select с 100 тыс.строк данных)
Ryzen 7 7800x3D + ddr5
тот же запрос 0.035 сек
Тестовый стенд intel xeon gold 6130
тот же запрос усредненно 0.1 сек

Проблема в том, что serialnumb должен быть уникальный в рамках sessid. Чего не получается добиться при большой интенсивности запросов.
Проблемка
66b62718633f9254116306.png

Причина-следственная связь ясна, из за большого наплыва информации select отрабатывает раньше у части запросов, в связи с чем отдается устаревший serialnumb, в связи с чем один serialnumb прописывается для нескольких запросов.
В итоговом решение - железо будет использовать энергоэффективное, соответственно вариант с железом отметаем.
Можно оптимизировать запросы + оптимизировать саму БД (т.к. она она стоковая без тюнинга mariadb 10.5) - но не думаю что приведет к исключению проблемы.
Как решение, вижу такой способ. Добавление всех записей во временную таблицу, Где будет 3 колонки (id (auto incremet), sessid, result) и далее уже перенос в актуальную таблицу. Но это может дать лаг при анализе данных на frontend, чего хотелось бы избежать. По этой же причине не хотелось бы делать задержку ожидания на обработчике, (тогда там memory может вспухнуть если будет гигантская очередь).
Подскажите, какие способы оптимизации или решения моего вопроса воспользовались бы Вы? Может я где-то нарушил логическую цепочку ?
  • Вопрос задан
  • 108 просмотров
Подписаться 1 Сложный Комментировать
Решения вопроса 2
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Какой-то бред.
1. Составной ключ по автоинкрементному полю и обычному смысла не имеет, поскольку автоинкрементное поле id всегда уникально, то добавление к нем sessid ничего, кроме торможения, не добавит.
2. Делать какую-то отдельную проверку на уникальность result внутри sessid смысла не имеет. Достаточно сделать уникальный составной ключ (sessid, result) и использовать INSERT IGNORE.
3. Отдельное поле serialnumb абсолютно не нужно. Выборку всегда можно отсортировать через ORDER BY id и пронумеровать через оконные функции.
Ответ написан
mayton2019
@mayton2019
Bigdata Engineer
Обычно при стриминге информации разделяют 2 процесса. Первое - это регистрация
событий с устройств.
Это обычно запись в Message Hub, Kafka, RabbitMQ, Apache Pulsar e.t.c.
прочие брокеры или системы которые работают с очередью. Тут - главная задача просто успеть
регистрировать событие. С этим системы очередей справляются отлично.

При регистрации событий никакого синхронизма между устройствами нет и в принципе
не может быть. По законам физики и по часовому времени и по прочим сложным процессам
которые идут в мире. Мы не можем вообще гарантировать точного совпадения часов на всех
устройствах поэтому фактор времени мы пропустим. К свойствам дублирования и грязи в атрибутах
на этом уровне можно относиться очень толерантно и не требовать никакой сложной логики фильтрации.
Если допустим 99.9% событий зашло ровными - оно и ладненько. Дальше почистим.

Дальше идет - процессинг. Вот здесь уже может вступать в игру MySQL сервер с его логикой
транзакций. Счетчиков. Уникальностей. Сурогатных или натуральных ключей. Партишенинга
и прочего. Здесь у вас уже может возникнуть лимит на скорость генерации (inserts) но он будет
мягкий потому что очередь уже смягчает удар.

Если у тебя приложение - тайм-критичное и клиент хочет видеть КАЖДОЕ событие как счетчик
в браузере то здесь тебе скорее всего надо отказаться от MySQL и попробовать что-то другое.
Что - я не знаю. Надо исследовать глубже use-case. Обычно разработка таких систем - это
компромисс между одним и другим. Идеала тут не будет.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

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