

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Costruire per l'efficienza con le funzioni
<a name="limitless-performance-functions"></a>

User-defined le funzioni non sono ottimizzate a shard singolo per impostazione predefinita, ma possono essere configurate per essere eseguite come operazioni a shard singolo. Le funzioni possono incapsulare la logica e garantire che venga eseguita in modo ottimizzato per un singolo shard.

## Perché le operazioni a shard singolo sono importanti
<a name="limitless-functions-importance"></a>

L'utilizzo delle risorse è importante per le prestazioni e l'efficienza dei costi. Single-shard le operazioni utilizzano un numero significativamente inferiore di risorse rispetto alle operazioni cross-shard. Ad esempio, quando si esegue una funzione per inserire un milione di righe, l'esecuzione a shard singolo utilizza circa 90,5 ACU rispetto alle 126,5 ACU per l'esecuzione su più shard, con un miglioramento del 35% dell'efficienza delle risorse.

Single-shard l'esecuzione fornisce inoltre:
+ throughput superiore del 35% rispetto alle operazioni cross-shard
+ Tempi di risposta più prevedibili
+ Migliore scalabilità man mano che i dati crescono

## Single-shard operazioni e funzioni
<a name="limitless-functions-sso"></a>

Le funzioni vengono eseguite sugli shard quando viene soddisfatto uno di questi prerequisiti:
+ La funzione viene creata come immutabile e inclusa in una query ottimizzata a shard singolo
+ La funzione è distribuita da un utente

Le funzioni eseguite sugli shard offrono prestazioni e scalabilità migliori perché vengono eseguite dove si trovano i dati.

## Funzioni e volatilità
<a name="limitless-functions-volatility"></a>

Per verificare la volatilità di una funzione, usa questa query sulle tabelle di sistema di PostgreSQL:

```
SELECT DISTINCT nspname, proname, provolatile 
FROM pg_proc PRO 
JOIN pg_namespace NSP ON PRO.pronamespace = NSP.oid 
WHERE proname IN ('random', 'md5');
```

Output di esempio:

```
  nspname   | proname | provolatile 
------------+---------+-------------
 pg_catalog | md5     | i
 pg_catalog | random  | v
(2 rows)
```

In questo esempio, `md5()` è immutabile ed è volatile. `random()` Ciò significa che un'istruzione ottimizzata per shard singolo che include `md5()` rimane ottimizzata per shard singolo, mentre un'istruzione che include no. `random()`

Esempio con funzione immutabile:

```
EXPLAIN ANALYZE 
SELECT pg_catalog.md5('123') 
FROM s1.t1 
WHERE col_a = 776586194 
  AND col_b = 654849524 
  AND col_c = '3ac2f2affb02987159ccd6ebd23e1ae5';
```

```
                          QUERY PLAN 
----------------------------------------------------
 Foreign Scan  (cost=100.00..101.00 rows=100 width=0) 
               (actual time=3.409..3.409 rows=1 loops=1)
 Single Shard Optimized
 Planning Time: 0.313 ms
 Execution Time: 4.253 ms
(4 rows)
```

Esempio con funzione volatile:

```
EXPLAIN ANALYZE 
SELECT pg_catalog.random() 
FROM s1.t1 
WHERE col_a = 776586194 
  AND col_b = 654849524 
  AND col_c = '3ac2f2affb02987159ccd6ebd23e1ae5';
```

```
                          QUERY PLAN 
------------------------------------------------------
 Foreign Scan on t1_fs00001 t1  
   (cost=100.00..15905.15 rows=1 width=8) 
   (actual time=0.658..0.658 rows=1 loops=1)
 Planning Time: 0.263 ms
 Execution Time: 2.892 ms
(3 rows)
```

L'output mostra che `md5()` viene premuto ed eseguito come ottimizzato per shard singolo, mentre non lo è. `random()`

## Funzioni di distribuzione
<a name="limitless-functions-distributing"></a>

