Подсчёт количества привязанных сущностей
От: rosencrantz США  
Дата: 15.07.21 19:07
Оценка:
Есть например аппликейшн для управления данными об учениках в школах. Например Mysql база, в которой хранится иерархия: Schools, Classes, Students. Ожидаемые объёмы данных скажем 1000 школ x 1000 классов на школу x 100 студентов на класс. Т.е. всего 1000 школ, 1млн классов, 100млн студентов. Редактировать-добавлять-удалять можно как отдельные записи через CRUD UI, так и балком — целый класс, целую школу, прям сразу вообще всё — это например через аплоад CSV файлов. Когда удаляешь школу, удаляются все её классы и все ученики из этих классов. Когда удаляешь класс, удаляются все ученики.

В аппликейшне при этом есть несколько страничек, на которых нужно показывать актуальную статистику:

1. Сколько вообще всего школ, классов и учеников
2. Глядя на школу — сколько в ней классов и учеников
3. Глядя на класс — сколько в нём учеников

Данные нужно показывать моментально. К этим страничкам обращаются параллельно по 100 запросов в секунду.

Как бы вы стали реализовывать функциональность подсчёта? Стали бы использовать триггеры в БД или стали бы делать изменение счётчиков в коде? Если статистике разрешается отставать от данных скажем на 1 час — как это повлияло бы на решение?
Отредактировано 15.07.2021 19:09 rosencrantz . Предыдущая версия .
Re: Подсчёт количества привязанных сущностей
От: Stanislav V. Zudin Россия  
Дата: 15.07.21 19:13
Оценка: +3
Здравствуйте, rosencrantz, Вы писали:

R>Есть например аппликейшн для управления данными об учениках в школах. Например Mysql база, в которой хранится иерархия: Schools, Classes, Students. Ожидаемые объёмы данных скажем 1000 школ x 1000 классов на школу x 100 студентов на класс. Т.е. всего 1000 школ, 1млн классов, 100млн студентов.


R>Данные нужно показывать моментально. К этим страничкам обращаются параллельно по 100 запросов в секунду.


R>Как бы вы стали реализовывать функциональность подсчёта? Стали бы использовать триггеры в БД или стали бы делать изменение счётчиков в коде? Если статистике разрешается отставать от данных скажем на 1 час — как это повлияло бы на решение?


При заявленных цифрах можно использовать каноничные примеры из учебника — пиписочные масштабы.
Можно даже в памяти хранить, вообще без СУБД.
_____________________
С уважением,
Stanislav V. Zudin
Re: Подсчёт количества привязанных сущностей
От: scf  
Дата: 15.07.21 19:20
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>Как бы вы стали реализовывать функциональность подсчёта? Стали бы использовать триггеры в БД или стали бы делать изменение счётчиков в коде? Если статистике разрешается отставать от данных скажем на 1 час — как это повлияло бы на решение?


Соглашусь с предыдущим оратором, обычная реляционная СУБД потянет такую нагрузку в реалтайм. Если вдруг случится невероятное и не потянет, то рядом с реляционной базой с мастер-данными нужно развернуть аналитическую базу и пополнять её асинхронно. Причем лучше не связываться с триггерами и закодить на уровне приложения.
Re[2]: Подсчёт количества привязанных сущностей
От: rosencrantz США  
Дата: 15.07.21 19:58
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>При заявленных цифрах можно использовать каноничные примеры из учебника — пиписочные масштабы.


Имеете ли вы в виду, что select count(*) from Students по таблице 100млн строчек 100 раз в секунду — это рабочий вариант?
Re: Подсчёт количества привязанных сущностей
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 15.07.21 20:06
Оценка: 94 (5) +1
Здравствуйте, rosencrantz, Вы писали:

