Teradata choosing primary index

Aus BI-Snippets - Business Intelligence Code und Module
Wechseln zu: Navigation, Suche

Teradata ist ein massiv parallel arbeitendes, relationales Datenbankmanagementsystem, welches auf einer shared nothing Architektur basiert. Daher verrichtet jeder am DBMS teilnehmende Knoten seine Arbeit eigenständig und völlig unabhängig von allen anderen beteiligten Knoten des DBMS. Das gesamte Cluster wird von einer zentralen Stelle lediglich verwaltet. Diese übernimmt die Aufgabe der Verteilung von Abfragen und Daten.

Ein besonderes Merkmal solcher "shared nothing" Systeme und hier im speziellen der Teradata, ist die gleichmäßige oder auch ungleichmäßige Verteilung von Daten über die verfügbaren Knoten. Das DBMS muss wissen wie es eine Tabelle auf den beteiligten Knoten verteilen soll. Im Falle der Teradata wird hierzu immer der Primary Index (PI) einer Tabelle verwendet. Dies hat zur Folge, dass alle identischen PI auf dem selben Knoten gespeichert werden. ist nun der PI gleich dem Primären Schlüssel (PK) der selben Tabelle, so ist dieser eh eindeutig und das Problem existiert nicht. Wird der PI aber aus einem Grund anders gewählt, können mehrfache identische Ausprägungen auf dem PI vorkommen. Diese werden dann "schief" auf der Teradata verteilt.

Szenario

Folgendes Rechenbeispiel veranschaulicht das Szenario:

Input:

  • tablename: foobar
  • 1 Tabelle mit 1000 Zeilen
  • Attribute: {a,b,x,y,z}
  • PI: {a,b}

Der PI {a,b} hat folgende identische Ausprägungen:

  • 100 * {'foo',100}
  • 100 * {'bar',100}
  • 800 * {'jedi',200}

Nehmen wir an die Teradata Installation besteht aus 3 Arbeitern (Worker Nodes), so ergibt sich folgende Verteilung auf den Knoten bei gegebenem PI:

NODE TABLE ROWS_PER_NODE
node_1 foobar 100
node_2 foobar 100
node_3 foobar 800

Auswirkungen

Die gegebene Verteilung hat folgende Auswirkungen zur Folge:

  • reduzierter Speicherplatz der Datenbank. Wenn ein Node mehr Bytes enthält, als es laut DBMS halten darf, so ist die gesamte Datenbank voll. in gegebenem Beispiel speichert Node 3 80% der Daten.  Angenommen 700 Zeilen wäre das maximum pro Node, dann hätten Node 1 und 2 noch viel Platz, aber Node 3 gibt die Meldung "no more space" zurück. Dies führt, dazu dass die Datenbank insgesamt als voll eingestuft wird und keine weiteren Daten mehr auf nehmen kann.
  • langsame Schreibzugriffe. Ein Node muss weit aus mehr Arbeit verrichten,als die anderen beiden Nodes. Jede Schreiboperation die auf Node 3 zu schreiben versucht, dauert daher überproportional länger, als die selben Operationen auf den anderen beiden Nodes.
  • Der selbe Effekt gilt für lesende Zugriffe. 80% der Arbeit muss auch hier Node 3 verrichte, während Node 1 und 2 schon lange fertig sind.

Wahl des Primärindex

Die Wahl des besten Primären Indizes hängt aber nicht nur von der Verteilung auf der Datenbank ab, sondern auch davon, wie die Tabelle später abgefragt wird. Beziehungsweise, wie auf die Tabelle geschrieben wird.

Bei der Anlage einer Tabelle kennt man nun entweder sein Datenmodell und kann auf Basis dieses Wissens eine fundierte Entscheidung treffen oder ist sich über die Gleichverteilung bei der gegebenen Zielstruktur noch nicht sicher. In jedem Fall hilft es zu analysieren, welche Verteilung eine gegebene Tabelle haben wird oder wie die Verteilung aktuell ist. Welches Szenario auch immer relevant ist, eine wichtige Funktion zur Bestimmung der Verteilung sind die Hashing Algorithmen auf der Teradata. Auf Basis der drei Funktionen HASHAMP(), HASHROW() und HASHBUCKET() werden die Daten auf den Nodes verteilt und mit den selben Funktionen lässt sich ermitteln, wieviele Zeilen einer Tabelle aktuell pro Node gespeichert werden. Folgende Abfrage liefert daher die Verteilung einer Tabelle auf allen Nodes:

SELECT HASHAMP( HASHBUCKET( HASHROW( columnslist ))) as AmpNo
, COUNT(*) AS RowCnt
FROM dbs.tablename
GROUP BY 1
ORDER BY 2 DESC
  • HASHROW( column ): gibt den Hashwert der gegebenen column wieder, mit der diese abgespeichert wurde.
  • HASHBUCKET( hashrow_value ): gibt den Bucket pro Node wieder
  • HASHAMP( hashbucket_value ): gibt den konrekten Node zurück, auf dem der Bucket liegt.

Verschachtetlt ergeben die drei Funktionen so die Nodes und die Menge der dort enthaltenen Zeilen zurück. Führt man diese Abfrage auf einem bestehenden PI durch, sieht man ob ein großer Unterschied zwischen dem größten und dem kleinsten Node besteht. Ebenfalls lässt sich mit selbiger Abfrage abschätzen, wie ein anderer PI die Gleichverteilung beeinflussen würde.

SELECT
    MIN(a.row_cnt) as min_row_cnt
   ,MAX(a.row_cnt) as max_row_cnt
   ,100 - ( AVG(a.row_cnt) / max(a.row_cnt) * 100 ) as skew_factor
FROM
    (select hashamp(hashbucket(hashrow(<column list>))) as amp_no
     ,count(*) as row_cnt
     from db.tablename
    )a

Während die erste Abfrage nur die totalen Werte liefert, hilft die 2. Abfrage dabei den sogenannten "SkewFactor" zu bestimmen. Der Skew definiert die schiefe einer Tabelle, daher die Differenz zwischen minimalem und maximalem Rowcount pro Node einer Tabelle.


Gleichverteilung erzwingen

Es gibt Situationen, in denen lässt sich aus den vorhandenen Feldern kein guter Primärindex bilden. Möglicherweise wurden zu viele der in Frage kommenden Felder komprimiert und stehen daher nicht mehr für einen Index zur Verfügung. Um in solch einem Fall einem ungünstigen Skew vorzubeugen kann man einfach ein automatisch generiertes Feld als Primärindex wählen:

CREATE SET TABLE ...
(
gkey INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY
           (START WITH -2147483647 
            INCREMENT BY 1 
            MINVALUE -2147483647 
            MAXVALUE 2147483647 
            CYCLE)
<weitere Spalten>
PRIMARY INDEX ( gkey );

Hier erhält das Feld gkey für jeden neuen Datensatz einen neuen Wert, wodurch diese nach der Reihe verteilt werden.