Создание переменной в базе данных для хранения глобальной статистики

#sql #sql-server #sql-server-2005 #variables

#sql #sql-сервер #sql-server-2005 #переменные

Вопрос:

Давайте представим, что у меня есть социальная сеть. Я всегда показываю пользователю, сколько пользователей зарегистрировано и активировали свой профиль. Итак, каждый раз, когда один пользователь входит в систему, он переходит в DB и создает:

 select count(*) from users where status = 'activated'
  

итак, если 5.000 пользователей войдут в систему или просто обновят страницу, это сделает 5.000 запросов к SQL выше.
Мне было интересно, не лучше ли где-нибудь разместить переменную (которую я до сих пор понятия не имею, куда поместить), чтобы каждый раз, когда пользователь активирует свой профиль, добавлялся 1, а затем, когда я захочу показать, сколько пользователей зарегистрировано в этой социальной сети, я получу только значение этой переменной.

Как я могу это сделать? Действительно ли это лучшее решение по сравнению с тем, что у меня есть?

Ответ №1:

Вы могли бы использовать индексированное представление, которое SQL Server будет автоматически поддерживать:

 create table dbo.users (
    ID int not null,
    Activated bit not null
)
go
create view dbo.user_status_stats (Activated,user_count)
with schemabinding
as
    select Activated,COUNT_BIG(*) from dbo.users group by Activated
go
create unique clustered index IX_user_status_stats on dbo.user_status_stats (Activated)
go
  

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

 SELECT user_count from user_status_stats with (NOEXPAND) where Activated = 1
  

и ему не придется запрашивать базовую таблицу. Вам нужно использовать WITH (NOEXPAND) подсказку о редакциях ниже (Enterprise / Developer).


Хотя, как предложил @Jim, выполнение ПОДСЧЕТА (*) по индексу, когда столбцы индекса могут удовлетворять критериям запроса, используя сравнения на равенство, также должно быть довольно быстрым.

Комментарии:

1. Разве это не было бы то же самое? Мне все равно пришлось бы сделать 5000 выборок, чтобы просмотреть ее. Не вините меня, если я ошибаюсь, но для меня это выглядит так же: (

2. @pringlesinn — это самое простое, что можно сделать, если вы хотите сохранить эту информацию на SQL Server . Или, чтобы изменить ваш запрос, разве для хранения этого в «переменной где-нибудь» не потребовалось бы 5000 запросов к этой переменной? Если вы не хотите запускать какие-либо запросы к базе данных, сохраните самые последние данные в кэше приложения, но не ищите SQL для их поддержки.

3. Я хотел иметь это полное количество выборок бла-бла-бла раз в 30 секунд, обновляя значение в одном столбце, и тогда мне пришлось бы выполнять 5000 запросов, чтобы выбрать это значение, а не количество выборок (*) опять же, имеет ли это большое значение, как я думаю, или это в основном одно и то же?

4. @pringlesinn — представление, которое я показал в своем ответе, поддерживается SQL Server — оно обновляет COUNT(*) значения всякий раз, когда строки вставляются или удаляются из таблицы users, или изменяется их статус. Если вы запрашиваете представление (используя последний запрос, который я получил в своем ответе), он не выполняет дальнейших действий COUNT(*) — у него уже вычислено это значение; или, другими словами, то, что я показал выше, более эффективно, чем то, что вы предлагаете, и требует написания меньшего количества кода.

5. итак, вы хотите сказать, что представление хранит кэшированное значение? что-то вроде этого?

Ответ №2:

Как вы уже догадались — не лучшая идея вычислять это значение каждый раз, когда кто-то заходит на сайт.

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

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

В качестве альтернативы # 2, вы могли бы использовать что-то вроде MemCache для хранения вычисленного значения в течение определенного периода времени, а затем, когда срок действия кэша истечет, пересчитать его снова.

Комментарии:

1. Вот что мне интересно, как это сделать, хахахах, я бы создал таблицу для хранения только этого? И для обновления этого значения я бы запустил триггер? Я никогда раньше этого не делал.

2.Да, в зависимости от других значений, которые вы хотите предварительно вычислить, вы могли бы создать таблицу stats or globals с name и value столбцом, чтобы вы могли хранить name='USERS' value=XXX — или, если вы хотите сохранить только несколько значений, у вас могла бы быть таблица с одной строкой и полями для каждого из ваших значений.

Ответ №3:

Есть несколько вариантов, которые вы могли бы рассмотреть:

1) как вы сказали, поддерживайте глобальное количество при каждой активации профиля, чтобы каждый раз сохранять попадание в таблицу users. Вы могли бы просто сохранить это количество в таблице «Статистика», а затем запросить это значение оттуда.