R>Есть например аппликейшн для управления данными об учениках в школах. Например Mysql база, в которой хранится иерархия: Schools, Classes, Students. Ожидаемые объёмы данных скажем 1000 школ x 1000 классов на школу x 100 студентов на класс. Т.е. всего 1000 школ, 1млн классов, 100млн студентов. Редактировать-добавлять-удалять можно как отдельные записи через CRUD UI, так и балком — целый класс, целую школу, прям сразу вообще всё — это например через аплоад CSV файлов. Когда удаляешь школу, удаляются все её классы и все ученики из этих классов. Когда удаляешь класс, удаляются все ученики.


R>В аппликейшне при этом есть несколько страничек, на которых нужно показывать актуальную статистику:


R>1. Сколько вообще всего школ, классов и учеников

R>2. Глядя на школу — сколько в ней классов и учеников
R>3. Глядя на класс — сколько в нём учеников

R>Данные нужно показывать моментально. К этим страничкам обращаются параллельно по 100 запросов в секунду.


R>Как бы вы стали реализовывать функциональность подсчёта? Стали бы использовать триггеры в БД или стали бы делать изменение счётчиков в коде? Если статистике разрешается отставать от данных скажем на 1 час — как это повлияло бы на решение?


1) Начинаем с простого: делаем БД в 3НФ и пишем нормальные запросы — с нужными проекциями и корректными предикатами.
2) Далее делаем в базе индексы, оптимизирующие эти запросы. Я думаю вам уже на этом этапе хватит производительности базы.
3) Если же запросов много и мы упираемся в базу, то делаем кэширование в приложении. Сохраняем результаты запроса в кэше по ключу. При записи сбрасываем элемент по этому ключу. Учитывая простую структуру базы управление кэшом не станет проблемой. Для начала достаточно кэша в памяти приложения.
4) Если после этого быстродействие начинает упираться в приложение — растет очередь запросов и падает среднее время ответа, то используем кэширование на клиенте. Для этого вместе со значением по ключу вы сохраняете дату последнего обновления. Отдаете страницу с Last-Modified заголовком, равным дате последнего обновления. При получении запроса с if-modified-since сравниваете дату со значением на сервере и отдаете 304 если совпадают.


К чему стоит стремиться если вы хотите делать нагруженное приложение:
1) 90% запросов на чтение должно отдаваться из кэша клиента (с помощью 304)
2) Из оставшихся 90% должно отдаваться из кэша сервера (статика, inmemory-кэши итд)
3) Оставшийся 1% запросов, реально улетевших в базу должен быть покрыт индексами

При правильном проектировании это все можно сделать без показа устаревших данных.
Re[2]: Подсчёт количества привязанных сущностей
От: rosencrantz США  
Дата: 15.07.21 20:06
Оценка:
Здравствуйте, scf, Вы писали:

scf>Соглашусь с предыдущим оратором, обычная реляционная СУБД потянет такую нагрузку в реалтайм. Если вдруг случится невероятное и не потянет, то рядом с реляционной базой с мастер-данными нужно развернуть аналитическую базу и пополнять её асинхронно. Причем лучше не связываться с триггерами и закодить на уровне приложения.


На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек, а не 100 (это AWS RDS — t3.micro). Или вы имеете в виду, что на сервере за 100 баксов в день это будет работать на много порядков быстрее? Каким образом вы принимаете решение о необходимой производительности сервера?
Re[3]: Подсчёт количества привязанных сущностей
От: scf  
Дата: 15.07.21 20:08
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>Имеете ли вы в виду, что select count(*) from Students по таблице 100млн строчек 100 раз в секунду — это рабочий вариант?


Нет, это fullscan, но это же единственный кейс, когда select count будет медленным
Такие вещи а) легко кешировать б) точное значение никому не интересно — какая разница, сколько на самом деле студентов в системе 1234567 или 1234568?
Re[3]: Подсчёт количества привязанных сущностей
От: scf  
Дата: 15.07.21 20:13
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек, а не 100 (это AWS RDS — t3.micro). Или вы имеете в виду, что на сервере за 100 баксов в день это будет работать на много порядков быстрее? Каким образом вы принимаете решение о необходимой производительности сервера?


