Büyük veri kümesinde saat başı gruplama


12

MS SQL 2008 kullanarak 2,5 milyon kayıttan ortalama bir alan seçiyorum. Her kayıt bir saniyeyi temsil eder. MyField, bu 1 saniyelik kayıtların saatlik ortalamasıdır. Elbette sunucu CPU% 100'e ulaşır ve seçim çok uzun sürer. SQL her istekte tüm bu kayıtları seçmek zorunda değil ki muhtemelen bu ortalama değerleri kaydetmek gerekiyor. Ne yapılabilir?

  SELECT DISTINCT
         CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR) AS TimeStampHour,
         MIN([timestamp]) as TimeStamp,
         AVG(MyField) As AvgField
    FROM MyData
   WHERE TimeStamp > '4/10/2011'
GROUP BY CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR)
ORDER BY TimeStamp

6
TimeStamp kümelenmiş bir dizinin parçası mı?

@antisanity - neden? o disk değil io CPU maxing olduğunu
Jack denemek topanswers.xyz

Yanıtlar:


5

Sorgunun bir kısmı CPU'yu uzun süre maksimize etmek, GROUP BY deyimindeki işlevler ve gruplamanın her zaman bu örnekte benzersiz bir sıralama gerektireceği gerçeğidir. Zaman damgası alanındaki bir dizin başlangıç ​​filtresine yardımcı olurken, bu işlem filtrenin eşleştiği her satırda gerçekleştirilmelidir. Bunu hızlandırmak, Alex'in önerdiği aynı işi yapmak için daha verimli bir yol kullanıyor, ancak yine de büyük bir verimsizliğiniz var, çünkü sorgu planlayıcıyı kullandığınız işlev birleşimi bulamayacak herhangi bir indeks tarafından yardımcı olacak bir şey, bu yüzden önce gruplama değerlerini hesaplamak için fonksiyonları çalıştıran her satırdan geçmesi gerekir, ancak daha sonra verileri sipariş edebilir ve sonuçta ortaya çıkan gruplamalar üzerinden toplamaları hesaplayabilir.

Dolayısıyla çözüm, bir şekilde süreç grubunu bir dizin için kullanabileceği bir şey haline getirmek veya tüm eşleşen satırları aynı anda dikkate alma ihtiyacını ortadan kaldırmaktır.

Saate yuvarlanan süreyi içeren her satır için fazladan bir sütun tutabilir ve bu sütunu bu tür sorgularda kullanmak üzere dizine ekleyebilirsiniz. Bu, verilerinizi denormalize eder, bu nedenle "kirli" hissedebilir, ancak işe yarar ve gelecekteki kullanım için tüm toplamaları önbelleğe almaktan (ve temel veriler değiştikçe bu önbelleği güncellemekten) daha temiz olur. Ek sütun, başka bir yerde mantık tarafından korunmak yerine tetikleyici ile korunmalı veya kalıcı bir hesaplanmış sütun olmalıdır, çünkü bu, veri ekleyebilecek veya zaman damgası sütunlarını veya mevcut satırları güncelleyebilecek tüm mevcut ve gelecekteki yerleri yeni sütunu. Yine de MIN (zaman damgası) çıktısını alabilirsiniz. Sorgunun bu şekilde sonuçlanacağı şey hala tüm satırların aşağı doğru bir yürüyüşüdür (bu önlenemez, açıkçası) ama dizin sırasını yapabilir, gruplama / toplama gerçekleştirilmeden önce dizine eklenmemiş bir sıralama işlemi için tüm satır kümesini hatırlamak yerine, her gruplama için bir satırın dizinde bir sonraki değere ulaşması. Şu anda bakmakta olanı veya geri kalanını işlemek için önceki gruplama değerlerinden herhangi bir satırı hatırlaması gerekmeyeceğinden çok daha az bellek kullanacaktır.

Bu yöntem, tüm sonuç kümesi için bellekte bir yer bulma ihtiyacını ortadan kaldırır ve grup işlemi için dizinsiz sıralamayı yapar ve grup değerlerinin hesaplamasını büyük sorgudan kaldırır (bu işi, veriler) içerir ve bu tür sorguların, toplanan sonuçların ayrı bir deposunu tutmaya gerek kalmadan kabul edilebilir şekilde çalışmasına izin vermelidir.

