G>3) Если же запросов много и мы упираемся в базу, то делаем кэширование в приложении. Сохраняем результаты запроса в кэше по ключу.
Речь идет об app server'е или о клиенте? Что выступает в роли ключа?
G>К чему стоит стремиться если вы хотите делать нагруженное приложение: G>1) 90% запросов на чтение должно отдаваться из кэша клиента (с помощью 304) G>2) Из оставшихся 90% должно отдаваться из кэша сервера (статика, inmemory-кэши итд)
Речь о 10%, что не кэшом клиента обслуживаются?
G>3) Оставшийся 1% запросов, реально улетевших в базу должен быть покрыт индексами
10% или 1%?
Т.е. как я понял 90% обслуживаются кэшем клиента, оставшиеся 10% бьются на кэш сервера(9%) и базу (1%).
Так?
Здравствуйте, rosencrantz, Вы писали:
R>Я бы хотел этот вариант рассмотреть. Стали бы вы делать update SchoolStats set studentCount = studentCount + 1 where schoolId = 123 в коде аппликейшна или через триггер?
Я бы сделал через триггер, но вот где он есть кроме Oralc'а и MsSql?
R>Меня интересует конкретно как так сделать, чтобы эти счётчики в какой-то момент не разъехались с данными из-за того, что в каком-то месте забыли этот +1 дёрнуть.
Для этого +1 надо дергать ровно 1 месте, желательно в триггере, чтобы сама бд все это делала.
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>3) Если же запросов много и мы упираемся в базу, то делаем кэширование в приложении. Сохраняем результаты запроса в кэше по ключу.
S>Речь идет об app server'е или о клиенте?
Сервер.
S>Что выступает в роли ключа?
"Тип запроса-ключ". А если у вас REST, то прям сегмент URL.
G>>К чему стоит стремиться если вы хотите делать нагруженное приложение: G>>1) 90% запросов на чтение должно отдаваться из кэша клиента (с помощью 304) G>>2) Из оставшихся 90% должно отдаваться из кэша сервера (статика, inmemory-кэши итд)
S>Речь о 10%, что не кэшом клиента обслуживаются?
да, такое часто бывает для часто меняющихся данных.
У меня было такое в дашборде коллцентра. Запросы приходят примерно раз в минуту и обновляются с такой же периодичностью. Поэтому почти все запросы долетали до сервера. Но данные в течении интервала обновления считывались один раз и отдавались всем клиентам из кэша сервера.
G>>3) Оставшийся 1% запросов, реально улетевших в базу должен быть покрыт индексами S>10% или 1%?
сли из 100% отнять 90%, то останется 10%. Если из оставшихся 10% отнять 90%, то останется 1% от первоначального количества.
S>Т.е. как я понял 90% обслуживаются кэшем клиента, оставшиеся 10% бьются на кэш сервера(9%) и базу (1%). S>Так?
Да
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, rosencrantz, Вы писали: R>>Имеете ли вы в виду, что select count(*) from Students по таблице 100млн строчек 100 раз в секунду — это рабочий вариант? S>Нет конечно — надо считать по индексу.
На тестовой базе оба варианта отрабатывают за одинаковое время — 7 минут:
select count(*) from Students;
select count(id) from Students;
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, rosencrantz, Вы писали: R>>На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек, а не 100 (это AWS RDS — t3.micro). S>А индекс у вас там есть?
Есть первичный ключ и его индекс.
S>З.Ы. Mysql — недоСУБД. Пока ещё не всё прополимерено, возьмите хотя бы postgres.
Я для общего развития спрашиваю. Нет у меня требований показывать эти счётчики
Здравствуйте, DenisCh, Вы писали:
DC>Здравствуйте, rosencrantz, Вы писали:
R>>На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек
DC>А если select count(pk_field) from Users, где pk_field — индексированное поле первичного ключа таблицы?
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, rosencrantz, Вы писали:
R>>Здравствуйте, scf, Вы писали:
scf>>>Соглашусь с предыдущим оратором, обычная реляционная СУБД потянет такую нагрузку в реалтайм. Если вдруг случится невероятное и не потянет, то рядом с реляционной базой с мастер-данными нужно развернуть аналитическую базу и пополнять её асинхронно. Причем лучше не связываться с триггерами и закодить на уровне приложения.
R>>На моей тестовой базе запрос select count(*) from Users отрабатывает за 7 минут при том, что там 50млн строчек, а не 100 (это AWS RDS — t3.micro). Или вы имеете в виду, что на сервере за 100 баксов в день это будет работать на много порядков быстрее? Каким образом вы принимаете решение о необходимой производительности сервера?
G>Для InnoDB движка надо count(PK) писать.
InnoDB processes SELECT COUNT(*) statements by traversing the smallest available secondary index unless an index or optimizer hint directs the optimizer to use a different index. If a secondary index is not present, InnoDB processes SELECT COUNT(*) statements by scanning the clustered index.
Processing SELECT COUNT(*) statements takes some time if index records are not entirely in the buffer pool. For a faster count, create a counter table and let your application update it according to the inserts and deletes it does.
G>Соглашусь с коллегами, что лучше взять postgres. У MySQL слишком много особенностей, помогающих писать тормозные запросы.
R>InnoDB processes SELECT COUNT(*) statements by traversing the smallest available secondary index unless an index or optimizer hint directs the optimizer to use a different index. If a secondary index is not present, InnoDB processes SELECT COUNT(*) statements by scanning the clustered index.
Судя по этому сообщению надо сделать некластерный индекс по PK, иначе запрос считывает всю таблицу.
R>
R>Processing SELECT COUNT(*) statements takes some time if index records are not entirely in the buffer pool. For a faster count, create a counter table and let your application update it according to the inserts and deletes it does.
R>>InnoDB processes SELECT COUNT(*) statements by traversing the smallest available secondary index unless an index or optimizer hint directs the optimizer to use a different index. If a secondary index is not present, InnoDB processes SELECT COUNT(*) statements by scanning the clustered index.
G>Судя по этому сообщению надо сделать некластерный индекс по PK, иначе запрос считывает всю таблицу.
Да там на самом деле были индексы по email, по username — я думаю кого-то из них он и использовал. Ради эксперимента сейчас добавил явный индекс по PK, explain select count(*) сказал, что будет использовать именно этот новый индекс. Запустил. Те же 7 минут.
Здравствуйте, rosencrantz, Вы писали:
R>Здравствуйте, gandjustas, Вы писали:
R>>>
R>>>InnoDB processes SELECT COUNT(*) statements by traversing the smallest available secondary index unless an index or optimizer hint directs the optimizer to use a different index. If a secondary index is not present, InnoDB processes SELECT COUNT(*) statements by scanning the clustered index.
G>>Судя по этому сообщению надо сделать некластерный индекс по PK, иначе запрос считывает всю таблицу.
R>Да там на самом деле были индексы по email, по username — я думаю кого-то из них он и использовал. Ради эксперимента сейчас добавил явный индекс по PK, explain select count(*) сказал, что будет использовать именно этот новый индекс. Запустил. Те же 7 минут.
Переходи на нормальную СУБД.
Здравствуйте, Sharov, Вы писали: S>Для этого +1 надо дергать ровно 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 час — как это повлияло бы на решение?
Добавил бы столбцы SchoolCount, ClassCount, StudentCount в соответствующие таблицы. Обновлял бы их при изменении данных. И всё.
Здравствуйте, Vladek, Вы писали:
V>Добавил бы столбцы SchoolCount, ClassCount, StudentCount в соответствующие таблицы. Обновлял бы их при изменении данных. И всё.
Здравствуйте, rosencrantz, Вы писали:
R>Здравствуйте, Vladek, Вы писали:
V>>Добавил бы столбцы SchoolCount, ClassCount, StudentCount в соответствующие таблицы. Обновлял бы их при изменении данных. И всё.
R>Триггером или кодом приложения?
В коде. Чтобы потом иметь возможность обновить эти данные в любой удобный момент.
Здравствуйте, Vladek, Вы писали:
R>>Триггером или кодом приложения? V>В коде. Чтобы потом иметь возможность обновить эти данные в любой удобный момент.
А почему не триггером и потом просто спросить цифирь у базы? А так получается, что выполняем работу за базу.
Здравствуйте, rosencrantz, Вы писали:
R>Здравствуйте, Vladek, Вы писали:
V>>Добавил бы столбцы SchoolCount, ClassCount, StudentCount в соответствующие таблицы. Обновлял бы их при изменении данных. И всё.
R>Триггером или кодом приложения?
Лучше триггером. Это не часть бизес-логики, это исправление косяков БД, которая не умеет в материализованные представления и нормальные индексы.
Если поменять базу на другую, то триггеры можно будет выкинуть.
Поэтому не стоит тащить такую логику в программу.
Здравствуйте, Sinclair, Вы писали:
S>Прекрасно изложено. Я бы ещё попробовал сделать materialized view между 2 и 3. Особенно если частота чтений << частоты записей.
Тут правильное отношение ? Чтений намного МЕНЬШЕ чем записей ?
Здравствуйте, IID, Вы писали:
S>>Прекрасно изложено. Я бы ещё попробовал сделать materialized view между 2 и 3. Особенно если частота чтений << частоты записей. IID>Тут правильное отношение ? Чтений намного МЕНЬШЕ чем записей ?
Да, ибо речь про бд. Что-то можно закэшировать и не лезть лишний раз в бд.