Zum Hauptinhalt springen

Exasol ist ein führender Hersteller von analytischen Datenbanksystemen. Das Kernprodukt ist eine auf In-Memory-Technologie basierende Software für die professionelle, parallele und schnelle Datenanalyse. Normalerweise werden SQL-Statements in einem SQL-Skript sequenziell abgearbeitet. Wie können aber mehrere Statements gleichzeitig ausgeführt werden? Dies zeigen wir anhand eines einfachen Skripts in diesem Blogbeitrag.

Sinn und Unsinn der Parallelisierung von Anweisungen

Exasol ist bekannt für seine massiv parallelen Verarbeitungsmöglichkeiten. Abfragen werden über alle Knoten in einem Cluster verteilt, wobei man optimierte, parallele Algorithmen verwendet, die Daten lokal im Hauptspeicher jedes Knotens verarbeiten.

ETL-Prozesse hängen oft von LUA-Skripten oder benutzerdefinierten Funktionen (UDFs) ab. Innerhalb dieser Skripte werden SQL-Anweisungen ausgeführt, um die verschiedenen Aufgaben des ETL-Prozesses zu erfüllen. Beispiele hierfür sind das Erstellen von Tabellen oder das Importieren von Daten. Diese Schritte müssen meistens sequenziell ablaufen, damit beispielsweise Tabellen erst beladen werden, nachdem diese erstellt wurden.

Beim Beladen von Tabellen oder beim Export von Daten kann es aber sein, dass gewisse Schritte parallel ausgeführt werden können. Es ist jedoch so, dass bei der Verwendung von LUA-Skripten in einem ETL-Prozess die Ausführung der Anweisungen innerhalb des Skripts nicht parallelisiert wird. Das bedeutet, dass alle Anweisungen in einer serialisierten Reihenfolge ausgeführt werden, was zu einer längeren Ausführungszeit und einer Vernachlässigung der potenziellen Rechenleistung von Exasol führt.

Welche Voraussetzungen müssen erfüllt werden?

Um mehrere Befehle parallel auszuführen, wird ein Datenbankcluster mit mehreren Knoten benötigt. Daher ist für die Implementierung der Lösung eine Enterprise-Version von Exasol mit mehreren Nodes zwingend notwendig. Außerdem ist die Lösung nur für Anweisungen geeignet, bei denen keine bestimmte Ausführungsreihenfolge erforderlich ist, da sie in einer zufälligen Reihenfolge ausgeführt werden.

Wie können mehrere Befehle parallel ausgeführt werden?

Um die Einschränkung der serialisierten Abfrageausführung innerhalb von LUA-Skripten zu umgehen, ist ein zweistufiger Ansatz erforderlich. Im ersten Schritt werden alle auszuführenden Anweisungen in einer dafür vorgesehenen Tabelle ausgeschrieben. Diese kann im Vorfeld oder während der Skriptausführung erstellt werden. In einem zweiten Schritt wird ein Python-Skript aufgerufen, das sich mit der Datenbank verbindet und die erhaltenen Anweisungen ausführt. Dieses Python-Skript ist vom Typ set/emits, was ermöglicht, die Anweisungen auf die verschiedenen Knoten zu verteilen und somit parallel auszuführen.

Beispiel-Code

Der folgende Code veranschaulicht die Lösung anhand eines Beispiels. In diesem sollen alle Views des Schemas EXA_STATISTICS in separate CSV Files exportiert werden.

Erstellen einer Verbindung zu Exasol

Zunächst wird eine Verbindung zur Exasol-Datenbank hergestellt. Hier ist es sinnvoll, den vollen IP-Bereich aller Knoten anzusprechen, im untenstehenden Code dargestellt mit 11..14 für die 4 Knoten im Cluster.

CREATE OR REPLACE CONNECTION exa_connection
                TO '192.168.6.11..14:8563'
                USER 'tech_user' 
                IDENTIFIED BY 'secret';

 

Implementieren des Python-Skripts

Im zweiten Schritt wird das Python-Skript für die parallele Ausführung von Anweisungen implementiert. Um die Verbindung herzustellen, müssen wir das Paket pyExasol verwenden. Es wird benötigt, um mit der vorab definierten Verbindung einen Kontakt mit dem Cluster aufzunehmen. Hierzu werden DSN, Nutzername und Passwort übergeben. Weiterhin wird ein Client-Name angegeben. Dieser kann im Anschluss als Filter auf die ausgeführten Anweisungen genutzt werden, um die korrekte Ausführung zu überprüfen. Das Skript gibt für jedes ausgeführte Statement zwei Werte zurück: Im Erfolgsfall liefert es "Success" und die Anzahl der betroffenen Zeilen, im Falle eines Fehlers "Failed" und die Anweisung, die nicht ausgeführt wurde. Dieses Skript kann in verschiedenen LUA-Skripten angesprochen werden, um Statements parallel ausführen zu lassen.

--/
CREATE OR REPLACE PYTHON SET SCRIPT SCRIPTS.PYT_PARALLEL_EXECUTION(stmt VARCHAR(20000)) 
EMITS(succes_state VARCHAR(10), outputs VARCHAR(20000)) AS 
import pyExasol