Bir yöntem değildirverilerinizi denormalize edin, ancak yine de fazladan bir yapı gerektiriyorsa, bir "zaman tablosu" kullanmaktır. Bu tablo, DB veya kayda değer bir boyutta önemli miktarda alan tüketmez - 100 yıllık bir zaman aralığını iki tarihin bir satırını içeren bir tabloyu kapsamak için (saatin başlangıcı ve bitişi, örneğin '2011-01-01 @ 00: 00: 00.0000 ',' 2011-01-01 @ 00: 00: 59.9997 ', "9997", bir DATETIME alanının sonraki saniyeye yuvarlanmayacak en az milisaniye sayısıdır. kümelenmiş birincil anahtar ~ 14Mbyte yer kaplar (satır başına 8 + 8 bayt * 24 saat / gün * 365,25 gün / yıl * 100, ayrıca kümelenmiş dizinin ağaç yapısının ek yükü için biraz, ancak bu ek yük önemli olmayacaktır) .

SELECT CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR) AS TimeStampHour
     , MIN([timestamp]) as TimeStamp
     , AVG(MyField) As AvgField
FROM TimeRangeByHours tt
INNER JOIN MyData md ON md.TimeStamp BETWEEN tt.StartTime AND tt.EndTime
WHERE tt.StartTime > '4/10/2011'
GROUP BY tt.StartTime
ORDER BY tt.StartTime

Bu, sorgu planlayıcının MyData.TimeStamp üzerindeki dizini kullanılacak şekilde ayarlayabileceği anlamına gelir. Sorgu planlayıcısı, MyData.TimeStamp dizininde adım adım uysal tablodan aşağı doğru yürüyebileceği kadar parlak olmalı, yine gruplama başına bir satır çıktısı almalı ve her grup veya satırı bir sonraki gruplama değerine ulaştıkça atacaktır. Aradaki tüm satırları RAM'de bir yerde saklamak ve üzerinde benzersiz bir sıralama yapmak yok. Elbette bu yöntem, zaman tablosunu oluşturmanızı ve hem geriye hem de ileriye doğru yeterince genişlediğinden emin olmanızı gerektirir, ancak "ekstra sütun" seçeneğinin gerektireceği gibi farklı sorgulardaki birçok tarih alanına karşı sorgular için zaman tablosunu kullanabilirsiniz. bu şekilde filtrelemek / gruplamak için ihtiyaç duyduğunuz her tarih alanı için fazladan hesaplanmış bir sütun ve tablonun küçük boyutu (10'a yayılması gerekmedikçe,

Zaman tablosu yöntemi, mevcut durumunuza ve hesaplanan sütun çözümüne kıyasla (bu oldukça avantajlı olabilir) ekstra bir farka sahiptir: yalnızca yukarıdaki örnek sorgudaki INNER JOIN'i değiştirerek veri olmayan dönemler için satırları döndürebilir SOL DIŞ biri olmak.

Bazı insanlar fiziksel bir zaman çizelgesine sahip olmayıp, her zaman bir tablo döndürme işlevinden döndürmeyi önerir. Bu, zaman tablosunun içeriğinin hiçbir zaman diskte depolanmadığı (veya okunması gerektiği) anlamına gelir ve işlev iyi yazılmışsa, zaman çizelgesinin ne kadar zaman içinde ileri ve geri yayılması gerektiği konusunda endişelenmeniz gerekmez, ancak ben şüphe bazı satırlar için bir bellek içi tablo üretme CPU maliyeti her sorgu fiziksel zaman tablosunu oluşturma (ve süresinin ilk sürümünün sınırının ötesine uzanması gerekiyorsa sürdürme) küçük bir tasarruf değerinde değer.

Yan not: Orijinal sorgunuzda da DISTINCT yantümcesine ihtiyacınız yoktur. Gruplama, bu sorguların dikkate alınan periyot başına yalnızca bir satır döndürmesini sağlar; yok sayın ve fazladan CPU zamanı kullanmayın).