Una funzione che accede ai dati su un solo shard deve essere eseguita su quello shard per ottenere vantaggi in termini di prestazioni. La funzione deve essere distribuita e la firma della funzione deve includere la chiave shard completa: tutte le colonne della chiave shard devono essere passate come parametri alla funzione.

Funzione di esempio:

```
CREATE OR REPLACE FUNCTION s1.func1(
    param_a bigint, 
    param_b bigint, 
    param_c char(100)
) 
RETURNS int AS $$
DECLARE 
    res int;
BEGIN
    SELECT COUNT(*) INTO res
    FROM s1.t1
    WHERE s1.t1.col_a = param_a
      AND s1.t1.col_b = param_b
      AND s1.t1.col_c = param_c;
    
    RETURN res;
END
$$ LANGUAGE plpgsql;
```

Prima della distribuzione, la funzione non è ottimizzata per shard singolo:

```
EXPLAIN ANALYZE 
SELECT * FROM s1.func1(776586194, 654849524, '3ac2f2affb02987159ccd6ebd23e1ae5');
```

```
                                              QUERY PLAN 
------------------------------------------------------------------------------------------------------
 Function Scan on func1  (cost=0.25..0.26 rows=1 width=4) 
                         (actual time=37.503..37.503 rows=1 loops=1)
 Planning Time: 0.901 ms
 Execution Time: 51.647 ms
(3 rows)
```

Per distribuire la funzione:

```
SELECT rds_aurora.limitless_distribute_function(
    's1.func1(bigint,bigint,character)', 
    ARRAY['param_a','param_b','param_c'], 
    's1.t1'
);
```

Dopo la distribuzione, la funzione è ottimizzata per shard singolo:

```
EXPLAIN ANALYZE 
SELECT * FROM s1.func1(776586194, 654849524, '3ac2f2affb02987159ccd6ebd23e1ae5');
```

```
                                           QUERY PLAN 
------------------------------------------------------------------------------------------------
 Foreign Scan  (cost=100.00..101.00 rows=100 width=0) 
               (actual time=4.332..4.333 rows=1 loops=1)
 Single Shard Optimized
 Planning Time: 0.857 ms
 Execution Time: 5.116 ms
(4 rows)
```

Puoi confermare l'ottimizzazione a shard singolo controllando la colonna in: `sso_calls` `rds_aurora.limitless_stat_statements`

```
subcluster_id | subcluster_type | calls | sso_calls |                query 
--------------+-----------------+-------+-----------+--------------------------------------
 2            | router          |     2 |         1 | SELECT * FROM s1.func1( $1, $2, $3 )
 3            | router          |     1 |         1 | SELECT * FROM s1.func1( $1, $2, $3 )
(2 rows)
```

## Funzioni e modelli di efficienza
<a name="limitless-functions-efficiency-patterns"></a>

L'esecuzione della logica in prossimità dei dati è più efficiente e le funzioni svolgono un ruolo chiave a tal fine. Esistono due casi d'uso principali per migliorare l'efficienza delle funzioni:

1. Estrazione della chiave shard da dati complessi per richiamare una funzione ottimizzata a shard singolo separata

1. Trasformazione dei carichi di lavoro cross-shard in single-shard ottimizzati separando la logica cross-shard dalle istruzioni ottimizzate a shard singolo

### Estrazione della chiave shard da dati complessi
<a name="limitless-functions-encapsulated-key"></a>

Consideriamo una funzione con firma `s3.func3(p_json_doc json)` che esegue diverse operazioni sul database. Queste operazioni verranno eseguite su tutti gli shard all'interno di una transazione che si estende su tutti gli shard. Se il documento JSON contiene la chiave shard, puoi creare una funzione ottimizzata per shard singolo per eseguire le operazioni del database.

Modello originale:

```
s3.func3(p_json_doc json)
    database operation 1;
    database operation 2;
    database operation 3;
```

Modello ottimizzato:

```
s3.func3(p_json_doc json)
DECLARE 
    v_a bigint;
BEGIN
    v_a := (p_json_doc->>'field_a')::bigint;
    SELECT s3.func3_INNER(v_a, p_json_doc);
END;
```

