Поиск последних данных в быстро растущей таблице базы данных SQL Server - PullRequest
0 голосов
/ 04 февраля 2019

Я создаю приложение IOT, в котором данные датчика регистрируются в базе данных SQL Server в таблицу с именем SENSOR_DATA.

Столбцы и тип данных таблицы SENSOR_DATA указаны ниже

ID             BIGINT
SENSOR_ID      BIGINT
READINGS_DATE  DATETIME
READING        DOUBLE

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

Всего должно быть установлено около 1000 датчиков.

Это означает, что каждыйдень будет около 1000 x 600 = "600 000" INSERTS.

Наиболее часто используемым запросом будет получение последних показаний (на основе столбца DATETIME) всех датчиков.

В настоящее время я реализовал это с помощью коррелированного запроса.То, как составлен запрос, у меня есть сильное ощущение, что он потребует много ресурсов процессора и памяти.

Я предложил работу, поясняющую ниже:

  • Создайте вторую таблицу с именем LATEST_SENSOR_DATA.
  • . При вставке данных в таблицу SENSOR_DATA обновите соответствующее значение в таблице LATEST_SENSOR_DATA.

Используя эту технику, янужно только запросить таблицу меньшего размера LATEST_SENSOR_DATA, используя только необходимый ID датчик.

Как звучит это решение и есть ли другие обходные пути?

UPDATE ON 11 /02/2019

Привет, ребята.Спасибо за ваш отзыв.Было очень полезно указать мне в правильном направлении.Сначала я хотел бы заявить, что я неопытен в настройке базы данных для производства.

Я хотел бы дать немного больше информации о дизайне базы данных.

  1. КакПравильно предположил Гордон Линофф, есть главная таблица датчиков, которая содержит метаинформацию о датчике.Это означает, что столбец «sensor_id» в таблице «sensor_data» является столбцом внешнего ключа.

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

  3. Данные в таблице sensor_data никогда не будут обновляться или удаляться пользователем.(За исключением случаев архивирования, когда данные будут удаляться блоками).

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

Особый тип INDEX, который привлек мое внимание, был«Фильтрованные индексы».С их помощью я могу создавать фильтрованный индекс для столбцов (readings_date, sensor_is) для каждого месяца.
Преимущества этого состоят в том, что у меня будут «маленькие» управляемые индексы, которые будет лучше поддерживать, чем один большой индекс длявся таблица (полный индекс таблицы).
С этим решением я думаю, что мне, возможно, придется придерживаться моего первоначального плана обслуживания таблицы latest_sensor_data.

Теперь у меня вопрос, какой из двух сценариев лучше

  1. Создание только отфильтрованных индексов.Используйте таблицу latest_sensor_data для последних данных.

  2. Создание одного большого полного индекса таблицы.Запрашивать последние данные, используя полный индекс таблицы.

Гордон Линофф был также прав, когда угадывал, какой запрос я использовал для получения последних данных (первый запрос в его ответе).Мне потребовалось некоторое время, чтобы понять его второй запрос, но теперь я понимаю, почему такой запрос намного лучше, чем тот запрос, который я использовал.Спасибо.

PS: мне понадобилось время, чтобы расшифровать его синтаксис для псевдонимов таблиц.Я узнал, что ключевое слово «AS» является обязательным, но на самом деле необязательным.

Ответы [ 3 ]

0 голосов
/ 04 февраля 2019

800 000 вставок в день имеет значение.То есть в среднем 10 вставок в секунду.

Запрос, такой как:

select sd.*
from sensor_data sd
where sd.readings_date = (select max(sd2.readings_date)
                          from sensor_data sd2
                          where sd2.sensor_id = sd.sensor_id
                         );

, является разумным.Но, вероятно, включает в себя полное сканирование всей таблицы, даже с индексом на sensor_data(sensor_id, readings_date).

. Это можно улучшить, написав запрос следующим образом:

select sd.*
from sensors s cross apply  -- I assume you have such a table
     (select top (1) sd.*
      from sensor_data sd
      where sd.sensor_id = s.sensor_id
      order by sd.readings_date desc
     ) sd;

Это должно использоватьиндекс, чтобы получить соответствующую строку для каждого датчика.

Вы можете сделать это быстрее, добавив sensor_date к кластерному индексу, чтобы все последние строки встречались вместе.Это, вероятно, будет верно на практике, поэтому это может быть необязательно.Это не учитывает выборку данных об одном датчике.

Для сравнения, добавление новой таблицы со значениями из триггера поможет.Но вам необходимо сбалансировать замедление 800 000 операций вставки в день для любых запросов, которые вы собираетесь выполнять.

В зависимости от ваших потребностей в срочности, вам может потребоваться скопировать данные в другую базу данных (даже на другой сервер).) с использованием периодического задания агента SQL Server.Затем используйте эту другую базу данных, чтобы суммировать данные в «витрине данных».Это уравновешивает «замедление вставок с помощью триггера» с «потребностями быстрого ответа» приложения.

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

0 голосов
/ 04 февраля 2019

Хорошие новости, я думаю, вам не нужна дублирующаяся таблица данных

сначала я сделал большие тестовые данные

truncate table sensor_data;
WITH NOS AS (SELECT row_number() OVER (order by a.name) N FROM sys.all_objects  a,sys.all_objects b)
    insert into sensor_data 
    SELECT  
         row_number() over (order by qsens.n) ID,
         qsens.n Sensor_ID,
        dateadd(second,
                qread.n,
                dateadd(day,qd.n,CAST('20150101' as datetime))
                ) readings_date
                , 456.255 + log(qd.n + qsens.n+qread.n) as reading 
        from nos as qsens, nos as qd , nos as qread
        where qsens.n<=100 and qd.n<1300 and qread.n <=600;


select distinct S1.Sensor_ID  into SENSORS from sensor_data S1;

(и я сделал таблицу датчиков, предложенную Гордоном Линоффом)

затем я добавил индекс

CREATE nonCLUSTERED INDEX [ClusteredIndex-20190204-151527] ON [dbo].[SENSOR_DATA]
(
    [Sensor_ID] ASC,
    [Readings_Date] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO

, тогда следующий запрос очень эффективно дает последние данные для каждого датчика.

select distinct S1.Sensor_ID,SQ.Reading,sq.Readings_Date,sq.ID  from SENSORS S1 
       outer apply (SELECT TOP 1 * from sensor_data S2 
                                    WHERE s2.Sensor_ID = s1.Sensor_ID
                                    ORDER BY S2.Readings_Date DESC) SQ;

Без таблицы «датчиков» ииндекс работает очень плохо.

примечание: я экспериментировал с созданием индексированного представления уникальных идентификаторов датчиков, которое также очень хорошо работает с запросом (после решения некоторых проблем при настройке индексированного представления).Однако их пригодность для использования на сильно обновленных таблицах была поставлена ​​под сомнение в документах.

0 голосов
/ 04 февраля 2019

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...