2012-10-16 1 views
3

У меня есть две таблицы, conttagtable (t) и contfloattable (cf). T имеет около 43 тыс. Строк. CF имеет более 9 миллиардов.Эффективное обращение к таблице огромных временных рядов по одной строке каждые 15 минут

Я установил индекс для обеих таблиц в столбце tagindex на обеих таблицах. Этот столбец можно рассматривать как уникальный идентификатор для conttagtable и в качестве внешнего ключа в conttagtable для confloattable. Я не создал явно PK или внешний ключ в любой таблице, относящейся к другой, хотя эти данные логически связаны столбцом tagindex на обеих таблицах, как если бы conttagtable.tagindex были PRIMARY KEY и contfloattable.tagindex, где FOREIGN KEY (tagindex) REFERENCES conttagtable(tagindex). Данные поступали с дампа доступа к Microsoft и я не знал, могу ли я доверять tagindex быть уникальным, поэтому «уникальность» не применяется.

Данные сами по себе чрезвычайно велики.

Мне нужно получить одиночную произвольно выбранную строку от contfloattable за каждые 15 минут contfloattable.dateandtime интервал для каждого conttagtable.tagid. Итак, если contfloattable для данного tagid имеет 4000 образцов, охватывающих 30 минут, мне нужен образец из диапазона 0-14 минут и образец из диапазона 15-30 минут. Любой образец в пределах 15 минут допустим; 1-й, последний, случайный, любой.

Вкратце, мне нужно получить образец каждые 15 минут, но только один образец на t.tagname. Образцы прямо сейчас записываются каждые 5 секунд, а данные охватывают два года. Это большая проблема с данными и мой взгляд с точки зрения sql. Все решения временного интервала, которые я пробовал из поиска или поиска в SO, дали время запроса, которое так долго, что они непрактичны.

  • Являются ли мои индексы достаточными для быстрого соединения? (они, как представляется, оставляют часть времени)
  • Могу ли я получить дополнительные индексы?
  • Какой лучший/самый быстрый запрос, который выполняет вышеуказанные цели?

Вот SQLFiddle, содержащий схему и некоторые примеры данных: http://sqlfiddle.com/#!1/c7d2f/2

Схема:

 Table "public.conttagtable" (t) 
    Column | Type | Modifiers 
-------------+---------+----------- 
tagname  | text | 
tagindex | integer | 
tagtype  | integer | 
tagdatatype | integer | 
Indexes: 
    "tagindex" btree (tagindex) 


      Table "public.contfloattable" (CF) 
    Column |   Type    | Modifiers 
-------------+-----------------------------+----------- 
dateandtime | timestamp without time zone | 
millitm  | integer      | 
tagindex | integer      | 
Val   | double precision   | 
status  | text      | 
marker  | text      | 
Indexes: 
    "tagindex_contfloat" btree (tagindex) 

Выход я хотел бы видеть что-то вроде этого:

cf.dateandtime  |cf."Val"|cf.status|t.tagname 
-------------------------------------------------- 
2012-11-16 00:00:02 45  S   SuperAlpha 
2012-11-16 00:00:02 45  S   SuperBeta 
2012-11-16 00:00:02 45  S   SuperGamma 
2012-11-16 00:00:02 45  S   SuperDelta 
2012-11-16 00:15:02 45  S   SuperAlpha 
2012-11-16 00:15:02 45  S   SuperBeta 
2012-11-16 00:15:02 45  S   SuperGamma 
2012-11-16 00:15:02 45  S   SuperDelta 
2012-11-16 00:30:02 45  S   SuperAlpha 
2012-11-16 00:30:02 45  S   SuperBeta 
2012-11-16 00:30:02 45  S   SuperGamma 
2012-11-16 00:30:02 45  S   SuperDelta 
2012-11-16 00:45:02 42  S   SuperAlpha 

... и т. Д. И т.д. ...

Как было предложено Клодоальдо, это моя последняя попытка, любые предложения по ее ускорению?