Dove funziona la funzione interna:

```
s3.func3_INNER(p_a, p_json_doc)
    database operation 1 WHERE shard_key = p_a;
    database operation 2 WHERE shard_key = p_a;
    database operation 3 WHERE shard_key = p_a;
```

In questo modello, la chiave shard è incapsulata in un tipo di dati complesso o deducibile da altri parametri. La logica, l'accesso ai dati e le funzioni possono determinare, estrarre o costruire la chiave shard, quindi richiamare una funzione ottimizzata per shard singolo che esegue operazioni su un solo shard. Poiché l'interfaccia dell'applicazione non cambia, l'ottimizzazione è relativamente facile da testare.

### Rimandare la chiave shard da altre funzioni o dati
<a name="limitless-functions-deferred-key"></a>

Un altro modello di progettazione si applica quando la logica o l'accesso ai dati calcola o determina la chiave shard. Ciò è utile quando una funzione può essere eseguita su un singolo shard per la maggior parte delle chiamate, ma occasionalmente richiede l'esecuzione tra shard.

Modello originale:

```
NEWORD(INTEGER, …) RETURNS NUMERIC
DECLARE
    all_whid_local := true;
    LOOP through the order lines
        Generate warehouse ID;
        IF generated warehouse ID == input warehouse ID
        THEN
            ol_supply_whid := input warehouse ID;
        ELSE
            all_whid_local := false;
            ol_supply_whid := generated warehouse ID;
        END IF;
        …
    END LOOP;
    …
    RETURN no_s_quantity;
```

Modello ottimizzato con funzioni separate:

```
CREATE OR REPLACE FUNCTION NEWORD_sso(no_w_id INTEGER, …)
RETURNS NUMERIC
…
    RETURN no_s_quantity;
    …
END;
LANGUAGE 'plpgsql';

SELECT rds_aurora.limitless_distribute_function(
    'NEWORD_sso(int,…)', 
    ARRAY['no_w_id'], 
    'warehouse'
);

CREATE OR REPLACE FUNCTION NEWORD_crosshard(no_w_id INTEGER, …)
RETURNS NUMERIC
…
    RETURN no_s_quantity;
    …
END;
LANGUAGE 'plpgsql';
```

Quindi fai in modo che la funzione principale chiami la versione ottimizzata per shard singolo o per shard incrociato:

```
IF all_whid_local THEN
    SELECT NEWORD_sso(…) INTO no_s_quantity;
ELSE
    SELECT NEWORD_crosshard(…) INTO no_s_quantity;
END IF;
```

Questo approccio consente alla maggior parte delle chiamate di trarre vantaggio dall'ottimizzazione a shard singolo mantenendo al contempo il comportamento corretto nei casi che richiedono l'esecuzione su shard incrociati.

## Verifica delle operazioni a shard singolo
<a name="limitless-functions-checking-sso"></a>

Utilizzato `EXPLAIN` per verificare se un'istruzione è ottimizzata per shard singolo. L'output riporta esplicitamente «Single Shard Optimized» per operazioni ottimizzate.

Cross-shard invocazione prima della distribuzione:

```
                       QUERY PLAN 
---------------------------------------------------------------------
 Function Scan on func1  (cost=0.25..0.26 rows=1 width=4) 
                         (actual time=59.622..59.623 rows=1 loops=1)
 Planning Time: 0.925 ms
 Execution Time: 60.211 ms
```

Single-shard invocazione dopo la distribuzione:

```
                       QUERY PLAN 
----------------------------------------------------------------------
 Foreign Scan  (cost=100.00..101.00 rows=100 width=0) 
               (actual time=4.576..4.577 rows=1 loops=1)
 Single Shard Optimized
 Planning Time: 1.483 ms
 Execution Time: 5.404 ms
```

La differenza nei tempi di esecuzione dimostra il vantaggio prestazionale dell'ottimizzazione a shard singolo.