Zaman serisi verilerinin depolanması


22

Bir dizi ilişkili değere sahip olduğum bir zaman serisi veri seti olduğuna inanmıştım (lütfen hatalıysam beni düzeltin).

Bir örnek, bir otomobilin modellenmesi ve yolculuk sırasında çeşitli niteliklerinin izlenmesi olabilir. Örneğin:

zaman damgası | hız | katedilen mesafe | sıcaklık | vb

Bir web uygulaması, zaman içinde ayarlanan her bir veriyi en fazla, dakika ve arsa bulmak için alanları verimli bir şekilde sorgulayabilmesi için bu verileri saklamanın en iyi yolu ne olurdu?

Veri dökümü ayrıştırma ve sonuçları saklamak için saf bir yaklaşım başlattım; Bununla birlikte, biraz çaldıktan sonra, bu çözümün bellek kısıtlamaları nedeniyle uzun vadede ölçeklenmeyeceği ve önbellek temizlenecek olursa, tüm verilerin yeniden ayrıştırılması ve yeniden önbelleğe alınması gerekir.

Ayrıca, 10 saniyelik veri setlerinin nadir olasılığı ile verilerin saniyede bir izlendiğini varsayarsak, genel olarak her N saniyede bir örnekleme yaparak ayarlanan verilerin kesilmesi önerilir mi?

Yanıtlar:


31

Zaman serisi verilerini depolamak için gerçekten 'en iyi yol' yok ve dürüst olmak gerekirse birçok faktöre bağlı. Ancak, öncelikle olmak üzere iki faktöre odaklanacağım:

(1) Bu proje şemayı optimize etme çabanızı hakettiği için ne kadar ciddi?

(2) Sorgu erişim kalıplarınız gerçekte nasıl olacak?

Bu sorular göz önünde bulundurularak, birkaç şema seçeneğini tartışalım.

Düz masa

Düz bir masa kullanma seçeneğinin soru (1) ile daha çok ilgisi var; bu ciddi veya büyük ölçekli bir proje değilse, şema hakkında fazla düşünmemeyi çok daha kolay bulacaksınız sadece düz bir masa kullanın:

CREATE flat_table(
  trip_id integer,
  tstamp timestamptz,
  speed float,
  distance float,
  temperature float,
  ,...);

Bu kursu tavsiye edebileceğim pek fazla durum yok, ancak bu, zamanınızı çok fazla garanti etmeyen küçük bir proje ise.

Boyutlar ve Gerçekler

Öyleyse, (1) sorusunun engelini çözdüyseniz ve daha fazla performans şeması istiyorsanız, bu göz önünde bulundurulması gereken ilk seçeneklerden biridir. Bazı temel normailizasyonları içerir, ancak “boyutsal” büyüklükleri ölçülen “gerçek” büyüklüklerinden çıkarır.

Temel olarak, gezilerle ilgili bilgileri kaydetmek için bir masa isteyeceksiniz,

CREATE trips(
  trip_id integer,
  other_info text);

ve zaman damgalarını kaydetmek için bir tablo,

CREATE tstamps(
  tstamp_id integer,
  tstamp timestamptz);

ve son olarak tüm ölçülen gerçekler, boyut tablolarına yabancı referanslar ( meas_facts(trip_id)referanslar trips(trip_id)ve meas_facts(tstamp_id)referanslar tstamps(tstamp_id)) ile

CREATE meas_facts(
  trip_id integer,
  tstamp_id integer,
  speed float,
  distance float,
  temperature float,
  ,...);

Bu, ilk bakışta hepsi yardımcı olacak gibi görünmeyebilir, ancak örneğin binlerce eşzamanlı seyahatiniz varsa, hepsi saniyede bir kez, ikincisinde ölçümler alıyor olabilir. Bu durumda, tstampstablodaki tek bir girişi kullanmak yerine, her yolculuk için zaman damgasını her seferinde yeniden kaydetmeniz gerekir .

Kullanım örneği: Veri kaydettiğiniz birçok eşzamanlı gezi varsa ve bu durumda tüm ölçüm türlerine birlikte erişmeye aldırış etmezseniz bu durum iyi olacaktır.

Postgres satırlarla okuduğundan, istediğiniz zaman, örneğin speedbelirli bir zaman aralığında ölçümler meas_factsyaptığınızda, üzerinde çalıştığınız veri kümesinin çalışmasına rağmen kesinlikle bir sorguyu yavaşlatan tablodaki satırın tamamını okumalısınız. çok büyük değil, o zaman farkı bile anlamazsın.

Ölçülen Gerçeklerin Ayrılması

Son bölümü biraz daha genişletmek için, ölçümlerinizi ayrı tablolara bölebilirsiniz, örneğin hız ve mesafe tablolarını göstereceğim:

CREATE speed_facts(
  trip_id integer,
  tstamp_id integer,
  speed float);

ve

CREATE distance_facts(
  trip_id integer,
  tstamp_id integer,
  distance float);

Elbette, bunun diğer ölçümlere nasıl uzatılabileceğini görebilirsiniz.

Kullanım örneği: Yani bu size bir sorgu için muazzam bir hız kazandırmaz, belki bir ölçüm türü hakkında sorgulama yaparken hızdaki doğrusal bir artış olabilir. Bunun nedeni, hız hakkında bilgi aramak istediğinizde, speed_factstablonun bir satırında mevcut olacak tüm ekstra, gereksiz bilgiler yerine yalnızca tablodaki satırları okumanız gerektiğidir meas_facts.

