Parallel uitvoeren van tabelfuncties in SQL met de JOIN_PARALLELIZATION hint

Go to English version

Tabelfuncties

In veel queries worden tabelfuncties gebruikt. Dit is meestal het geval bij platformen die werken met zogenaamde “puntqueries” en het uitvoeren van talrijke I/O’s zoals:

select *  
from   customers ctr
join   httpdownload(ctr.website_url) htp

Een tabelfunctie wordt vaak duizenden of miljoenen keren aangeroepen bij grote datasets. De uitvoering vindt altijd achtereenvolgens plaats:

  • Haal een handvol rijen op uit de basistabel zoals customers hierboven.
  • Voer voor de eerste rij de tabelfunctie uit.
  • Wacht tot die klaar is.
  • Voer voor de tweede rij de tabelfunctie uit.
  • Wacht tot die klaar is.
  • … etc.

Veel tabelfuncties roepen een API of website aan, waarbij er vertraging kan optreden van enkele milliseconden tot honderden milliseconden per aanroep.

Bijvoorbeeld voor het verwerken van de gegevens achter https://zoek.officielebekendmakingen.nl zijn in bepaalde use cases 6 tot 12 miljoen aanroepen nodig.

Snellere verwerking

Met de nieuwe join_parallelization hint krijgt de Invantive UniversalSQL-engine om - waar mogelijk - tabelfuncties parallel aan te roepen met meerdere threads:

  • Haal een handvol rijen op uit de basistabel zoals customers hierboven.
  • Start de uitvoering voor de eerste rij met de tabelfunctie.
  • Start de uitvoering voor de tweede rij met de tabelfunctie.
  • … etc.
  • Wacht tot er een klaar is en start weer een nieuwe.
  • … etc.

In de meest optimale situatie wordt de doorlooptijd lineair korter: bij parallellisatie over n keer zoveel threads, is de totale verstreken kloktijd n keer korter.

Als het aantal threads niet opgegeven wordt, dan wordt 2x het aantal processoren gebruikt. Het maximaal aantal actieve threads wordt altijd op 128 afgekapt per query.

Een lineaire snelheidssprong zal niet altijd gehaald kunnen worden, maar zeker bij I/O-intensieve bewerkingen is een grote versnelling mogelijk.

Een grote snelheidswinst is niet te verwachten bij berekeningen die al beperkt worden doordat ze al de beschikbare rekenkracht en andere resources optimaal gebruiken.

Parallellisatie is sowieso pas aan te raden na het uitproberen van een slimmere aanpak via tuning.

Aan de hand van twee I/O-intensieve voorbeelden worden de mogelijkheden geillustreerd.

Teamleader

Het ophalen van de volgende query kan parallel plaatsvinden:

select count(*)
from   Teamleader.V2.Companies l
join   Teamleader.V2.CompanyById(l.id) p

Het aantal threads (in dit voorbeeld 8) kan ingesteld worden met de hint join_parallelization:

select /*+ join_parallelization(dl, true, 8) */count(*)
from   Teamleader.V2.Companies l
join   Teamleader.V2.CompanyById(l.id) p

Als er 100 bedrijven geregistreerd zijn in Teamleader Focus, dan passen alle aanroepen van CompanyById binnen de rate limiter van 200 API-calls per minuut. Er zullen uiteraard andere vertragende factoren zijn zoals een snelheidsbeperking binnen de database van Teamleader Focus zelf of wachtrijen met beperkte capaciteit.

Metingen tonen aan dat de doorvoersnelheid van initieel 4 rijen per seconde via parallellisatie stijgen als volgt:

#Threads Doorvoersnelheid (rijen/sec)
1 4
2 6
4 9
8 13
16 16
32 17
64 18
128 19

De snelheidswinst is groot tot pakweg 8 a 16 threads, en een hogere snelheidssprong dan pakweg 5 lijkt niet haalbaar op deze manier.

Binaire download

De volgende query haalt 1000x een bestand van 1 MB op via een Intern-verbinding met maximaal 8 threads:

select /*+ join_parallelization(dl, true, 8) */ r.value rval 
from   range(1000, 1) r 
join   httpdownload
       ( url => 'https://ipv4.download.thinkbroadband.com/1MB.zip'
       , ignoreInvalidCertificate => true
       , ignoreWebError => true
       ) dl

De snelheidswinst is sterk afhankelijk van de beschikbare bandbreedte. Met 8 threads is de query 3x sneller klaar en met 128 threads is hij 28x sneller.

Beschikbaarheid

De join_parallelization hint is beschikbaar vanaf BETA-release 24.1.122, die naar verwachting voor 15 juli 2025 beschikbaar zal komen.