Ну не 100 баксов в день, но быстро и бесплатно — так не бывает. На хосте с постгресом в идеале должно быть достаточно памяти, чтобы дисковое хранилище БД полностью поместилось в кеше операционной системы.
SSD тоже не повредит, если нужна вменяемая скорость вставки.

Например, разверните ту же базу с теми же данными на своей машине и сравните время запросов на прогретой базе.
Re[4]: Подсчёт количества привязанных сущностей
От: rosencrantz США  
Дата: 15.07.21 20:13
Оценка:
Здравствуйте, scf, Вы писали:

scf>Здравствуйте, rosencrantz, Вы писали:


R>>Имеете ли вы в виду, что select count(*) from Students по таблице 100млн строчек 100 раз в секунду — это рабочий вариант?


scf>Нет, это fullscan, но это же единственный кейс, когда select count будет медленным

scf>Такие вещи а) легко кешировать б) точное значение никому не интересно — какая разница, сколько на самом деле студентов в системе 1234567 или 1234568?

Ну в исходном сообщении: "нужно показывать актуальную статистику".

Вы говорите о кешировании — мне интересно откуда всё-таки вы предлагаете результат для кеширования брать? Типа удалили 1 юзера, сделали этот зверский select count, запомнили. Второго удалили — то же самое? Или раз в час select count делать (и тогда статистика будет отставать на 1 час конечно).
Re[4]: Подсчёт количества привязанных сущностей
От: rosencrantz США  
Дата: 15.07.21 20:20
Оценка:
Здравствуйте, scf, Вы писали:

scf>Здравствуйте, rosencrantz, Вы писали:


R>>На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек, а не 100 (это AWS RDS — t3.micro). Или вы имеете в виду, что на сервере за 100 баксов в день это будет работать на много порядков быстрее? Каким образом вы принимаете решение о необходимой производительности сервера?


scf>Ну не 100 баксов в день, но быстро и бесплатно — так не бывает. На хосте с постгресом в идеале должно быть достаточно памяти, чтобы дисковое хранилище БД полностью поместилось в кеше операционной системы.

scf>SSD тоже не повредит, если нужна вменяемая скорость вставки.

scf>Например, разверните ту же базу с теми же данными на своей машине и сравните время запросов на прогретой базе.


База 150 Гб весит, на моём девелоперском компе столько нет
Утверждаете ли вы, что мне нужен сервер со 150 Гб памяти? У AWS RDS m5.12xlarge — 192 Гб за $4 в час, что как раз даёт $100 в день.
Re[5]: Подсчёт количества привязанных сущностей
От: scf  
Дата: 15.07.21 20:29
Оценка: +1
Здравствуйте, rosencrantz, Вы писали:

R>Вы говорите о кешировании — мне интересно откуда всё-таки вы предлагаете результат для кеширования брать? Типа удалили 1 юзера, сделали этот зверский select count, запомнили. Второго удалили — то же самое? Или раз в час select count делать (и тогда статистика будет отставать на 1 час конечно).


Вообще-то это тяжелый аналитический запрос, который собрались гонять на OLTP базе. Примитивное кеширование здесь плохая идея, т.к. даже "раз в час" такой селект будет просаживать персентили остальных запросов.
Что сделал бы я:
— либо убедил, что это полностью бессмысленная метрика, либо что нужна полноценная аналитика (а-ля отчеты по среднему кол-ву школьников в школе)
— если это природное любопытство руководства, сделал бы им отдельную кнопку "посчитать кол-во школьников всего"
— если им нужен крутящийся счетик на главной, хранил бы общий count в базе и обновлял его при модификации базы. Возможно, через очередь, чтобы разгрузить базу при массовой вставке.
— если все предыдущие варианты не подходят (включая отдельную аналитическую базу, где это быстрый запрос), то завел бы таблицу "кол-во школьников в каждой школе" и обновлял её после каждой вставки, по каждой измененной школе.
Re[5]: Подсчёт количества привязанных сущностей
От: scf  
Дата: 15.07.21 20:31
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>Утверждаете ли вы, что мне нужен сервер со 150 Гб памяти? У AWS RDS m5.12xlarge — 192 Гб за $4 в час, что как раз даёт $100 в день.


