Partycje Tabel OpenEdge cz. III

Zanim przejdę do kolejnej części związanej z partycjami tabel, winien jestem odpowiedzi na pytanie związane z definiowaniem zasad partycjonowanie.
Otóż kilku z Was chciało wiedzieć czy jest planowana możliwość definiowania podziału danych w tabeli w oparciu nie o wartość pola, lecz o funkcję (np. YEAR(TODAY)-1).
Miałem okazję niedawno rozmawiać z Richem Banvill’em – odpowiedzialnym za rozwój bazy OpenEdge. Taka możliwość nie jest w planach, ani w wersji OE12 ani w dalszej przyszłości.
Rich wspomniał, że problem ten można rozwiązać np. poprzez zdefiniowanie zadań w cronie.

Wróćmy jednak do tematu. W poprzedniej części zakończyliśmy podział tabeli w oparciu o wartości pola Country (lokalny idex). Wspomniałem, że partycjonowanie jest transparentne dla aplikacji, jednakże mogą zdarzyć się sytuacje gdy zapytanie zwraca nie te same rekordy co przed partycjonowaniem. Jednym z przykładów jest stosowanie w kodzie wyszukiwania po adresach RECID/ROWID. Ponieważ partycjonowanie przenosi rekordy, ich fizyczne adresy ulegają zmianom. W przypadku stosowania tego rozwiązania trzeba wprowadzić odpowiednie poprawki w kodzie aplikacji.

Innym, częstszym przykładem jest stosowanie w zapytaniu filtra na dane pole (WHERE). Ponieważ w partycjonowanej tabeli dodany jest zazwyczaj nowy, lokalny index, wyszukiwanie może być realizowane w oparciu o niego. Aby mieć pewność, że index nie został zmieniony i aby zachować ten sam porządek rekordów trzeba ew. dopisać frazę USE-INDEX.

Teraz trzeba wspomnieć o ważnej funkcji technologi partycjonowania tzw. pruning (okrajanie partycji).

Proces pruningu analizuje automatycznie zapytanie i (o ile to możliwe) nie bierze pod uwagę rekordów w partycjach, które nie spełniają warunków zapytania. Na rysunku widać przykład podziału rekordów na kwartały wg pola OrderDate. Jeśli w zapytaniu warunki będą dotyczyły rekordów tylko z kwartału drugiego, to tylko ta jedna partycja będzie w użyciu. Technologia ta może znacząco wpłynąć na poprawę wydajności.

Następnym zagadnieniem jest stosowanie frazy TABLE-SCAN. Powoduje ona pobranie rekordów bez dostępu do indexów i wyświetlenie ich w porządku, w jakim znajdują się w blokach bazy. Porządek rekordów zmieni się więc po partycjonowaniu, ze względu na przeniesienie ich do innych bloków. Przed pobieraniem wykonywana jest operacja pruning. Zobaczmy poniższy przykład:

FOR EACH order WHERE country = “USA” AND
  OrderDate >= 10-01-2014 AND
  OrderDate <= 12-31-2014 TABLE-SCAN:
DISPLAY OrderNum...

Jak widać, partycjonowanie tabel może wpłynąć na porządek przetwarzanych rekordów. Zobaczmy teraz jak wygląda sytuacja z tworzenie nowych rekordów i ich edytowaniem.

Należy pamiętać, że rekordy muszą mieć określone wartości dla pól, wg których odbywa się partycjonowanie. Dlatego warto stosować instrukcję ASSIGN grupującą ustawienia wartości dla rekordu, zaraz po instrukcji CREATE. Instrukcja ASSIGN wymusza fizyczne utworzenie rekordu w bazie, a więc zadziałanie mechanizmu portycjonowania. Poniższy przykład wywoła błąd (partycjonowanie po polu Country).

CREATE order.
ASSIGN ordernum = NEXT-VALUE(next-ord-num). /* ERROR! */
ASSIGN country = “USA”. 

Przykład ten łatwo poprawić do postaci:

CREATE order.
ASSIGN ordernum = NEXT-VALUE(next-ord-num)
       country = “USA”.

Edycja pola rekordu, wg którego realizowany jest podział tabeli niesie ze sobą przeniesienie całego rekordu do innego obszaru, a więc skasowanie rekordu, utworzenie go w nowym obszarze, aktualizacja indexów. Zbyt częste takie operacje mogą nieść ze sobą obniżenie wydajności. Może to być wynikiem niewłaściwie wybranego klucza partycji, co warto przedyskutować i ew. wybrać inny klucz.

 

 

