Come posso generare un bytea casuale


18

Vorrei essere in grado di generare byteacampi casuali di lunghezza arbitraria (<1 Gb) per popolare i dati dei test.

Qual è il modo migliore per fare questo?

Risposte:


20

Migliorando la risposta di Jack Douglas per evitare la necessità di loop PL / PgSQL e di concatenare bytea, puoi usare:

CREATE OR REPLACE FUNCTION random_bytea(bytea_length integer)
RETURNS bytea AS $body$
    SELECT decode(string_agg(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0') ,''), 'hex')
    FROM generate_series(1, $1);
$body$
LANGUAGE 'sql'
VOLATILE
SET search_path = 'pg_catalog';

È una SQLfunzione semplice che è più economica da chiamare rispetto a PL / PgSQL.

La differenza di prestazioni dovuta al metodo di aggregazione modificato è immensa per byteavalori più grandi . Sebbene la funzione originale sia in realtà fino a 3 volte più veloce per dimensioni <50 byte, questa scala è molto meglio per valori più grandi.

Oppure usa una funzione di estensione C :

Ho implementato un generatore bytea casuale come una semplice funzione di estensione C. È nel mio repository scrapcode su GitHub . Vedi il README lì.

Colpisce le prestazioni della versione SQL sopra:

regress=# \a
regress=# \o /dev/null
regress=# \timing on
regress=# select random_bytea(2000000);
Time: 895.972 ms
regress=# drop function random_bytea(integer);
regress=# create extension random_bytea;
regress=# select random_bytea(2000000);
Time: 24.126 ms

1
Bene, ho trovato quasi la stessa soluzione, ma ho testato solo per valori più bassi. La soluzione di @ Jack è stata un chiaro vincitore. +1 per te per non esserti fermato qui :)
dezso

Grazie - questo è eccellente e stimolante. Penso che FROM generate_series(0, $1);debba essere FROM generate_series(1, $1);. Hai provato la ricorsione? Il mio test limitato implica che questo ridimensiona meglio:
Jack Douglas

2
Ho provato un collegamento simbolico /dev/urandomin /var/lib/pgsql/datae la lettura con pg_read_file()per il bonus punti pazzeschi, ma purtroppo pg_read_file()recita textingresso tramite una conversione di codifica, quindi non in grado di leggere bytea. Se vuoi davvero la massima velocità, scrivi una Cfunzione di estensione che utilizza un generatore di numeri pseudo-casuale veloce per produrre dati binari e avvolgere un dato bytea attorno al buffer :-)
Craig Ringer

1
@JackDouglas Non ho potuto farne a meno. Versione estensione C di random_bytea. github.com/ringerc/scrapcode/tree/master/postgresql/…
Craig Ringer

1
Un'altra risposta eccellente! In realtà uno dei migliori che abbia mai visto finora. Non ho testato l'estensione, ma credo che funzioni come pubblicizzato.
Erwin Brandstetter,

5

Vorrei essere in grado di generare casualmente bytea campi di lunghezza arbitraria

Questa funzione lo farà, ma 1 Gb richiederà molto tempo perché non si ridimensiona linearmente con la lunghezza dell'uscita:

create function random_bytea(p_length in integer) returns bytea language plpgsql as $$
declare
  o bytea := '';
begin 
  for i in 1..p_length loop
    o := o||decode(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0'), 'hex');
  end loop;
  return o;
end;$$;

test di uscita:

select random_bytea(2);

/*
|random_bytea|
|:-----------|
|\xcf99      |
*/

select random_bytea(10);

/*
|random_bytea          |
|:---------------------|
|\x781b462c3158db229b3c|
*/

select length(random_bytea(100000))
     , clock_timestamp()-statement_timestamp() time_taken;

/*
|length|time_taken     |
|-----:|:--------------|
|100000|00:00:00.654008|
*/

dbfiddle qui


Bel uso di width_bucket. Maneggevole.
Craig Ringer,

1
Ho migliorato il tuo approccio per evitare il PL / PgSQL e il costoso ciclo di concatenazione; vedi nuova risposta. Usando string_agg su generate_series invece di un ciclo di concatenazione PL / PgSQL su bytea sto vedendo un miglioramento di 150 volte nelle prestazioni.
Craig Ringer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.