with i as (
    select cf.tagindex, min(dateandtime) dateandtime 
    from contfloattable cf 
    group by 
     floor(extract(epoch from dateandtime)/60/15), 
     cf.tagindex 
) 
select cf.dateandtime, cf."Val", cf.status, t.tagname 
from 
    contfloattable cf 
    inner join 
    conttagtable t on cf.tagindex = t.tagindex 
    inner join 
    i on i.tagindex = cf.tagindex and i.dateandtime = cf.dateandtime 
order by floor(extract(epoch from cf.dateandtime)/60/15), cf.tagindex 

план запроса из вышеизложенного: http://explain.depesz.com/s/loR

+0

отдельная строка для каждого {t.tagname, 15 минутного интервала}. Тег - это всего лишь столбец, описывающий устройство. Устройство в этом случае является датчиком, который записывал значение в определенный момент времени. Меня спрашивают предоставить дискретное значение для каждого устройства (тэг) каждые 15 минут. –

+0

Я не совсем понимаю ваши запросы. В среднем каждая запись 'conttagtable' составляет около 200 000' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''. Как вы получаете это до одной строки-результата для каждого 'conttagtable.tagname'? – ruakh

+0

@ruakh, да, есть много записей. Извините за запутанное объяснение. Допустим, у меня есть 10 тэгов, и каждый тэг записывает значение каждые 5 секунд. Я хочу только 1 из этих записанных значений, вырванных с интервалом 15 минут. Таким образом, для значения времени 01:00:00 я увижу 10 строк. Если бы я хотел два значения времени, 01:00:00 и 01:15:00, я бы увидел 20 строк. Одна строка для каждого тэга за каждые 15-минутный интервал. Это более ясно? –

ответ

2

За 15 минутные интервалы:

with i as (
    select cf.tagindex, min(dateandtime) dateandtime 
    from contfloattable cf 
    group by 
     floor(extract(epoch from dateandtime)/60/15), 
     cf.tagindex 
) 
select cf.dateandtime, cf."Val", cf.status, t.tagname 
from 
    contfloattable cf 
    inner join 
    conttagtable t on cf.tagindex = t.tagindex 
    inner join 
    i on i.tagindex = cf.tagindex and i.dateandtime = cf.dateandtime 
order by cf.dateandtime, t.tagname 

Покажите объяснить вывод для этого запроса (если он работает), чтобы мы могли попытаться оптимизировать. Вы можете опубликовать его в этом ответе.

Объяснить Выходной

"Sort (cost=15102462177.06..15263487805.24 rows=64410251271 width=57)" 
" Sort Key: cf.dateandtime, t.tagname" 
" CTE i" 
" -> HashAggregate (cost=49093252.56..49481978.32 rows=19436288 width=12)" 
"   -> Seq Scan on contfloattable cf (cost=0.00..38528881.68 rows=1408582784 width=12)" 
" -> Hash Join (cost=270117658.06..1067549320.69 rows=64410251271 width=57)" 
"  Hash Cond: (cf.tagindex = t.tagindex)" 
"  -> Merge Join (cost=270117116.39..298434544.23 rows=1408582784 width=25)" 
"    Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))" 
"    -> Sort (cost=2741707.02..2790297.74 rows=19436288 width=12)" 
"     Sort Key: i.tagindex, i.dateandtime" 
"     -> CTE Scan on i (cost=0.00..388725.76 rows=19436288 width=12)" 
"    -> Materialize (cost=267375409.37..274418323.29 rows=1408582784 width=21)" 
"     -> Sort (cost=267375409.37..270896866.33 rows=1408582784 width=21)" 
"       Sort Key: cf.tagindex, cf.dateandtime" 
"       -> Seq Scan on contfloattable cf (cost=0.00..24443053.84 rows=1408582784 width=21)" 
"  -> Hash (cost=335.74..335.74 rows=16474 width=44)" 
"    -> Seq Scan on conttagtable t (cost=0.00..335.74 rows=16474 width=44)" 

Похоже, вам нужен этот индекс:

create index cf_tag_datetime on contfloattable (tagindex, dateandtime) 

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

Update

Я добавил индекс cf_tag_datetime (tagindex, dateandtime) и вот новый объясняю:

"Sort (cost=15349296514.90..15512953953.25 rows=65462975340 width=57)" 
" Sort Key: cf.dateandtime, t.tagname" 
" CTE i" 
" -> HashAggregate (cost=49093252.56..49490287.76 rows=19851760 width=12)" 
"   -> Seq Scan on contfloattable cf (cost=0.00..38528881.68 rows=1408582784 width=12)" 
" -> Hash Join (cost=270179293.86..1078141313.22 rows=65462975340 width=57)" 
"  Hash Cond: (cf.tagindex = t.tagindex)" 
"  -> Merge Join (cost=270178752.20..298499296.08 rows=1408582784 width=25)" 
"    Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))" 
"    -> Sort (cost=2803342.82..2852972.22 rows=19851760 width=12)" 
"     Sort Key: i.tagindex, i.dateandtime" 
"     -> CTE Scan on i (cost=0.00..397035.20 rows=19851760 width=12)" 
"    -> Materialize (cost=267375409.37..274418323.29 rows=1408582784 width=21)" 
"     -> Sort (cost=267375409.37..270896866.33 rows=1408582784 width=21)" 
"       Sort Key: cf.tagindex, cf.dateandtime" 
"       -> Seq Scan on contfloattable cf (cost=0.00..24443053.84 rows=1408582784 width=21)" 
"  -> Hash (cost=335.74..335.74 rows=16474 width=44)" 
"    -> Seq Scan on conttagtable t (cost=0.00..335.74 rows=16474 width=44)" 