Нет, я утверждаю, что 150 Гб памяти — это оптимальный по быстродействию вариант, если нет "холодных" данных. Реальные требования к железу будут видны после нагрузочного тестирования.
Re[6]: Подсчёт количества привязанных сущностей
От: rosencrantz США  
Дата: 15.07.21 20:48
Оценка:
Здравствуйте, scf, Вы писали:

scf>Здравствуйте, rosencrantz, Вы писали:


R>>Утверждаете ли вы, что мне нужен сервер со 150 Гб памяти? У AWS RDS m5.12xlarge — 192 Гб за $4 в час, что как раз даёт $100 в день.


scf>Нет, я утверждаю, что 150 Гб памяти — это оптимальный по быстродействию вариант, если нет "холодных" данных. Реальные требования к железу будут видны после нагрузочного тестирования.


Вот сделали select count по таблице Users. 7 минут он занимает. Это проблема дизайна (сама идея использования такого запроса) или это проблема сервера (тормозит)?
Re[6]: Подсчёт количества привязанных сущностей
От: rosencrantz США  
Дата: 15.07.21 20:56
Оценка:
Здравствуйте, scf, Вы писали:

scf>Здравствуйте, rosencrantz, Вы писали:


R>>Вы говорите о кешировании — мне интересно откуда всё-таки вы предлагаете результат для кеширования брать? Типа удалили 1 юзера, сделали этот зверский select count, запомнили. Второго удалили — то же самое? Или раз в час select count делать (и тогда статистика будет отставать на 1 час конечно).


scf>Вообще-то это тяжелый аналитический запрос, который собрались гонять на OLTP базе.


Согласен. Я поэтому и удивился, когда мне указали на "каноничные примеры из учебника" и "пиписочные масштабы"

scf>- если все предыдущие варианты не подходят (включая отдельную аналитическую базу, где это быстрый запрос), то завел бы таблицу "кол-во школьников в каждой школе" и обновлял её после каждой вставки, по каждой измененной школе.


Я бы хотел этот вариант рассмотреть. Стали бы вы делать update SchoolStats set studentCount = studentCount + 1 where schoolId = 123 в коде аппликейшна или через триггер? Меня интересует конкретно как так сделать, чтобы эти счётчики в какой-то момент не разъехались с данными из-за того, что в каком-то месте забыли этот +1 дёрнуть.
Re: Подсчёт количества привязанных сущностей
От: swame  
Дата: 15.07.21 20:57
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>Есть например аппликейшн для управления данными об учениках в школах. Например Mysql база, в которой хранится иерархия: Schools, Classes, Students. Ожидаемые объёмы данных скажем 1000 школ x 1000 классов на школу x 100 студентов на класс. Т.е. всего 1000 школ, 1млн классов, 100млн студентов. Редактировать-добавлять-удалять можно как отдельные записи через CRUD UI, так и балком — целый класс, целую школу, прям сразу вообще всё — это например через аплоад CSV файлов. Когда удаляешь школу, удаляются все её классы и все ученики из этих классов. Когда удаляешь класс, удаляются все ученики.


R>Данные нужно показывать моментально. К этим страничкам обращаются параллельно по 100 запросов в секунду.


R>Как бы вы стали реализовывать функциональность подсчёта? Стали бы использовать триггеры в БД или стали бы делать изменение счётчиков в коде? Если статистике разрешается отставать от данных скажем на 1 час — как это повлияло бы на решение?