Bu nedenle, yalnızca bir ölçüm türüyle ilgili devasa veri yığınlarını okumanız gerekir, bunun bir faydası olabilir. Bir saniyelik aralıklarla önerilen 10 saatlik veri durumunuzla, yalnızca 36.000 satır okuyacağınız için, bunu yapmanın hiçbir zaman önemli bir faydasını bulamayacaksınız. Ancak, yaklaşık 10 saat süren 5.000 seyahat için hız ölçüm verilerine bakıyorsanız, şimdi 180 milyon satır okumayı düşünüyorsunuz. Böyle bir sorgu için hızdaki doğrusal bir artış, bir seferde yalnızca bir veya iki ölçüm türüne erişmeniz gerektiği sürece, bazı faydalar sağlayabilir.

Diziler / HStore / & TOAST

Muhtemelen bu kısım için endişelenmene gerek yok, ama önemli olduğu durumları biliyorum. Erişmeye gerekiyorsa BÜYÜK zaman serisi miktarlarda veri ve size bir büyük blokta her şeyi erişmesi gereken biliyorsun, kullanımı yapacak bir yapı kullanabilirsiniz TOST Tablolar sıkıştırılmış esasen daha büyük verilerinizi depolar, segmentleri. Bu, amacınız tüm verilere erişmek olduğu sürece verilere daha hızlı erişim sağlar.

Örnek bir uygulama olabilir

CREATE uber_table(
  trip_id integer,
  tstart timestamptz,
  speed float[],
  distance float[],
  temperature float[],
  ,...);

Bu tabloda, tstartdizideki ilk giriş için zaman damgasını saklar ve sonraki her giriş sonraki saniye için bir okuma değeri olur. Bu, bir uygulama yazılımındaki her bir dizi değer için ilgili zaman damgasını yönetmenizi gerektirir.

Başka bir olasılık

CREATE uber_table(
  trip_id integer,
  speed hstore,
  distance hstore,
  temperature hstore,
  ,...);

ölçüm değerlerinizi (anahtar, değer) çiftleri (zaman damgası, ölçüm) olarak eklediğiniz yere.

Kullanım örneği: Bu, PostgreSQL'de daha rahat birisine bırakılmış ve yalnızca toplu erişim düzenleri olması gereken erişim düzenlerinden eminseniz, muhtemelen daha iyi bir uygulamadır.

Sonuçlar?

Vay, bu beklediğimden çok daha uzun sürdü, üzgünüm. :)

Temel olarak, bir dizi seçenek var, ancak muhtemelen daha genel davaya uygun olduklarından, ikinci ya da üçüncüyü kullanarak paranın karşılığını en büyük şekilde alacaksınız.

Not: İlk sorunuz, toplandıktan sonra verilerinizi toplu olarak yükleyeceğiniz anlamına geliyordu. Verileri PostgreSQL örneğinize aktarıyorsanız, hem veri alımınızı hem de sorgu iş yükünüzü işlemek için daha fazla iş yapmanız gerekecek, ancak bunu başka bir zaman bırakacağız. ;)


Vay, detaylı cevap için teşekkürler, Chris! Seçenek 2 veya 3'ü kullanmaya çalışacağım
guest82

Sana iyi şanslar!
Chris,

Vay, mümkünse bu cevabı 1000 kere oylardım. Detaylı açıklama için teşekkürler.
kikocorreoso

1

Onun 2019 ve bu soru güncelleştirilmiş cevabı hak ediyor.

  • Yaklaşımın en iyisi olup olmadığı, sizi kıyaslama ve teste bırakacağım, işte size bir yaklaşım.
  • Timescaledb adlı bir veritabanı uzantısı kullanın
  • Bu standart PostgreSQL üzerine kurulu bir eklentidir ve zaman serilerini oldukça iyi saklarken karşılaşılan çeşitli problemleri çözer

Örneğin, PostgreSQL'de basit bir tablo oluşturun.

Aşama 1

CREATE TABLE IF NOT EXISTS trip (
    ts TIMESTAMPTZ NOT NULL PRIMARY KEY,
    speed REAL NOT NULL,
    distance REAL NOT NULL,
    temperature REAL NOT NULL
) 

Adım 2

  • Bunu , timescaledb dünyasında hiperbilite denilen şeye dönüştürün .
  • Basit bir ifadeyle, her zaman mini tablonun bir yığın olarak adlandırıldığı bir gün demek, sürekli olarak belirli bir zaman aralığındaki daha küçük tablolara bölünmüş büyük bir tablodur.
  • Sorguları çalıştırdığınızda bu mini tablo açık değildir, ancak sorgularınıza dahil edebilir veya hariç tutabilirsiniz

    SELECT create_hypertable ('gezi', 'ts', chunk_time_interval => aralık '1 saat', if_not_exists => TRUE);

  • Yukarıda yaptığımız şey, seyahat masamıza katılmak, her saat başı "ts" sütunu temelinde mini yığın masalara bölmek. Saat 10:00 - 10:59 arasında bir zaman damgası eklerseniz, 1 öbeke eklenirler, ancak 11:00 yeni öbeklere eklenir ve bu sonsuza dek devam eder.

  • Verileri sonsuz bir şekilde saklamak istemiyorsanız, DROP parçalarını 3 aydan eski

    SELECT drop_chunks (aralık '3 ay', 'gezi');

  • Ayrıca, benzeri bir sorgu kullanarak tarihe kadar oluşturulan tüm parçaların bir listesini de alabilirsiniz.

    Chunk_table, table_bytes, index_bytes, total_bytes SELECT chunk_relation_size ('trip');

  • Bu size bugüne kadar oluşturulan tüm mini tabloların bir listesini verecektir ve bu listeden isterseniz son mini tablo üzerinde bir sorgu çalıştırabilirsiniz

  • Sorgularınızı, dahil etmek, hariç tutmak veya yalnızca son N parçalarda çalıştırmak için optimize edebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.