# Create Exasol connection
con = exa.get_connection("exa_connection") 
C = pyExasol.connect(dsn=con.address, user=con.user, password=con.password, autocommit=True, encryption=True, client_name="PARALLEL_EXECUTION")

def run(ctx):

 while True:    
  stmt = ctx.stmt
  try:
   retrun_stmt = C.execute(stmt)   
   ctx.emit("Success", str(retrun_stmt.rowcount()))
   C.commit()
  except:
   ctx.emit("Failed", stmt)
   C.rollback()
  if not ctx.next(): break
 C.close()
/

 

Implementieren des LUA-Skripts

Als Letztes wird das LUA-Skript implementiert. Hier erstellen wir zunächst eine Tabelle, die wir dann mit Anweisungen füllen, um die EXA_STATISTICS-Tabellen zu exportieren. Im Anschluss können wir das Python-Skript aufrufen, um diese Anweisungen auszuführen. Schlussendlich wird die erstellte Tabelle mit den Export-Statements gelöscht. Dabei sollte beachtet werden, dass das Python-Skript eine neue Session öffnet und vor dem LUA-Skript beendet wird. Entsprechend müssen im LUA-Skript COMMITS gesetzt werden, um die durch das Python-Skript vorgenommenen Änderungen nicht zu überschreiben.

CREATE OR REPLACE LUA SCRIPT SCRIPTS.LUA_PARALLEL_EXECUTION () AS
require "string"
nClock = os.clock()
-- Example case: export all stats tables    

    -- create tmp table 
	local suc, res = pquery([[CREATE OR REPLACE TABLE TABLES.TEMP_TABLE (i VARCHAR(20000))]])
	if suc == true then
		output("Temp table created")
		output("Execution time" .. os.clock()-nClock)
	elseif suc == false then
		output("ERROR: It was not possible to create the temp table")
		output("Script stopped")
		output("Execution time" .. os.clock()-nClock)
		exit()
	end
    -- Generate export statements
	-- Get Table names
   	local suc, res = pquery([[SELECT OBJECT_NAME FROM EXA_SYSCAT WHERE SCHEMA_NAME = 'EXA_STATISTICS']])
	
	-- Fill tmp table with statements for parallel execution	
	for i=1, #res do
		exp_stmt = "EXPORT"..res[i].."INTO LOCAL CSV FILE '/tmp/"..res[i]..".csv'"
		local suc, res1 = pquery([[INSERT INTO TABLES.TEMP_TABLE VALUES(:s)]], {s=exp_stmt})
		if suc == true then
	        suc_sum = suc_sum + 1	        
	    elseif suc == false then
	        output("WARNING: It was not possible to create the following import statement: ")
	        output(exp_stmt)
	    end
	end
    output(suc_sum.." Insert statements created and saved")


    -- Execute python script to parallelize import
    res = query([[SELECT SCRIPTS.PYT_PARALLEL_EXECUTION(i) FROM TABLES.TEMP_TABLE GROUP BY Iproc()]])
    
    -- Return total number of exported rows
    total = 0
    for i=1, #res do
            local stmt_return = res[i][1]
            if stmt_return == "Failed" then
                    output("WARNING: It was not possible to execute the following statement")
                    output(res[i][2])
                    output(stmt_return)
            elseif stmt_return == "Success" then
            	total = total + stmt_return
            end		
    end

   -- drop temp Table
	local suc, res = pquery([[DROP TABLE TABLES.TEMP_TABLE]])
	if suc == true then
		output("Temp table dropped ")
		output("Execution time" .. os.clock()-nClock)
	elseif suc == false then
		output("ERROR: Temp table could not be dropped")
	end   
	

	output("Number of exported row " .. total)
	output("Script finished successfully")
	output("Execution time" .. os.clock()-nClock)

 

Der Funktion werden die Statements i übergeben, die sich in der temporären Tabelle TEMP_TABLE befinden. Mit der Anweisung GROUP BY Iproc() werden die Statements auf die Anzahl Knoten verteilt, auf jedem Knoten wird die Python-Funktion gestartet, und somit werden die Statements schlussendlich parallel ausgeführt.

Geschafft! … und nun?

Nun, da alles eingerichtet ist, können wir das LUA-Skript ausführen und die Daten parallel exportieren.

EXECUTE SCRIPT SCRIPTS.LUA_PARALLEL_EXECUTION();

Mit der Parallelisierung von SQL-Anweisungen innerhalb von LUA-Skripten konnten wir in unserem Tagesgeschäft die Laufzeit von Import- und Export-Anweisungen um 90 % reduzieren. Das ist eine deutliche Steigerung im Vergleich zur Ausführung der SQLs innerhalb von LUA-Anweisungen im serialisierten Modus.

Dieses Beispiel soll verdeutlichen, wie man SQL-Anweisungen innerhalb von LUA-Skripten parallel ausführen kann. Natürlich kann die Vorlage an viele verschiedene Anwendungsfälle angepasst werden und ist nicht nur an den Export von Daten gebunden.

 

 

 

Wenn Du mehr darüber erfahren möchtest, wie Du das volle Potenzial von Exasol ausschöpfen kannst, oder Hilfe bei der Implementierung des Skripts benötigst, dann nimm gerne Kontakt mit uns auf!

Jetzt mehr erfahren!