Partycje Tabel OpenEdge cz. II

W poprzedniej części zapoznaliśmy się nieco z częścią teoretyczną, dotyczącą partycjonowania tabel OpenEdge. Teraz czas zrobić coś w praktyce.

Tworzymy nową bazę np. mytp, kopię bazy sports2000 :

prodb mytp sports2000

Do partycjonowania tabel dane muszą znajdować się w obszarach Typ II. Demonstracyjna baza sports2000 posiada tylko obszary Typ I. Dodajemy zatem obszary, do których przeniesiemy tabele Customer (Cust_Data2) i Order (Order2), a także  obszary, w których będą przechowywane dane po procesie partycjonowania.

Najpierw tworzymy plik add_tp.st

d "Cust_Data2":20,64;8 . f 1280
d "Cust_Data2":20,64;8 .
#
d "Cust_Index2":25,32;8 . f 1280
d "Cust_Index2":25,32;8 .
#
d "Order2":30,64;8 . f 1280
d "Order2":30,64;8 .
#
d "CustomerData":300,64;8 ./customer f 1280
d "CustomerData":300,64;8 ./customer
d "CustomerIndex":301,32;8 ./customer f 320
d "CustomerIndex":301,32;8 ./customer
#
d "OrderData":400,64;8 ./order f 1280
d "OrderData":400,64;8 ./order 
d "OrderIndex":401,32;8 ./order f 320
d "OrderIndex":401,32;8 ./order
#

W katalogu z bazą tworzymy podkatalogi customer i order.
Teraz uruchamiamy polecenie:

prostrct add mytp add_tp.st

Przenosimy tabele Customer i Order do nowych obszarów Typ II:

proutil mytp -C tablemove customer Cust_Data2

proutil mytp -C tablemove order  Order2

Niestety, musimy także przenieść wszystkie indexy do obszarów Typ II (nie będę tego tutaj pokazywać, ale to też proste zadanie). Bazę demo można przygotować na różne sposoby, np. kasując niepotrzebne obiekty lub dump i load do nowej struktury.

Podobnie jak w przypadku CDC, dodamy konfigurację serwera bazy w OE Management (lub OE Explorer).  Po zalogowaniu się wybieramy Resources -> Database. Pojawia się widok Database Migration Utility, w którym podajemy parametry utworzonej bazy mytp wraz z numerem portu, np. 1009. Zaznaczamy Autostart database broker.

Na górnej listwie OE Management (OE Exlporer) klikamy Database Administration i na nazwę bazy: mytp. Pojawia się poniższy ekran.

W sekcji Database Features klikamy Table Partitioning Enable

… i jeszcze w Enable table partitioning. Funkcja jest już włączona w bazie.

W słowniku baz danych dodajemy dla tablicy Order index: OrderDateLocal, obszar: OrderIndex, pole: OrderDate. Analogicznie dla tablicy Customer index: CustomerCountryLocal, obszar: CustomerIndex, pole: Country.

Wracamy do OE Explorera. W sekcji Storage Management klikamy Create partition policy.

Pojawia się poniższy ekran, w którym definiujemy ustawienia dla naszej pierwszej partycji dla tablicy Order. Klikamy Create partition policy. Tworzymy partycję dla tablicy Customer.

Wypełniamy dane jak powyżej (w lookupach wyświetlają się tylko tablice w obszarach Typ II), zaznaczamy: Immediate – set new partitions to allocate space. Klikamy Next.

Będzie to partycja typu List, więc NIE zaznaczamy opcji Has range. Klikamy Add fields from index, wybieramy nowo utworzony index CustomerCountryLocal. Partycja podzieli dane wg pola Country.

Klikamy Next oraz Load Details. Otrzymujemy info, że znaleziono 9 podobszarów dla naszej partycji (jest to związane z wartościami pola Country). Klikamy Next.

Widzimy szczegóły Table Partition Policy. Klikamy Finish.

Zostaje utworzona nowa zasada o nazwie Customer. Dane w tablicy nie są jednak jeszcze podzielone.

Przygotowujemy dane do migracji. Klikamy w Edit Details wchodząc ponownie w szczegóły zasady partycjonowania. Klikamy podwójnie w każdy szczegół np. Austria i zaznaczamy Split-target. Tylko te zaznaczone elementy zostaną podzielone. Oznaczam Split-target wszystkie 9 elementów (czyli dla wszystkich krajów).


