JĘZYK ZAPYTAŃ CIĄGŁYCH
Transkrypt
JĘZYK ZAPYTAŃ CIĄGŁYCH
POLITECHNIKA ŚLĄSKA INSTYTUT INFORMATYKI Labolatorium ZBiHD JĘZYK ZAPYTAŃ CIĄGŁYCH Marcin Gorawski Aleksander Chrószcz Gliwice 2007 Spis treści: 1 WSTĘP 3 2 WPROWADZENIE 4 3 4 5 2.1 ZALETY PRZETWARZANIA STRUMIENIOWEGO 4 2.2 JĘZYK ZAPYTAŃ 4 2.3 POJĘCIA PODSTAWOWE 5 2.4 MODELE PRZETWARZANIA STRUMIENIOWEGO 7 2.4.1 Strumienie danych z interpunkcją 2.4.2 Model przetwarzania strumień/relacja relacja/strumień 11 2.4.3 Algebra operatorów danych temporalnych 13 SYSTEM STREAM APAS V2.0 7 13 3.1 ARCHITEKTURA SYSTEMU PRZETWARZANIA STRUMIENIOWEGO 13 3.2 ARCHITEKTURA WĘZŁA PRZETWARZAJĄCEGO 15 3.3 STRUMIENIE DANYCH 16 3.4 OPERATORY 16 3.4.1 Filtr 17 3.4.2 Mapa 17 3.4.3 Produkt kartezjański 17 3.4.4 Okno czasowe 17 3.4.5 Unia 18 3.5 OPERACJA REORGANIZACJI 18 3.6 KOLEKCJE KROTEK 19 3.7 UPORZĄDKOWANIE KROTEK W STRUMIENIACH 20 JĘZYK ZAPYTAŃ 21 4.1 DRZEWA ATRYBUTÓW 21 4.2 SKŁADNIA ZAPYTANIA 23 4.3 PRZYKŁADOWE ZAPYTANIE 24 LITERATURA 26 1 Wstęp Rozszerzenie definicji danych jako strumieni pozwala skorzystać z potokowości przetwarzania, która zwiększa skalowalność rozwiązań. Elementem wyróżniającym tą klasę systemów jest polityka zarządzania danymi historycznymi. Ograniczenie potrzeby archiwizacji jest koniecznością w przypadku systemów zasilanych dużą liczbą danych. Systemy przetwarzania strumieniowego (ang. Stream Processing System) (w skrócie: systemy strumieniowe) służą przetwarzaniu strumieni danych a nie ich gromadzeniu, powyższa własność sprzyja ich zastosowaniu w analityce i systemach wspierających podejmowanie decyzji(DSS). Dodatkowo uzyskiwane wyniki odzwierciedlają bieżący stan systemu, co jest oczekiwane w zastosowaniach takich jak: nadzorowanie ruchu ulicznego, monitorowanie notowań giełdowych. Przyjęcie koncepcji przetwarzania strumieniowego wiąże się z większą złożonością procesu uruchamiania zapytania, która może wymagać zasilenia danymi historycznymi, słownikami oraz mapami. Ponadto w trakcie działania zapytania należy monitorować węzły przetwarzające, aby w przypadku awarii poprawnie wyłączyć sieć przetwarzania. Przebieg ćwiczenia jest podzielony na następujące etapy: 1. zapoznanie się z podstawami teoretycznymi przetwarzania strumieniowego, 2. zapoznanie się z aplikacją strumieniowej bazy danych, 3. wykonanie przykładowych zapytań. Sprawozdanie powinno zawierać: 1. wstęp krótko definiujący tematykę zajęć, 2. opis przebiegu laboratorium, 3. propozycja rozwiązania problemu przedstawionego przez prowadzącego ćwiczenia. 2 Wprowadzenie 2.1 Zalety przetwarzania strumieniowego Systemy przetwarzania strumieniowego znacząco różnią się od tradycyjnych systemów bazodanowych, w których dane są wpierw zapisywane, następnie wyliczane są indeksy, na koniec wyznaczane odpowiedzi na zgromadzonych zasobach. W przed-stawionej aplikacji źródła generują nieograniczonych rozmiarów strumienie danych, które są na bieżąco przetwarzane przy użyciu długookresowych zapytań. Systemy przetwarzania strumieni wyróżniają następujące cechy [2]: ciągłe przetwarzania – w systemach DBMS obowiązkiem klienta jest zadawanie zapytań na zgromadzonych danych („czy stopa zwrotu osiągnęła zadany pułap„). W systemie strumieniowym użytkownik uruchamia długookresowe zapytanie („powiadom mnie, gdy stopa zwrotu osiągnie zadany pułap”). „wypychanie” danych – źródła danych oraz węzły przetwarzające strumienie przekazują krotki kolejnym obiektom w systemie. W DBMS klienci aktywnie wywołują zapytania w celu aktualizacji wyników, podczas gdy w systemie strumieniowym klient oczekuje na nie pasywnie. krótki czas latencji – wiele systemów strumieniowych monitoruje zachodzące zjawiska przy rygorze krótkiego czasu odpowiedzi. Przykładowo dla systemu nadzorującego ruch uliczny ważniejsza jest bieżąca informacja o stanie drożności ulic niż wystąpienie korków kilka godzin wcześniej. potokowość – przetwarzanie danych bazuje na nieblokowalności, własność ta pozwala na wydajną współbieżną pracę węzłów sieci przetwarzania. skalowalność – każdy węzeł stanowi samodzielną jednostkę co ułatwia umieszczenie sieci przetwarzającej w środowisku rozproszonym. wysoka przepustowość – system strumieniowy nie gromadzi danych tylko je przetwarza dzięki czemu ogranicza się wąskie gardło systemów DBMS, którym jest potrzeba zapisu wszystkich danych na dysk. 2.2 Język zapytań Obecnie brak standardu języka zapytań strumieniowych. Może się wydawać, że zdefiniowanie języka zapytań ciągłych dla strumieni danych nie stanowi problemu, ponieważ algebra relacji systemów strumieniowych nie odbiega znacząco od algebry relacji tradycyjnych baz danych. Idąc dalej wystarczyłoby wziąć język SQL, zastąpić relacje na tabelach relacjami na strumieniach, utworzyć nowy procesor zapytań strumieniowych i zmienić aktywny model obsługi wyników na pasywny. Dla prostych zapytań podejście takie jest wystarczające. Jeżeli jednak planujemy użycie agregatów, zapytań złożonych, okien czasowych oraz dostępu do danych tabelarycznych rozwiązanie takie zawodzi. Rozważmy poniższe zapytanie: Select P.price From Items [Rows 5] as I, PriceTable as P Where I.itemID = P.itemID Items reprezentuje strumień ze sprzedanymi przedmiotami, PriceTable jest tabelą zawierającą ceny produktów oraz [Rows 5] definiuje okno składające się z 5-ciu ostatnich wpisów. Nawet tak proste zapytanie nie posiada jednoznacznej interpretacji. Dla powyższego przykładu można zadać pytanie czy otrzymany wynik jest relacją czy strumieniem. Co się stanie, jeżeli cena ostatnio sprzedanego produktu ulegnie zmianie, podczas gdy obiekt ten nadal będzie znajdował się w 5-cio elementowym oknie? Aby odpowiedzieć na pytanie jakie dane otrzymamy w wyniku realizacji powyższego zapytania należy uściślić model przetwarzania strumieniowego. 2.3 Pojęcia podstawowe Krotka reprezentuje pojedynczą porcję danych przesyłaną w strumieniu. Strumień danych w ogólnym ujęciu to medium które przesyła nieskończoną kolekcję krotek wśród których możemy wyróżnić krotki z danymi oraz krotki specjalne (systemowe, interpunkcji). Należy zwrócić uwagę na to, że strumień przesyła krotki tylko raz. Zatem, jeżeli operator strumieniowy musi odwołać się do krotek nadesłanych wcześniej, musi je przechowywać we wewnętrznych strukturach. Tradycyjna baza danych udostępnia między innymi operatory: filtr( σ ), mapa( µ ), łączenie( >< ),agregacja( α ), grupowanie, unia( U ), różnica. Operatory możemy podzielić na dwie grupy: 1. bezstanowe – operator bezstanowy wylicza wynik w oparciu o krotkę wejściową, bez potrzeby korzystania z dodatkowych danych przechowywanych wewnątrz operatora. Typowymi operatorami bezstanowymi są: filtracja i mapowanie. Do odfiltrowania czyli usunięcia krotki ze strumienia wystarczy aby wartość predykatu wynosiła fałsz, przy czym jest on zdefiniowany tylko na atrybutach krotki wejściowej. 2. stanowe – operator stanowy wymaga przechowywania dodatkowych danych. Dane te mogą być aktualizowane wraz z nadejściem kolejnych krotek na wejście. Zaliczamy tutaj np. operator różnicy. Operacja różnicy usuwa ze strumienia lewego krotki które pojawiły się w strumieniu prawym. Realizacja takiego operatora sprowadza się do przechowywania krotek strumienia prawego w wewnętrznej strukturze operatora, po czym nadejście krotki strumienia lewego wyzwala sprawdzenie czy nie istnieje w wewnętrznej kolekcji operatora; jeżeli nie to zostaje przekazana na wyjście. Większość operatorów tradycyjnej bazy danych nie nadaje się do utworzenia systemu przetwarzania strumieniowego. Wyróżniamy dwie cechy które ograniczają zastosowanie tych operatorów w systemach strumieniowych: 1. blokowalność operatora – operator musi odczytać pełny zbiór danych wejściowych zanim będzie mógł wygenerować wynik (np. operacja grupowania, sortowania). Taka własność operatora nie pozwala na zastosowanie jego w przetwarzaniu strumieniowym, ponieważ strumień jest kolekcją nieskończoną. Użycie takiego operatora skutkowałoby zablokowaniem pracy do momentu otrzymania informacji o zamknięciu strumienia, po czym możliwe byłoby przetworzenie zgromadzonych danych. Warto tutaj zauważyć, że jeżeli znamy charakter danych strumieniowych pojęcie blokowalności operatorów jest bardziej złożonym zagadnieniem. Przyjmijmy, że chcemy pogrupować dane ze strumienia wejściowego względem atrybutu A oraz wiemy, że ten strumień przekazuje krotki w porządku rosnącym względem atrybutu A. Operator grupowania który w ogólnym zastosowaniu jest blokowany w tym przypadku jest nie blokowany, ponieważ na podstawie napływających danych potrafimy zidentyfikować ostatnią krotkę podgrupy. Uogólniając tradycyjny operator grupowania można zastosować do przetwarzania strumieni monotonicznie nie malejących względem atrybutów grupowania. 2. nieograniczony stan operatora – każdy operator w rzeczywistym systemie dysponuje ograniczonym rozmiarem zasobów. Operatory strumieniowe przetwarzają nieskończoną kolekcję krotek, zatem jeżeli do wyliczenia wyniku należy przechowywać otrzymane wcześniej dane wejściowe, w pewnym momencie praca operatora zostanie przerwana z powodu braku wolnych zasobów. Przykładem jest operator eliminacji duplikatów, jeżeli dana krotka pojawia się pierwszy raz na wejściu zostaje ona zapisana do struktury wewnętrznej operatora oraz przekazana na wyjście. Ponowne otrzymanie identycznej krotki skutkuje jej odfiltrowaniem. Samo zdefiniowanie zbioru operatorów nie blokowanych nie wystarcza do zbudowania procesora strumieni. Operatory te musi również cechować ograniczony stan operatora co gwarantuje, że praca systemu nie zostanie wstrzymana na skutek braku wolnych zasobów. Obecnie istnieje kilka propozycji w jaki sposób zaadaptować operatory tradycyjnych baz danych do przetwarzania danych strumieniowych, co zostanie przedstawione w kolejnych paragrafach. Przystępując do budowy operatorów strumieniowych należy odpowiedzieć na pytanie jak zamierzamy zrealizować trzy typy niezmienników: • niezmiennik przejścia (pass invariants) – definiuje kiedy operator dysponuje kompletem danych potrzebnym do wyliczenia wyniku; niezmiennik ten informuje kiedy operator może wygenerować wynik (tzn. kiedy jest odblokowany). • niezmiennik stanu (keep invariants) – definiuje obsługę elementów składających się na stan operatora; niezmiennik ten zabezpiecza przed przepełnieniem struktur wewnętrznych operatora. • niezmiennik propagacji (propagation invariants) – definiuje sposób obsługi krotek specjalnych (systemowych, interpunkcji), mechanizm generacji nowych krotek specjalnych oraz warunki przy jakich następuje ich propagacja do strumieni wyjściowych. 2.4 Modele przetwarzania strumieniowego 2.4.1 Strumienie danych z interpunkcją Interpunkcja reprezentowana jest jako krotka o specjalnym typie. Interpunkcja stabilizuje oraz zaznacza koniec pewnego fragmentu strumienia. Stabilizacja fragmentu strumienia wejściowego służy poinformowaniu operatora o tym, że kolejne krotki które napłyną na wejście nie mają wpływu na bieżący wynik obliczeń. Wyobraźmy sobie że chcemy posortować imiona w strumieniu wejściowym w porządku alfabetycznym, bez mechanizmu interpunkcji musielibyśmy zaczekać do momentu, gdy otrzymamy pełen komplet danych wejściowych. Wystarczy wprowadzić krotkę interpunkcji która powiadomi o tym, że przekazano wszystkie imiona od A-D. Wówczas operator może uporządkować dane oraz przesłać je na wyjście, ponieważ wiemy że kolejne krotki wejściowe nie wpłyną już na ten fragment wyniku zapytania (tzn. wygenerowane krotki wyjściowe będą stabilne). Interpunkcja pozwala nam uczynić operatory nie monotoniczne co najmniej częściowo monotonicznymi, w wyniku tego możliwe jest ich zastosowanie w przetwarzaniu strumieniowym. Przyjmijmy następującą definicję krotki interpunkcji. Krotka interpunkcji definiuje zakres wartości wszystkich atrybutów wchodzących w skład krotki danych. Możemy zdefiniować następujące typy zakresu wartości atrybutów: wildcard (*) – oznacza pełny zakres dziedziny atrybutu, constant – precyzuje pojedynczą wartość atrybutu, range – definiuje zakres wartości atrybutu, list – definiuje listę wartości atrybutu. Korzystając z notacji XML poniżej zamieszczono kilka przykładów krotek interpunkcji: <!--przesłano wszystkie pomiary temperatur do 75 stopni C dla godziny 8 --> <punct:SENSOR> <ID> * </ID> <HOUR> 8 </HOUR> <MINUTE> * </MINUTE> <TEMP> [,75) </TEMP> </punct:SENSOR> <!--przesłano wszystkie pomiary temperatur dla sensorów o numerze identyfikacyjnym: 1,5,6 --> <punct:SENSOR> <ID> {1,5,6} </ID> <HOUR> * </HOUR> <MINUTE> * </MINUTE> <TEMP> * </TEMP> </punct:SENSOR> Akcje wywoływane po odbiorze interpunkcji: - operator identyfikuje które dane może przekazać na wyjście - redukowany jest stan operatora - przekazywane są na wyjście krotki interpunkcji Mówimy że strumień z interpunkcją jest poprawny gramatycznie jeżeli krotka interpunkcji pojawia się w strumieniu wtedy, gdy wszystkie krotki opisane przez tą krotkę interpunkcji zostały już przesłane. Dla uproszczenia analizy zdefiniujmy funkcję match która zwróci wartość true, gdy krotka danych jest opisana przez wskazaną krotkę interpunkcji. Przyjmijmy że strumień danych będziemy oznaczali [| |]. Pojedyncza porcja danych przesyłana w strumieniu z interpunkcją to lista krotek oznaczana [ ]. Aby rozróżnić typ krotki stosujemy literę P jako prefix krotek interpunkcji; oto przykład strumienia danych: [|[1,5], [3], [P(0,4)], [5,6,7],…|]. Przeanalizujemy teraz działanie kilku operatorów zbudowanych na strumieniach z interpunkcją. 2.4.1.1 Operator z pojedynczym strumieniem wejściowym Idee działania takiego operatora można przedstawić jako wywołanie kolejno poniższych funkcji dla każdej krotki wejściowej: step: [input] state ([output], state) pass: [inputp] state [output] prop: [inputp] state [outputp] keep: [inputp] state [state] gdzie: input – wejściowe krotki danych, output – wyjściowe krotki danych, inputp – wejściowe krotki interpunkcji, outputp – wyjściowe krotki interpunkcji, state – struktura w której operator zapisuje stan. Opis funkcji: step - definiuje jak zachowuje się operator po nadejściu nowej krotki danych (tzn. aktualizacje stanu operatora oraz jakie krotki przekazywane są na wyjście). pass - definiuje które krotki danych mogą zostać przekazane na wyjście po otrzymaniu nowej krotki interpunkcji. prop - definiuje które krotki interpunkcji zostają przesłane na wyjście po nadejściu nowej krotki interpunkcji. keep - definiuje które dane wewnątrz operatora mogą zostać usunięte. Jeżeli dana krotka reprezentuje interpunkcję wówczas na wejście input podawana jest krotka pusta, analogicznie gdy na wejściu pojawi się krotka danych wtedy na inputp podawana jest krotka pusta. Przykładem operatora jednowejściowego jest eliminacja duplikatów. Dla powyższego szkieletu otrzymujemy następujące definicje funkcji: step – sprawdza czy krotka wejściowa nie jest duplikatem (przegląda wewnętrzną listę krotek historii), jeżeli jest unikalna przekazujemy ją na wyjście oraz dodajemy do listy krotek historii. pass – operator eliminacji duplikatów jest nie blokujący dlatego brak potrzeby definiowania tej funkcji. prop – przekazuje na wyjście otrzymaną na wejściu krotkę interpunkcji. keep – usuwa z listy (do której wstawiane są unikalne krotki wejściowe) te elementy, które są pokrywane krotkę interpunkcji na wejściu. Prześledzimy działanie operatora eliminacji duplikatów dla strumienia: [| [1,5], [3], [P(0,4)], [5,6,7],…|]. funkcja We stan wy nowy stan step [1,5] [] [1,5] [1,5] prop [] [1,5] [] keep [] [1,5] step [3] [1,5] prop [] [1,5,3] [] keep [] [1,5,3] [1,5] [3] [1,5,3] [1,5,3] 2.4.1.2 step [] [1,5,3] [] prop [P(0,4)] [1,5,3] [P(0,4)] keep [P(0,4)] [1,5,3] step [5,6,7] [5] prop [] [5,6,7] [] keep [] [5,6,7] [1,5,3] [5] [6,7] [5,6,7] [5,6,7] Operator z dwoma strumieniami wejściowymi W przypadku takiego operatora dysponujemy oddzielną grupą funkcji dla strumienia wejścia lewego oraz prawego: stepL stepR passL passR propL popr. keepL keepR Przykładem operatora dwuargumentowego jest różnica strumieni, jego zadaniem jest odfiltrowanie ze strumienia lewego krotki występujące w strumieniu prawym. W operatorze tym definiujemy dwie listy przechowujące oddzielnie interpunkcję dla strumienia lewego oraz prawego. Krotki danych z lewego strumienia mogą zostać przekazane na wyjście jeżeli są pokryte przez interpunkcję prawego strumienia i nie występują w liście krotek danych prawego strumienia. Przekazanie interpunkcji na wyjście wymaga uwzględnienia obu strumieni, tzn. na wyjście przekazywane są tylko te krotki interpunkcji które opisują jednocześnie oba strumienie wejściowe. Funkcji keepR redukuje listę krotek historii dla strumienia prawego w oparciu o krotki interpunkcji przekazane na wejście lewe (zachowywane są tylko te dane które nie są opisane przez interpunkcję strumienia przeciwnego). Jak można zauważyć operator ten nie definiuje funkcji stepL oraz stepR, ponieważ dopiero nadejście krotek interpunkcji pozwala uruchomić proces wyznaczania wyniku. 2.4.1.3 Podsumowanie strumieni z interpunkcją Istotną zaletą strumieni z interpunkcją jest to, że nie nakładają warunków wprowadzających uporządkowanie krotek wewnątrz strumieni. Przedstawiona metoda przetwarzania strumieni danych wiąże się z bardziej złożony projektem sieci przetwarzania strumieniowego, ponieważ należy zapewnić poprawną propagację krotek interpunkcji. Zadanie to nie zawsze jest banalne, wyobraźmy sobie operator który dokonuje konwersji liczby na tekst. Konwersji wymagają również krotki interpunkcji które pojawiają się na wejściu, prosta zamiana z P(11,33) na P(„11”,”33”) jest błędna, ponieważ przedział nazw od „11” do „33” przy uwzględnieniu porządku alfabetycznego obejmuje większy zbiór dozwolonych wartości niż tylko liczbowe. Pozostaje także ustalić z jaką częstością generować krotki interpunkcji, tak aby system działał płynnie z drugiej strony wzrost kosztu pracy sytemu wynikający z takich krotek był utrzymany na niskim poziomie. 2.4.2 Model przetwarzania strumień/relacja relacja/strumień Podejście stream-to-relation-to-stream wychodzi z założenia, że operatory tradycyjnych baz danych mogą przetwarzać strumienie jeżeli zdefiniujemy następujące funkcje konwersji: • konwersja strumień-tabela • konwersja tabela-strumień Nadejście nowej krotki w takim modelu przetwarzania wyzwala aktualizację tabeli budowanej na wskazanym strumieniu wejściowym, po czym następuje wyliczenie zapytania tak jakby miało to miejsce w tradycyjnej bazie danych. Otrzymana tabela rekordów wynikowych jest w dalszym etapie konwertowana do postaci strumienia wyjściowego. Taka koncepcja przetwarzania danych strumieniowych sprowadza problem do wprowadzenia dwóch nowych operatorów. 2.4.2.1 Konwersja strumień-tabela Naturalną funkcją konwertującą dane strumieniowe do postaci tabeli jest okno czasowe. Przyjmijmy, że każda krotka posiada znacznik czasu τ , zatem można ją opisać jako parę ( s, τ ) gdzie: s - wartości atrybutów, τ - znacznik czasu. Okno czasowe jest to funkcja która definiuje czas życia elementu tabeli na postawie znacznika czasu krotki. Nadejście nowej krotki na wejście skutkuje aktualizacją bieżącego czasu operatora zgodnie ze znacznikiem czasu tej krotki. Wyróżniamy następujące okna czasowe: przesuwne (sliding window) – parametrem okna jest rozmiar T w jednostkach czasu. Każda nowa krotka wejściowa wstawiana jest do tabeli, następnie aktualizowany jest bieżący czas operatora, po czym tabela jest oczyszczana z krotek których znaczniki czasu wychodzą poza zakres okna. Okno przesuwne pozwala zgrupować krotki z przeciągu ostatnich T jednostek czasu. Działanie tego okna można wyrazić: R(τ ) = {s | ( s,τ ' ) ∈ S ∧ (τ ' ≤ τ ) ∧ (τ ' ≥ max(τ − T ,0))} gdzie: S – strumień danych τ - bieżący czas operatora T – rozmiar okna sekwencyjne (fixed window) – parametrem okna jest rozmiar T w jednostkach czasu, interwał tego okna definiujemy następująco: znacznik początku: i*T znacznik końca: (i+1)*T Przy jego użyciu możemy grupować krotki np. w przedziały godzinne przy czym początek każdego okna przypada na pełną godzinę zegarową. 2.4.2.2 Konwersja tabela-strumień Wyróżniamy kilka konwersji tabeli wynikowej na strumień. W najprostszym przypadku wstawiamy do strumienia wszystkie krotki tabeli wynikowej. Nie jest to najrozsądniejsze rozwiązanie. Wyobraźmy sobie, że realizujemy operator łączenia strumieni. W wyniku nadejścia nowej krotki aktualizowane są tabele wejściowe operatora, następnie generowany wynik zgodnie z predykatem łączenia. Należy zauważyć, że utworzona tabela wynikowa zawiera wszystkie poprawne połączenia krotek zbudowane na tabelach źródłowych, a nie tylko wynik połączenia nowej krotki z tabelą krotek strumienia przeciwnego. Wstawienie krotek takiej tabeli wynikowej do strumienia wyjściowego skutkuje wprowadzeniem do systemu ogromnej liczby duplikatów. Dlatego udostępniono bardziej złożone operatory konwersji tabeli wynikowej na strumień: Istream (insert stream) – do strumienia wyjściowego wstawiane są tylko krotki nowe względem poprzedniej tabeli wynikowej. Istream(R ) = U ((R(τ ) − R(τ − 1))× {r}) τ ≥0 Dstream (delete stream) – do strumienia wyjściowego wstawiane są elementy których zostały usunięte z poprzedniej tabeli wynikowej. Istream(R ) = U ((R(τ − 1) − R(τ ))× {r}) τ >0 Rstream (relation stream) – do strumienia wyjściowego wstawiane są wszystkie elementy tabeli wynikowej. Rstream(R ) = U (R(τ )× {r}) τ ≥0 2.4.2.3 Podsumowanie modelu przetwarzania strumień/relacja relacja/strumień Model takiego przetwarzania strumieni w odniesieniu do przetwarzania strumieni z interpunkcją jest wolny od potrzeby definiowania mechanizmu generowania krotek interpunkcji. Model ten z drugiej strony nakłada warunek uporządkowania krotek strumieni chronologicznie względem znaczników czasu. Jeżeli operator ma kilka strumieni wejściowych należy krotki tych strumieni wejściowych wstępnie uporządkować. Słabą stroną takiego przetwarzania strumieni jest ograniczenie sterowania stanem operatora do funkcji okien czasowych. Drugim słabym elementem takiego modelu przetwarzania jest przewaga operatorów typu strumień-relacja-strumień które są wolniejsze od operatorów strumień-strumień. Główną zaletą takiego podejścia do przetwarzania strumieni jest prostota tworzenia zapytań. Do nauczenia się obsługi takiej platformy przetwarzania strumieni wystarczy zaznajomienie się z funkcjami konwersji strumień tabela, ponieważ działanie reszty operatorów jest identyczne jak w tradycyjnych bazach danych. 2.4.3 Algebra operatorów danych temporalnych Chęć utworzenia modelu przetwarzania w którym wszystkie operatory byłyby typu strumień-strumień poskutkowało zdefiniowaniem algebry operatorów dla danych temporalnych. Przystępując do budowy tego modelu przetwarzania zadecydowano, aby każda krotka indywidualnie definiowała czas aktywności poprzez znaczniki początku i końca czasu życia. Z chwilą kiedy istnienie krotki jest umiejscowione w czasie, definicja operatorów staje się o wiele prostsza. Ustalenie które krotki tworzą stan operatora sprowadza się do zweryfikowania ich czasu życia z bieżącym czasem operatora. Warto tutaj zwrócić uwagę na inną konstrukcję operatora łączenia. Aby połączyć krotki dwóch strumieni nie tylko musi zachodzić predykat łączenia ale również łączone krotki muszą mieć wspólny okres czasu życia. Utworzona krotka wynikowa składa się z atrybutów krotek łączonych, a jej czas życia to część wspólna czasów życia krotek łączonych. Precyzyjniejszy opis implementacji tej algebry zamieszczono w następnym rozdziale. 3 System Stream APAS v2.0 3.1 Architektura systemu przetwarzania strumieniowego poglądowy Na rys.1 zamieszczono strumieniowego. schemat Interfejs użytkownika zbudowanego przetwarzania N adzorca zapytań S ieci przetwa rzania R ejestracja strum ieni systemu Zarządca warstwy zasilania N asłuchiwanie zgłoszeń źródeł K om pilator zapytań K atalog zasobów K om unikacja ze źródłam i Rys. 1. Architektura systemu przetwarzania strumieniowego Fig. 1. The architecture of the stream processing system Poniżej przedstawiono opis ważniejszych elementów składowych systemu. Zarządca warstwy zasilania – moduł ten aktualizuje informacje o meta-schemacie strumieni w ramach katalogu zasobów oraz zasila danymi uruchomione strumieniowe sieci przetwarzania. Aby podłączyć do systemu nowe źródło danych należy wpierw zarejestrować nazwę, pod którą dany zasób będzie dostępny. W ramach rejestracji nie podajemy definicji meta-schematu strumienia, dostarcza go źródło danych. Po dodaniu nowego strumienia jest on widoczny w katalogu zasobów jako nie podłączony oraz z pustym meta-schematem. Jeżeli użytkownik zbuduje zapytanie zasilane takim strumieniem, kompilator zgłosi błąd informujący o braku atrybutów, do których zdefiniowano odwołania. Procedura przyjęcia zgłoszenia źródła danych: • pobranie adresu zgłaszanego źródła, • utworzenie wątku obsługującego wskazany kanał komunikacji, • oczekiwanie na krotkę definiującą meta-schemat strumienia, • odbiór krotek z danymi. W trakcie pracy strumienia możliwa jest zmiana meta-schematu. W tym celu źródło przesyła krotkę systemową z nową definicją. Zarządca warstwy zasilania aktualizuje wtedy zawartość katalogu zasobów oraz przekazuje tą krotkę do strumieni wewnątrz systemu. Dzięki temu uruchomione zapytania zasilane tym źródłem informowane są o aktualizacji. Katalog zasobów – repozytorium dostępnych w systemie strumieni. Nadzorca zapytań – zapytanie strumieniowe to długookresowy proces, dlatego rolą nadzorcy zapytań jest nie tylko kontrolowanie przebiegu kompilacji, ale również nadzór uruchomionej sieci przetwarzania. Aby uprościć zarządzanie uruchomionymi sieciami przetwarzania przyjęto, że zapytania są rejestrowane pod wybraną przez użytkownika nazwą. Dzięki temu w chwili wystąpienia awarii łatwo odnaleźć jej przyczynę na podstawie logów historii gromadzonych dla każdego zapytania oddzielnie. Przetwarzanie strumieniowe jest procesem wielowątkowym, dlatego moduł ten implementuje procedurę bezpiecznego wyłączenia zapytania, która gwarantuje zwolnienie wszystkich zasobów systemowych zajętych przez daną sieć przetwarzania. Kolejnym zadaniem nadzorcy zapytań jest obsługa komunikatów specjalnych generowanych przez sieć przetwarzania. Sygnalizują one: • wykrycie zmiany meta-schematu jednego z strumieni źródłowych, • usunięcie strumienia źródłowego z katalogu strumieni, • odbiór wyjątku w trakcie pracy węzła przetwarzającego. Jeżeli zapytanie nie definiuje metody obsługi komunikatu, nadzorca przystępuje do wyłączenia sieci przetwarzania. Kompilator zapytania – kompiluje zapytanie do postaci sieci przetwarzania strumieniowego. Potrzebne informacje o strukturach strumieni pobiera z katalogu zasobów, z kolei wykryte błędy w zapytaniu kieruje do nadzorcy zapytania. Sieć przetwarzania – przy użyciu operatorów fizycznych implementuje operacje logiczne zdefiniowane poprzez zapytanie w postaci tekstowej. Sieć przetwarzania to węzły przetwarzające połączone strumieniami danych. Węzeł przetwarzający jest platformą na której uruchamiane są operatory fizyczne. Architektura taka w przejrzysty sposób rozdziela funkcjonalność wykorzystywaną przez większość operatów fizycznych (taką jak porządkowanie krotek strumieni wejściowych, obsługa komunikatów błędów) od konkretnej realizacji operatora fizycznego. Ponieważ przetwarzanie strumieniowe jest nie blokowalane każdy z węzłów przetwarzających pracuje w ramach oddzielnego wątku. Przetwarzanie strumieniowe jest nie blokowalne tzn. dany operator nie przetwarza całego zbioru danych wejściowych zanim przekaże wyliczone wyniki do kolejnego operatora w planie produkcji. Proces przetwarzania strumieniowego dzięki zastosowaniu okien czasowych jest nie blokowalny czyli dla każdej krotki wejściowej operator potrafi natychmiast wyliczyć wynik. 3.2 Architektura węzła przetwarzającego Aby strumienie wewnątrz pojedynczego zapytania działały wydajnie, każdy z nich transportuje minimalny zbiór atrybutów konicznych do ukończenia przetwarzania zapytania. W ę z e ł P r z e t w a r z a ją c y S y n c h r o n iz a c j a s t r u m ie n i W y lic z a n y o p e ra to r a lg e b r y µ k o le k c j e g r o m a d z ą c e k r o t k i Rys. 2. Przepływ krotek w węźle przetwarzającym Fig. 2. Tuples data flow in the processing node Cel ten jest osiągany poprzez wprowadzenie operacji mapującej na wyjściu każdego węzła przetwarzającego. Na rys. 2. przedstawiono przepływ krotek w trakcie wyliczania zapytania. Scenariusz działania jest następujący: 1. Uporządkowanie leksykograficzne krotek jeżeli operator posiada kilka strumieni wejściowych, 2. Reorganizacja kolekcji w oparciu o znacznik czasu krotki przekazanej do przetworzenia, 3. Wstawienie krotki do kolekcji, 4. Realizacja operacji algebry, 5. Wyznaczenie krotki wynikowej (wstawienie do strumienia wynikowego krotki z zredukowaną liczbą atrybutów). Jeżeli dana kolekcja zasila dodatkowo indeks, zamiany zachodzące w wyniku wstawienia krotki i reorganizacji struktury zgłaszane są przy użyciu wydarzeń: insert, update, delete. 3.3 Strumienie danych Niech I := {[t s , t e ) ∈ T × T | t s ≤ t e } oznacza zbiór przedziałów czasowych włącznie z punktami czasowymi, gdzie T jest dziedziną czasu, wprowadzamy również Ω , która oznacza przestrzeń dostępnych atrybutów. Definicja 1. ( ) Strumień opisuje para symboli S = M ,≤ ts ,te gdzie: M - nieskończony strumień krotek (type, e, [t s , t e ) ) gdzie: type - typ krotki, e – dane transportowane przez krotkę [t s , t e ) ∈ I ≤ t s ,te - uporządkowanie leksykograficzne elementów strumienia M (wpierw po ts potem po te). W systemie rozróżniamy kilka typów krotek (tab.1), przez co możliwe jest zarządzanie siecią przetwarzania przy użyciu strumieni źródłowych. Tab .1. Typy krotek w systemie Typ Opis HEADER definiuje metaschemat krotek INSERTION REMOVE Informuje o usunięciu strumienia z systemu STOP wstrzymuje działanie operatorów INSERTION służy transmisji wartości atrybutów BOUNDARY znacznik upływu czasu 3.4 Operatory Logika operatorów algebry bazuje na architekturze zaproponowanej w [3]. Aktualnie system wspiera operatory: filtr( σ ), mapa( µ ), łączenie( >< ), okno czasowe( ω ), unia( U ), agregacja( α ). Ponieważ w dalszej części podrozdziału przedstawione zostaną operatory zdefiniowane tylko na krotkach typu INSERTION, pominięty zostanie element type definicji krotki. 3.4.1 Filtr Niech P jest zbiorem predykatów filtracji takich, że p ∈ P p : (Ω × T ) → {true, false} . Filtr σ : S × P → S zwraca krotki, dla których predykat ma wartość pozytywną. W systemie strumieniowym operator filtracji definiujemy: σ p (S ) := {(e, t ) ∈ S | p (e, t )} 3.4.2 Mapa Niech Fmap jest zbiorem wszystkich funkcji mapujących takich, że f ∈ Fmap f : Ω → Ω ; warto zauważyć, że f może być funkcją n-argumentową, ponieważ Ω jest uniwersum atrybutów krotki. Operator mapy µ f : S × Fmap → S dla systemu strumieniowego definiujemy: µ f (S ) := {(e, t ) | (eˆ, t ) ∈ S | e = f (eˆ )} Należy zauważyć, że ogólność definicji operatora mapy pozwala na realizowanie przy jego użyciu również operacji projekcji atrybutów. 3.4.3 Produkt kartezjański Produkt kartezjański × : S × S → S dla systemu strumieniowego definiujemy: × (S1 , S 2 ) := {(o (e1 , e2 ), t ) | (∃(e1 , t1 ) ∈ S1 ∧ ∃(e2 , t 2 ) ∈ S 2 ∧ int er sec t (t1 , t 2 )), t = overlap(t1 , t 2 )} Operator łączy ze sobą tylko te krotki, których czas życia nachodzi na siebie. Wynikowa krotka otrzymuje czas równy części wspólnej czasów życia łączonych krotek. 3.4.4 Okno czasowe Utworzona logika modeluje okna czasowe poprzez przypisanie każdej krotce czasu życia[3]. Niech w∈ T , operator okna czasowego ω definiujemy wtedy: ω w : S × T → S . Pozwala to utworzyć między innymi okna: infinity – czas życia krotki ustawiany jest na nieskończoność; krotka (e,[t s , t e )) jest przetwarzana do postaci (e,[t s , ∞)) . range – tworzy okno przesuwające się; krotka (e,[t s , t e )) jest przetwarzana do postaci (e,[t s , t s + w)) , gdzie: w – czas życia krotki. fixed – dziedzina czasu zostaje podzielona w na sekcje o stałym rozmiarze w. Sekcja rozpoczyna się w chwili i * w gdzie i ∈ N 0 a kończy (i + 1) * w . Dla każdej krotki (e,[t s , t e )) wyznaczany jest nowy znacznik t e = i * w będący liczbą najbliższą ts spełniającą warunek t s < t e . Jeżeli nie znamy czasu życia krotki źródłowej należy go ustawić na zero, w przeciwieństwie do zaproponowanej w [3] nieskończoności. Założenie to nie zmienia funkcjonalności, ponieważ dysponujemy operatorami okien czasowych, zabezpiecza z kolei przed nieumyślną budową okien o nieskończonym rozmiarze. 3.4.5 Unia Operator unii ∪ + : S1 × S 2 × ... × S n → S łączy kilka strumieni wejściowych w jeden strumień wynikowy. Rezultat zawiera wszystkie krotki S i dla i ∈< 1, n > . Warunkiem koniecznym użycia unii jest zgodność meta-schematów strumieni wejściowych. 3.5 Operacja reorganizacji Reorganizacja wyzwalana jest przez kolejne krotki przekazywane węzłowi na wejście zanim zostaną jeszcze przetworzone. Jej zadaniem jest oczyszczanie operatora z elementów, których czas życia upłynął [3]. Niech S1 ,..., S n ∈ S gdzie n ∈ N są strumieniami wejściowymi operatora. Na potrzebę operacji reorganizacji przechowujemy najstarsze znaczniki czasu t s j gdzie j ∈ {1,..., n} krotek kolejnych strumieni wejściowych oczekujących na przetworzenie. Każda krotka (e,[t s , te ) ) może zostać bezpiecznie usunięta ze struktur wewnętrznych operatora jeżeli jej te jest mniejsze lub równe { } min t s j | j ∈ {1,..., n} . Wywołanie reorganizacji przed wykonaniem właściwej operacji węzła pozwala przyspieszyć wyliczania wyników, ponieważ operator nie musi wówczas sprawdzać krotek, co do których mamy pewność, że nie będą tworzyły wyniku. 3.6 Kolekcje krotek Kolekcje służą przechowywaniu elementów pochodzących z jednego strumienia. Występują one tylko w węzłach reprezentujących operatory stanowe. Po uwzględnieniu faktu, że każda krotka ma ustalony czas życia oraz kolekcje mogą zasilać indeksy, obiekt kolekcji krotek udostępnia następującą funkcjonalność: 1) przeglądanie zawartości zgodnie z porządkiem leksykograficznym, 2) wstawianie krotek, 3) realizacja operacji reorganizacji, 4) dostęp do krotek poprzez identyfikator numeryczny, 5) zgłaszanie wydarzeń o zmianie zawartości kolekcji: • usunięcie wpisu, • wstawienie wpisu, • aktualizacja wpisu, • reorganizacja. Dodanie indeksów wyliczających relacje wiąże się z potrzebą zdefiniowania atrybutu kluczowego. Jest on wartością numeryczną, jeżeli krotka nie definiuje takiego atrybutu wówczas kolekcja automatycznie nadaje wartość unikalną kluczowi. Aby zapewnić spójność danych w węźle, aktualizacja indeksu jest wywoływana natychmiast po aktualizacji kolekcji, na którym dany indeks jest zbudowany. Prezentowany system wspiera kolekcje: 1) czasowe, 2) czasowo-tabelaryczne. Kolekcja czasowa: − wstawianym krotką przypisywane są automatycznie unikalne wartości klucza, − czas życia obiektów zależy od wartości atrybutów ts, te krotek, − elementy kolekcji są usuwane w wyniku wywołania operacji reorganizacji. Kolekcja czasowo-tabelaryczna: − czas życia obiektów zależy od wartości atrybutów ts, te krotek, − elementy kolekcji są usuwane w wyniku wywołania operacji reorganizacji, − krotka zawiera atrybut kluczowy który steruje funkcjami: wstawiana, aktualizacji, zmiany czasu życia krotki. Przekazanie do kolekcji czasowo-tabelarycznej krotki o atrybucie kluczowym id skutkuje operacją: id krotki nie istnieje w zbiorze – krotka jest dodana do zbioru, id krotki istnieje w zbiorze – krotka poprzednia jest zastąpiona nową, id krotki jest liczbą ujemną – atrybut te krotki będącej w kolekcji jest zastępowany atrybutem te nowej krotki. Importowanie rekordów z tabel można zrealizować poprzez skopiowanie wartości atrybutów do krotki, ustawieniu znacznika ts na bieżący czas oraz t e = ∞ . Operacja usuwania rekordu odpowiada wysłaniu krotki o ujemnym identyfikatorze oraz przekazaniu informacji o czasie usunięcia poprzez atrybut te. Właściwe usunięcie elementu z kolekcji ma miejsce dopiero podczas fazy reorganizacji. Jeżeli zostanie wysłana krotka o identyfikatorze istniejącym już w kolekcji, zastąpiony zostanie poprzedni wpis nowym, włącznie z aktualizacją znaczników czasu. Każda operacja na krotce kolekcji wiąże się z aktualizacją znacznika ts, co ma na celu zagwarantowanie niezmienności prefiksu krotek przetworzonych przez operator (zmiany nie obejmują historii). 3.7 Uporządkowanie krotek w strumieniach Definicja 2. Prefiksu sekwencji krotek: Pod-sekwencja krotek rozpoczynająca się od najstarszej kończąc na krotce ze znacznikiem [ts,te) (korzystamy z porządku leksykograficznego). Operatory zasilane kilkoma strumieniami wejściowymi wymagają przetwarzania krotek w porządku leksykograficznym. Powyższa własność gwarantuje finalność generowanych wyników, ponieważ nie modyfikujemy prefiksu krotek dla [ts,te). Aby uniknąć konieczności przesyłania replik krotek w strumieniach reprezentujących zjawiska o małej zmienności oraz w celu utrzymania płynności przetwarzania, wprowadzono krotkę typu BOUNDARY [2]. Pełni ona rolę mechanizmu „bicia serca”. Znacznik czasowy krotki tego typu (e,[t s , t s ) ) informuje, że kolejne elementy w strumieniu nie będą posiadały dat wcześniejszych. Podobnie jak dla operacji reorganizacji, przechowujemy najstarsze znaczniki [t s j , t e j ) gdzie j ∈ {1,..., n} krotek kolejnych strumieni wejściowych oczekujących na przetworzenie. Kolejna krotka pobrana ze strumienia wejściowego i przekazana do dalszego przetwarzania spełnia warunek: { } [t s , t e ) ≤ ts ,te min [t s j , t e j ) | j ∈ {1,..., n} k r o t k a z w y k ła 15 16 S t r u m ie ń S 1 S t r u m ie ń S 2 Rys. 3. Fig. 3. k ro tk a B O U N D A R Y 10 14 11 13 24 24 19 20 19 21 20 14 czas Porządkowanie krotek strumieni wejściowych. Liczby w wierszach oznaczają odpowiednio ts, te Sorting the input streams tuples. The numbers in boxes denotes ts, te Po przetworzeniu kolejek wejściowych z rys.3 pozostaną w kolejkach ostatnie obiekty. Krotka BOUNDARY pozwoliła na przekazanie do dalszego przetworzenia obiekty o ts = 19, 19, 20. Mechanizm ten rozwiązuje problem zachowania płynności przetwarzania w przypadku, gdy strumienie rzadko nadsyłają krotki z danymi. 4 Język zapytań 4.1 Drzewa atrybutów Dane w języku zapytań StreamAPAS v2.0 reprezentowane są jako drzewa atrybutów. Etykieta korzenia wskazuje strumień systemu. Każdy z węzłów drzewa posiada nazwę, typ oraz identyfikator atrybutu w krotce. Nie każdy węzeł struktury musi posiadać zdefiniowany typ. Takie puste węzły tworzą strukturę danych czytelniejszą poprzez wprowadzenie zgrupowań. W poniższym przykładzie rolę tą pełni węzeł position. Błąd! Nie można tworzyć obiektów przez edycję kodów pól. Rys. 4. Przykład struktury drzewa etykiet Fig. 5. Example of attribute tree Ponieważ każda krotka posiada znacznik początku i końca życia ich identyfikatory mają wartości stałe kolejno 0, 1. Chcąc odczytać atrybut I.position.x (rys.1) należy wpierw odczytać wartość identyfikatora atrybutu, następnie pobrać atrybut o wskazanym identyfikatorze z krotki strumienia. Drzewo informacji jest reprezentowane przez zagnieżdżone listy atrybutów. Skorzystanie z definicji, w której rozpatrywalibyśmy drzewo jako zagnieżdżone wielozbiory [6] nie pozwoliłoby odwoływać się do atrybutów przy użyciu numeru porządkowego. W konsekwencji funkcje operujące na drzewach informacji mogłyby odwoływać się do atrybutów tylko poprzez etykietę atrybutu. Przyjęcie koncepcji zagnieżdżonych list atrybutów pozwala rozpoznawać atrybuty funkcji zgodnie z numerem porządkowym (tak jak ma to miejsce w językach C/C++) lub na podstawie etykiety. Dla danej zagnieżdżonej listy etykiet A, definiujemy listę LT atrybutów indeksowany poprzez I tak, że: -pusta lista etykiet {}, należy do LT, -jeśli m występuje w A oraz I w LT, wtedy istnieje para {<m,I>} w LT, -LT jest unią zagnieżdżonych list U M ( j ) , gdzie J jest zbiorem indeksów i M ∈ J → LT . j∈J Przyjmując powyższą definicję drzewa etykiet, utworzono funkcję mapującą wyrażenie A na atrybuty krotki strumienia. η ::= $nazwa nazwa A, B ::= T etykieta węzła drzewa etykieta korzenia etykieta węzła w bieżącym kontekście wyrażenie drzewo atrybutów począwszy od bieżącego kontekstu η [ A] lokacja bieżącego kontekstu na węźle η A , B kompozycja drzew atrybutów Aby uprościć odwoływanie się do pojedynczych węzłów np. O[z[x = 1.1]] następujące po sobie klamry można zastąpić „.”. Otrzymamy wtedy postać O.z.x = 1.1. Na zdefiniowanej powyżej strukturze danych zostały zdefiniowane podstawowe operacje matematyczne, logiczne oraz operacja kopiowania drzewa atrybutów. Przykładowe wyrażenie: O[z = $I.position.x + $I.position.y, $I. position[T]] Domyślnym kontekstem etykiet jest lista nazw strumieni, dlatego nie ma potrzeby zapisu $O. W podanym przykładzie występuje operacja przypisania z = … która tworzy nową etykietę o typie zgodnym z wyliczeniem po prawej stronie. Jeżeli etykieta zostałaby wcześniej zdefiniowana, wynik konwertowany byłby do zdefiniowanego typu. Jeżeli brak odpowiedniego rzutowania nastąpi zgłoszenie błędu. W wyniku kompozycji …, $I.position do struktury wynikowej zostaną skopiowane atrybuty ze wskazanego węzła. Należy zwrócić uwagę na prostotę przenoszenia ze struktury źródłowej grupy zmiennych związanych ze sobą znaczeniowo. Obliczenia etykiet wykonywane są zgodnie z kolejnością wystąpienia np.: O[z = $I.place.x + $I.place.y, z = 1.0] zostanie wyliczone $O.z = $I.place.x + $I.place.y $O.z = 1.0 4.2 Składnia zapytania Pojedyncze zapytanie przetwarzania strumieniowego to sieć powiązanych ze sobą pod-zapytań. Po słowie kluczowym from następuje definicja kolejnych strumieni wewnątrz tej sieci. Na koniec w frazie select podajemy strumień wynikowy zdefiniowany na jednym ze strumieni wewnętrznych. Lista strumieni źródłowych potrzebna do budowy pod-zapytań wyznaczana jest na podstawie analizy atrybutów w frazie select i where. Składnia języka: from {<deklaracja strumienia>;} selekt <wyliczenie wartości atrybutów> Deklaracja strumienia: uproszczona <wyliczenie wartości atrybutów> pełna where wyrażenie operatorowe select <wyliczenie wartości atrybutów> operatory – operacja: łączenia, filtracji, okno czasowe, funkcja relacyjna (np. implementacja indeksu), unii wyliczenie wartości atrybutów – definicja drzewa atrybutów dla strumienia wynikowego, można skorzystać z funkcji agregacyjnej Prezentowany język posiada następującą składnię okien czasowych: window.range(source, time) – realizuje funkcję okna zakresowego. window.fixed(source, time) – realizuje funkcję okna z stałymi interwałami czasu. window.infinite(source) – realizuje funkcję okna gromadzącego wszystkie krotki. Atrybuty: source – nazwa strumienia zasilającego np. $I time – stała wartość definiująca interwał czasu np. 10s Domyślną kolekcją krotek dla operatorów stanowych jest kolekcja czasowa. Jeżeli chcemy skorzystać z innej podajemy tą informację w frazie where. Alternatywą dla domyślnej kolekcji czasowej jest kolekcja czasowo-tablearyczna o składni: window.table(source) Atrybuty: source – nazwa atrybutu będącego kluczem głównym krotki; atrybut musi być typu numerycznego. Dzięki zastosowaniu drzewa atrybutów nazwa strumienia jest elementem atrybutu source np. $I.id. Operatory stanowe mogą być zdefiniowane na kilku strumieniach, zatem występuje wiele konfiguracji okien czasowych. Przeanalizujmy działanie operacji łączenia zdefiniowanej: $I.a == $II.a ze względu na konfigurację okien czasowych (tab. 2). Przyjmijmy że dane źródłowe mają wstępnie ustawiony czas życia na zero. Tab. 1. Konfiguracje okien dla operacji łączenia Sytuacja Okno dla I Okno dla II Wynik relacji 1 brak Brak kolekcje pozostają puste, brak krotek wynikowych 2 istnieje Brak krotka ze zbioru $II jest dopasowywana do kolekcji krotek z okna $I. Przy czym krotki $I nie są dopasowywane do $II 3 istnieje Istnieje Zarówno pojawienie się krotki na wyjściu strumienia $I jak i $II skutkuje rozpoczęciem operacji łączenia Sytuacja 1 to błąd użytkownika, który nie zdefiniował w pełni zapytania, przy czym nie ma ryzyka utworzenia kolekcji nieskończonej. Sytuację 2 ilustruje przykład: „Jesteśmy zainteresowani zbiorem (strumień $I) pojazdów z ostatnich 5 min, których szybkość przekroczyła zadaną wartość” (strumień $II). Rozbudowując powyższy przykład ilustrujemy sytuację 3: „Jesteśmy zainteresowani zbiorem (strumień $I) pojazdów z ostatnich 5min, których szybkość będzie przekraczała zadaną wartość (strumień $II) przez najbliższą godzinę”. 4.3 Przykładowe zapytanie Zaprezentowane poniżej zapytanie może służyć wskazaniu trzech najbliższych stacji serwisowych dla każdego z nadzorowanych samochodów. Przyjmijmy, że strumień I zawiera informacje o bieżącym położeniu przemieszczających się obiektów a strumień II przekazuje informacje o położeniu stacji serwisowych. from where window.table($service.id) AND util.knn([$car.point[T], k = 3], $service[T]) select sol[ name = $car.id, query = $car.point[T], service = $service.point[T]]; select out[$sol[T]] Widzimy tutaj zaletę stosowania drzewiastej struktury atrybutów. Jeżeli dobrze zaprojektuje się schematy strumieni w systemie StreamAPAS v2.0, to korzystanie z funkcji sprowadza się do wskazania węzłów struktury atrybutów strumienia, z których będą pobierane dane. Unikamy w ten sposób konieczności wielokrotnego podawania długiej listy atrybutów. Powyższy przykład korzysta również z tymczasowego drzewa atrybutów, które służy rozszerzeniu zbioru atrybutów strumienia zasilającego o atrybut zawierający liczbę poszukiwanych najbliższych sąsiadów. 5 Literatura 1. 2. 3. 4. 5. 6. 7. 8. 9. Marcin Gorawski, Skierski Andrzej : „Okienkowy język zapytań. II Krajowa Konferencja Naukowa Technologie Przetwarzania Danych”, 24-26 września 2007, Poznań, Polska. Magdalena Balazinska: „Fault-tolerance and load management in a Distributed Stream Processing System”, PhD dissertation, Massachusetts institute of technology, February 2006 Joürgen Krämer, Bernhard Seeger: „A Temporal Foundation for Continuous Queries over Data Streams”, Department of Mathematics and Computer Science PhilippsUniversity, Marburg, Germany, 11th International Conference on Management of Data (COMAD 2005) Arvind Arasu, Shivnath Babu, Jennifer Widom: „The CQL Continuous Query Language Semantic Foundations and Query Execution”, Stanford University, the International Journal on Very Large Databases (VLDB Journal) June 2006, 15:2 121-142 Songmei Yu, Vijayalashmi Atluri, Nabil Adam: „Optimizing View Materialization Cost in Spatial Data Warehouses”, Lecture Notes in Computer Science 4081 Springer 2006, 45-54 Luca Ardelli, Giorgio Ghelli: „TQL: A Query Language for Semistructured Data Based on the Ambient logic” MATHEMATICAL STRUCTURES IN COMPUTER SCIENCE 2004, VOL 14; PART 3, pages 285-328 Daniel Johannes, Pieter Leijen: „The λ Abroad – A Functional Approach To Software Components” Daan Leijen. PhD thesis, Department of Computer Science, Utrecht University, November 2003 Radosław Grabowski, Artur Wilczek: „Przetwarzanie strumieni danych z zastosowaniem relacyjnego systemu zarządzania bazą danych”, XIV Konferencja Systemy czasu rzeczywistego, 10-13.09.2007, Karpacz, Polska Peter A.Tucker: „Punctuated Data Streams” PhD dissertation, OGI School of Science & Technology At Oregon Health & Science University, August 2005