3

Bu soruya bakın (bir kat tarihi ) Ayrıca, neden her şeyi dizeye dönüştürmeyle uğraşın - bunu daha sonra yapabilirsiniz (gerekirse).

  SELECT DISTINCT
         dateadd(hour,datediff(hour,0,[timestamp]),0) AS TimeStampHour,
         MIN([timestamp]) as TimeStamp,
         AVG(MyField) As AvgField
    FROM MyData
   WHERE TimeStamp > '4/10/2011'
GROUP BY dateadd(hour,datediff(hour,0,[timestamp],0);
ORDER BY TimeStamp

1

Sorguyu daha hızlı hale getirmek istiyor musunuz veya bir veri anlık görüntüsünün nasıl oluşturulacağını ve kaydedileceğini mi soruyorsunuz?

Daha hızlı yapmak istiyorsanız, TimeStamp alanında kesinlikle bir indekse ihtiyacınız var. Ayrıca, bu saate dönüştürmek için kullanmanızı öneririz:

select convert(varchar(13), getdate(), 121)

Anlık görüntü oluşturmanız ve daha sonra yeniden kullanmanız insert intogerekiyorsa, sorgunuzdaki sonuçlarla yeni bir tablo oluşturmak için kullanın. Dizin tablosuna göre kullanın ve kullanın. Anladığım kadarıyla TimeStampHour'da bir dizine ihtiyacınız olacak.

Ayrıca, yeni toplu tablonuzda günlük verileri toplayan bir iş de ayarlayabilirsiniz.


-1

Grubunuzu yan tümce ile böyle bir dizeye dönüştürerek, aslında veritabanındaki her satıra benzersiz bir isabet yaparsınız. Performansınızı öldüren budur. Herhangi bir yarı iyi sunucu, dizinler doğru kullanılırsa, bir milyon kayıtta olduğu gibi basit bir toplu işleyebilecektir. Sorgunuzu değiştirir ve zaman damgalarınıza kümelenmiş bir dizin koyarım. Bu, performans sorununuzu çözecek, ancak her saatte bir veri hesaplamak sorunu çözüyor.


1
-1 - hayır "veritabanındaki her satıra benzersiz bir isabet yapmıyorsunuz" - herhangi bir dizin TimeStamphala satırları filtrelemek için kullanılacak
Jack diyor denemek topanswers.xyz

-3

İlişkisel bir veritabanı modeli kullanarak bu tür bir hesaplama yapma fikrinden vazgeçmeyi düşünürdüm. Özellikle her saniye değer topladığınız birçok veri noktanız varsa.

Paranız varsa, tarihçi gibi özel bir işlem verisi satın almayı düşünebilirsiniz:

  1. Honeywell Üniforması Doktora
  2. Osisoft PI
  3. Aspentech IP21
  4. vb.

Bu ürünler, aynı zamanda veri çıkarma sorgularının hızlı bir şekilde işlenmesine izin verirken, büyük miktarlarda delice yoğun zaman serisi verilerini (özel formatlarda) depolayabilir. Sorgular birçok veri noktası (etiketler de denir), uzun zaman aralıkları (ay / yıl) belirtebilir ve ek olarak çok çeşitli özet veri hesaplaması (ortalamalar dahil) yapabilir.

.. ve genel bir not: Ben her DISTINCTzaman SQL yazarken anahtar kelimeyi kullanmaktan kaçının . Bu hiç de iyi bir fikir değil. Sizin durumunuzda , hükmünüze DISTINCTekleyerek aynı sonuçları bırakıp alabilmeniz gerekir .MIN([timestamp])GROUP BY


1
Bu gerçekten doğru değil. İlişkisel veritabanı 2,5 milyon kayıt için mükemmeldir. Ve birçok masaya bile katılmıyor. Verilerinizi denormalize etmeniz veya ilişkisel olmayan bir sisteme geçmeniz gerektiğinin ilk göstergesi, birçok tabloda büyük ve karmaşık birleşimler yaptığınızdır. Posterin veri seti aslında ilişkisel bir veritabanı sisteminin mükemmel kabul edilebilir bir kullanımı gibi geliyor.
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.