Wybieram Commit. Wszystko jest już przygotowane aby przenieść dane z partycji złożonej (Composite) do partycji podzielonych (Split-target).

W oknie komend proenv wpisujemy polecenie:

proutil mytp -C partitionmanage split table customer composite initial useindex

CustomerCountryLocal recs 100

Dane zostały podzielone przy użyciu zdefiniowanego indexu lokalnego. Ilość rekordów w transakcji: 100.

Możemy sprawdzić gdzie znajdują się teraz dane z tablicy Customer uruchamiając dobrze znane polecenie: proutil mytp -C tabanalys

Analiza dla obszaru CustomerData pokazuje podział tablicy na 9 partycji.

To na razie tyle w niniejszym odcinku, ale to nie koniec tematu partycjonowania.

Partycje Tabel OpenEdge cz. I

W ostatnim czasie kilka osób prosiło mnie o wyjaśnienie na czym polega partycjonowanie tabel w bazach OpenEdge. Ten temat czasem powraca w rozmowach z Wami, więc postanowiłem go nieco przybliżyć.

Partycjonowanie tabel (Table Partitioning) to oddzielny produkt dla baz Enterprise, umożliwiający dzielenie dużych tabel na mniejsze części pod względem logicznym zwane partycjami.

Partycje są zaimplementowane na poziomie bazy danych i przezroczyste dla aplikacji. Korzystanie z partycjonowanych tabel może wymagać niewielkich zmian w kodzie aplikacji lub nie wymagać ich wcale.

Uwaga: obiekty podzielone na partycje muszą znajdować się w storage area Typ II.

Partycjonowanie posiada kilka istotnych cech. Partycje w tabeli są niezależne, więc można realizować do nich jednoczesny dostęp, poprawiając wydajność zapytań. Jeśli jedna partycja w tabeli nie jest dostępna, pozostałe partycje są nadal dostępne dla aplikacji.


Każdy rekord tabeli podzielonej na partycje ma te same kolumny.
Każdy rekord może znajdować się tylko w jednej partycji.
Każda partycja może znajdować się we własnym obszarze przechowywania (storage area),
Każda partycja może być niezależnie modyfikowana i zarządzana bez wpływu na inne partycje tej samej tabeli.


Ponadto:
indeksy związane z tabelami podzielonymi na partycje można również podzielić na partycje (tzw. lokalne indeksy).
Podobnie jak partycje tabel, każdy lokalny indeks może znajdować się we własnym obszarze przechowywania i być niezależnie zarządzany. Oprócz indeksów lokalnych mogą istnieć także indeksy globalne dla wszystkich danych w tabeli, bez względu na wydzielone partycje.

Kiedy warto stosować tę technologię?

Np. dla tabel zawierających dane historyczne, które muszą być
archiwizowane. Ciekawą cechą partycji jest to, że jeśli jakaś partycja zawiera dane, które nie mogą być modyfikowane, może być ustawiona tylko do odczytu. W ten sposób w tabeli można modyfikować tylko dane aktualne, a nie zarchiwizowane.

Inne przykładowe przyczyny:

Tabele zawierające dane, które muszą być rozłożone na różnych urządzeniach pamięci masowej.
Duże tabele, które muszą podlegać okresowym operacjom na rekordach i indeksach.
Tabele zawierają kolumny, wg których można logicznie pogrupować dane.
Tabele zawierające dane z częstymi zapytaniami z frazą TABLE-SCAN, a nie WHOLE-INDEX.
Tabele, które będą rosły do bardzo dużych rozmiarów.

Partycjonowanie wykorzystuje jedną kolumnę (tzw. partition key) do jednoznacznej identyfikacji każdej partycji. Podział może być według zakresu (np. zakres dat) lub według listy (lista odrębnych wartości kolumn, np. kraje, województwa, itp.). Kolumna partition key nie może mieć wartości nieokreślonych (“?”).

Należy dodać, że dane w partycji można podzielić na dalsze partycje (możliwe jest 15 poziomów podziału). Np. dzielimy zamówienia (tablica order) według zakresu dat, a następnie każdą partycję dzielimy dodatkowo na subpartycje wg kraju.

 

Sposób w jaki tabele są podzielone jest opisane w tzw. zasadach partycjonowania (partition policy), znajdujących się w meta-schemacie bazy.

W następnym odcinku pokażę jak w praktyczny sposób stworzyć partycje i przenieść do nich dane.