Comment puis-je générer un bytea aléatoire

18

J'aimerais pouvoir générer des byteachamps aléatoires de longueur arbitraire (<1 Go) pour remplir les données de test.

Quelle est la meilleure façon de procéder?

Jack Douglas
la source

Réponses:

20

En améliorant la réponse de Jack Douglas pour éviter le besoin de bouclage PL / PgSQL et de concaténation de bytea, vous pouvez utiliser:

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';

C'est une SQLfonction simple qui coûte moins cher à appeler que PL / PgSQL.

La différence de performances due à la méthode d'agrégation modifiée est immense pour les byteavaleurs plus importantes . Bien que la fonction d'origine soit en réalité jusqu'à 3 fois plus rapide pour les tailles <50 octets, celle-ci évolue beaucoup mieux pour les valeurs plus grandes.

Ou utilisez une fonction d'extension C :

J'ai implémenté un générateur de bytea aléatoire comme une simple fonction d'extension C. C'est dans mon dépôt de scrapcode sur GitHub . Voir le README là-bas.

Il annule les performances de la version SQL ci-dessus:

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
Craig Ringer
la source
1
Eh bien, j'ai trouvé presque la même solution, mais je n'ai testé que des valeurs inférieures. La solution de Jack @ était clairement gagnante. +1 pour vous pour ne pas vous arrêter ici :)
dezso
Merci - c'est excellent et suscite la réflexion. Je pense que cela FROM generate_series(0, $1);doit être FROM generate_series(1, $1);. Avez-vous essayé la récursivité? Mes tests limités impliquent que cela évolue mieux:
Jack Douglas
2
J'ai essayé un lien symbolique /dev/urandomdans /var/lib/pgsql/dataet de la lecture avec pg_read_file()des points bonus fous, mais malheureusement , se pg_read_file()lit textentrée par une conversion d'encodage, donc il ne peut pas lire bytea. Si vous voulez vraiment la vitesse maximale, écrivez une Cfonction d'extension qui utilise un générateur de nombres pseudo-aléatoire rapide pour produire des données binaires et enrouler une donnée bytea autour du tampon :-)
Craig Ringer
1
@JackDouglas Je ne pouvais pas m'en empêcher. Version d'extension C de random_bytea. github.com/ringerc/scrapcode/tree/master/postgresql/…
Craig Ringer
1
Une autre excellente réponse! En fait, l'un des meilleurs que j'ai vus jusqu'à présent. Je n'ai pas testé l'extension, mais j'espère qu'elle fonctionne comme annoncé.
Erwin Brandstetter
5

J'aimerais pouvoir générer des champs aléatoires de longueur arbitraire

Cette fonction le fera, mais 1 Go prendra du temps car il ne se met pas à l'échelle linéairement avec la longueur de sortie:

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 de sortie:

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 ici

Jack Douglas
la source
Belle utilisation de width_bucket. Pratique.
Craig Ringer
1
J'ai amélioré votre approche pour éviter le PL / PgSQL et la boucle de concaténation coûteuse; voir la nouvelle réponse. En utilisant string_agg sur generate_series au lieu d'une boucle de concaténation PL / PgSQL sur bytea, je constate une amélioration de 150 fois des performances.
Craig Ringer