Это, кажется, пошли вовремя :(Однако, если я удалить заказ по статье (не то, что мне нужно, но будет работать), это то, что происходит, большое сокращение:

"Hash Join (cost=319669581.62..1127631600.98 rows=65462975340 width=57)" 
" Hash Cond: (cf.tagindex = t.tagindex)" 
" CTE i" 
" -> HashAggregate (cost=49093252.56..49490287.76 rows=19851760 width=12)" 
"   -> Seq Scan on contfloattable cf (cost=0.00..38528881.68 rows=1408582784 width=12)" 
" -> Merge Join (cost=270178752.20..298499296.08 rows=1408582784 width=25)" 
"  Merge Cond: ((i.tagindex = cf.tagindex) AND (i.dateandtime = cf.dateandtime))" 
"  -> Sort (cost=2803342.82..2852972.22 rows=19851760 width=12)" 
"    Sort Key: i.tagindex, i.dateandtime" 
"    -> CTE Scan on i (cost=0.00..397035.20 rows=19851760 width=12)" 
"  -> Materialize (cost=267375409.37..274418323.29 rows=1408582784 width=21)" 
"    -> Sort (cost=267375409.37..270896866.33 rows=1408582784 width=21)" 
"     Sort Key: cf.tagindex, cf.dateandtime" 
"     -> Seq Scan on contfloattable cf (cost=0.00..24443053.84 rows=1408582784 width=21)" 
" -> Hash (cost=335.74..335.74 rows=16474 width=44)" 
"  -> Seq Scan on conttagtable t (cost=0.00..335.74 rows=16474 width=44)" 

Я не вы т пробовал этот индекс ... будет делать это, хотя. ожидание.

Теперь, глядя на нее еще раз, я думаю, что обратный показатель может быть даже лучше, так как он может быть использован не только в Merge Join, но и в конечном Sort:

create index cf_tag_datetime on contfloattable (dateandtime, tagindex) 
+0

Спасибо. Это отлично работает над небольшим набором данных (я вытащил некоторые данные из большего набора, чтобы экспериментировать). Любые идеи о том, как уменьшить время запроса на нем? Я опубликовал новое объяснение выше. –

+1

@SneakyWombat Я опубликовал индекс, который, как я подозреваю, вам нужен. –

+0

- этот индекс в дополнение к существующему? Я предполагаю, что да. Я начал процесс создания этого индекса, но, вероятно, потребуется два часа (или больше). В ожидании, и спасибо за помощь. –

1

Вот другая формулировка. Мне будет очень интересно посмотреть, как он масштабируется в полном наборе данных. Создание этого индекса первый:

CREATE INDEX contfloattable_tag_and_timeseg 
ON contfloattable(tagindex, (floor(extract(epoch FROM dateandtime)/60/15))); 

затем запустить это с столько work_mem, как вы можете себе позволить:

SELECT 
    (first_value(x) OVER (PARTITION BY x.tagindex, floor(extract(epoch FROM x.dateandtime)/60/15))).*, 
    (SELECT t.tagname FROM conttagtable t WHERE t.tagindex = x.tagindex) AS tagname 
FROM contfloattable x ORDER BY dateandtime, tagname; 

Sneaky Вомбат: Объясните сверху SQL на полный набор данных (без предложенного индекса) : http://explain.depesz.com/s/kGo

В качестве альтернативы, здесь требуется только один последовательный проход через contfloattable, со значениями, собранными в tuplestore, то есть JOIN ed, чтобы получить имя тега. Это требует много work_mem:

SELECT cf.dateandtime, cf.dataVal, cf.status, t.tagname 
FROM 
    (
    SELECT (first_value(x) OVER (PARTITION BY x.tagindex, floor(extract(epoch FROM x.dateandtime)/60/15))).* 
    FROM contfloattable x 
) cf 
    INNER JOIN 
    conttagtable t ON cf.tagindex = t.tagindex 
ORDER BY cf.dateandtime, t.tagname; 

подлый вомбат: Объясните сверху SQL на полный набор данных (без предложенного индекса): http://explain.depesz.com/s/57q

Если он работает вы хотите, чтобы бросить столько work_mem как вы можете себе позволить по запросу. Вы не упомянули оперативную память вашей системы, но вы захотите получить приличный кусок ее; попробуйте:

SET work_mem = '500MB'; 

... или более, если у вас есть не менее 4 ГБ ОЗУ и находятся на 64-битном процессоре. Опять же, мне было бы очень интересно посмотреть, как это работает с полным набором данных.

BTW, для правильности этих запросов я бы посоветовал ALTER TABLE conttagtable ADD PRIMARY KEY (tagindex);, затем DROP INDEX t_tagindex;. Это займет некоторое время, так как будет создан уникальный индекс.Большинство упомянутых здесь запросов предполагают, что t.tagindex является уникальным в conttagtable, и это действительно должно быть соблюдено. Уникальный индекс можно использовать для дополнительных оптимизаций, которые старый не уникальный t_tagindex не может, и он дает гораздо лучшие статистические оценки.

Кроме того, при сравнении планов запросов обратите внимание, что cost не обязательно строго пропорционален времени выполнения в реальном времени. Если оценки хороши, то это должно грубо коррелировать, но оценки таковы. Иногда вы увидите, что дорогостоящий план выполняется быстрее, чем предположительно недорогой план из-за таких вещей, как плохие оценки числа строк или оценки избирательности индекса, ограничения в способности планировщика запросов вывести отношения, неожиданные корреляции или параметры затрат, такие как random_page_cost и seq_page_cost, которые не соответствуют реальной системе.

+0

@SneakyWombat ** explain.depesz.com ** пожалуйста, избегайте раздувания вопросов/ответов с подробными планами запросов. Что с двойным цитированием вокруг этих планов? –

+0

Я добавлю этот индекс. Это займет некоторое время, хотя :(в режиме ожидания, также work_mem установлен на 5000 МБ. Система представляет собой четырехъядерный E5520 @ 2,27 ГГц, что дает 16 ядер. 8Gig of ram, raid 5, 1tb памяти.Я знаю, что рейд 5 плохо для записи, но я буду только запрашивать эту информацию, никогда не записывая новые строки. –

+0

@SneakyWombat Хм. Первая оценка составляет 1,4 миллиона строк, вторая - 63 миллиона строк. У 1-го есть более высокая оценка стоимости в 10 раз, поэтому я не уверен, что будет лучше. Глядя на эти планы, мне интересно, в какой версии PostgreSQL вы работаете, и сможете ли вы извлечь выгоду из сканирования только по индексу 9.2 или нет; для получения тэгов в 9.2 следует использовать только индексное сканирование. –

Смежные вопросы