При таком объеме данных можно даже пофамильные списки учеников полностью уместить в 2-3 Гб оперативной памяти 32 битного процесса.
Но для задачи просмотра в WEБ приложение даже этого не надо, если циклически обновлять статические странички по школам и классам.
Отредактировано 15.07.2021 21:01 swame . Предыдущая версия .
Re[3]: Подсчёт количества привязанных сущностей
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.07.21 02:22
Оценка:
Здравствуйте, rosencrantz, Вы писали:
R>Имеете ли вы в виду, что select count(*) from Students по таблице 100млн строчек 100 раз в секунду — это рабочий вариант?
Нет конечно — надо считать по индексу.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Подсчёт количества привязанных сущностей
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.07.21 02:23
Оценка:
Здравствуйте, rosencrantz, Вы писали:
R>На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек, а не 100 (это AWS RDS — t3.micro).
А индекс у вас там есть?
З.Ы. Mysql — недоСУБД. Пока ещё не всё прополимерено, возьмите хотя бы postgres.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Подсчёт количества привязанных сущностей
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.07.21 02:26
Оценка: 4 (1) +1
Здравствуйте, gandjustas, Вы писали:


G>1) Начинаем с простого: делаем БД в 3НФ и пишем нормальные запросы — с нужными проекциями и корректными предикатами.

G>2) Далее делаем в базе индексы, оптимизирующие эти запросы. Я думаю вам уже на этом этапе хватит производительности базы.
G>3) Если же запросов много и мы упираемся в базу, то делаем кэширование в приложении. Сохраняем результаты запроса в кэше по ключу. При записи сбрасываем элемент по этому ключу. Учитывая простую структуру базы управление кэшом не станет проблемой. Для начала достаточно кэша в памяти приложения.
G>4) Если после этого быстродействие начинает упираться в приложение — растет очередь запросов и падает среднее время ответа, то используем кэширование на клиенте. Для этого вместе со значением по ключу вы сохраняете дату последнего обновления. Отдаете страницу с Last-Modified заголовком, равным дате последнего обновления. При получении запроса с if-modified-since сравниваете дату со значением на сервере и отдаете 304 если совпадают.
Прекрасно изложено. Я бы ещё попробовал сделать materialized view между 2 и 3. Особенно если частота чтений << частоты записей.
Просто иметь иерархию счётчиков типа count класса, count параллели, count школы, count района добавляет O(1) ко времени вставки/удаления, зато позволяет очень многие агрегатные запросы делать за O(1)

G>К чему стоит стремиться если вы хотите делать нагруженное приложение:

G>1) 90% запросов на чтение должно отдаваться из кэша клиента (с помощью 304)
G>2) Из оставшихся 90% должно отдаваться из кэша сервера (статика, inmemory-кэши итд)
G>3) Оставшийся 1% запросов, реально улетевших в базу должен быть покрыт индексами

G>При правильном проектировании это все можно сделать без показа устаревших данных.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Подсчёт количества привязанных сущностей
От: DenisCh Россия  
Дата: 16.07.21 03:48
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек


А если select count(pk_field) from Users, где pk_field — индексированное поле первичного ключа таблицы?
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Re[3]: Подсчёт количества привязанных сущностей
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.07.21 07:22
Оценка:
Здравствуйте, rosencrantz, Вы писали:

R>Здравствуйте, scf, Вы писали:


scf>>Соглашусь с предыдущим оратором, обычная реляционная СУБД потянет такую нагрузку в реалтайм. Если вдруг случится невероятное и не потянет, то рядом с реляционной базой с мастер-данными нужно развернуть аналитическую базу и пополнять её асинхронно. Причем лучше не связываться с триггерами и закодить на уровне приложения.


R>На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек, а не 100 (это AWS RDS — t3.micro). Или вы имеете в виду, что на сервере за 100 баксов в день это будет работать на много порядков быстрее? Каким образом вы принимаете решение о необходимой производительности сервера?


Для InnoDB движка надо count(PK) писать.
Соглашусь с коллегами, что лучше взять postgres. У MySQL слишком много особенностей, помогающих писать тормозные запросы.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.