2) не показывайте фактическое «текущее» количество, показывайте количество, которое «в значительной степени обновлено» — например, кэшируйте количество в вашем приложении и периодически обновляйте значение, чтобы вы реже запрашивали количество. Или, если вы сохраняете количество в таблице «Статистика», как указано выше, у вас может быть запланированное задание, которое обновляет количество каждый час, а не при каждой активации профиля.

Зависит от того, хотите ли вы отображать точную цифру в режиме реального времени или можете смириться с задержкой. Очевидно, что объемы данных тоже имеют значение — если у вас большая база данных, то наличие слегка устаревшего кэшированного значения может иметь смысл.

Ответ №4:

С чисто точки зрения SQL Server, нет, вы не найдете лучшего способа сделать это. Если, возможно, ваша социальная сеть не размером с Facebook. Денормализация вашего дизайна данных (например, ведение подсчета в отдельной таблице) приведет к тому, что возможные источники данных не будут синхронизированы. Это не обязательно должно выйти из синхронизации, если оно закодировано правильно, но это возможно…

Просто убедитесь, что у вас есть индекс состояния. В этот момент SQL не будет сканировать таблицу на предмет количества, но вместо этого он будет сканировать индекс. Индекс будет намного меньше (то есть на странице диска поместится больше данных). Если бы вы преобразовали свой статус в int, smallint или tinyint, вы получили бы еще больше индексных листов на странице диска и, следовательно, намного меньше операций ввода-вывода. Чтобы получить ваше описание («активировано» и т.д.), Используйте справочную таблицу. Справочная таблица была бы настолько маленькой, SQL просто сохранил бы все это в оперативной памяти после первого доступа.

Теперь, если вы все еще считаете, что это слишком большие накладные расходы (а так не должно быть), вы могли бы придумать гибридный метод. Вы могли бы сохранить свой счет в отдельной таблице (которую SQL сохранял бы в оперативной памяти, если это только одна запись) или предполагая, что ваш сайт находится в asp.net вы могли бы создать переменную приложения для отслеживания количества. Вы могли бы увеличить ее в Session_Start и уменьшить в Session_End. Но вам нужно будет придумать способ сделать поток инкремента и декремента безопасным, чтобы два сеанса не пытались обновить значение одновременно.

Комментарии:

1. Что, если я, например, буду производить подсчет (*) каждые 30 секунд и сохранять этот столбец count в таблице? У него никогда не возникнет проблемы с конфликтом с чем-либо еще. Это то, что они хотят, чтобы я сделал, на самом деле..

2. @pringlesinn: Да, это сработало бы. Это всегда будет просто оценка, но если это все, что вы ищете, это работает.

Ответ №5:

Вы также можете использовать глобальную временную таблицу. Вы всегда получите быстрый поиск. Даже если вы устанавливаете 30-секундный пинг. Пример триггера Link1, пример триггера Link2 будут поддерживать такие действия в этой таблице.

Комментарии:

1. Я забыл о ##временных таблицах. Интересное приложение. Вам просто нужно помнить, что она автоматически удаляется, когда закрывается последнее соединение, которое ее использовало.

2. Извините, не упомянул это явно. Создание глобальной временной таблицы будет выполнено вручную. Я думаю, теперь это имеет смысл.