Wie kann ich einen zufälligen bytea generieren

18

Ich möchte in der Lage sein, zufällige byteaFelder beliebiger Länge (<1 GB) zum Auffüllen von Testdaten zu generieren .

Wie geht das am besten?

Jack Douglas
quelle

Antworten:

20

Wenn Sie die Antwort von Jack Douglas verbessern, um PL / PgSQL-Schleifen und die Verkettung von Bytes zu vermeiden, können Sie Folgendes verwenden:

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

Es ist eine einfache SQLFunktion, die billiger aufzurufen ist als PL / PgSQL.

Der Leistungsunterschied aufgrund der geänderten Aggregationsmethode ist bei größeren byteaWerten immens . Obwohl die ursprüngliche Funktion bei Größen <50 Byte bis zu dreimal schneller ist, skaliert diese Funktion bei größeren Werten viel besser.

Oder verwenden Sie eine C-Erweiterungsfunktion :

Ich habe einen Zufalls-Bytea-Generator als einfache C-Erweiterungsfunktion implementiert. Es befindet sich in meinem Scrapcode-Repository auf GitHub . Siehe dort die README.

Es macht die Leistung der obigen SQL-Version zunichte:

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
quelle
1
Nun, ich habe fast die gleiche Lösung gefunden, aber nur auf niedrigere Werte getestet. Die Lösung von There @ Jack war ein klarer Gewinner. +1 für dich, weil du hier nicht
anhältst
Vielen Dank - das ist ausgezeichnet und zum Nachdenken anregend. Ich denke FROM generate_series(0, $1);muss sein FROM generate_series(1, $1);. Haben Sie Rekursion versucht? Meine begrenzten Tests deuten darauf hin, dass dies besser skaliert:
Jack Douglas
2
Ich habe versucht, es mit Symbolen /dev/urandomzu verknüpfen /var/lib/pgsql/dataund zu lesen, pg_read_file()um verrückte Bonuspunkte zu erhalten, aber leider pg_read_file()liest ich textEingaben über eine Codierungskonvertierung, sodass es nicht bytea lesen kann. Wenn Sie wirklich maximale Geschwindigkeit wollen, schreiben Sie eine CErweiterungsfunktion, die einen schnellen Pseudozufallszahlengenerator verwendet, um Binärdaten zu erzeugen und ein Bytea-Datum um den Puffer zu wickeln :-)
Craig Ringer
1
@ JackDouglas Ich konnte nicht anders. C-Erweiterungsversion von random_bytea. github.com/ringerc/scrapcode/tree/master/postgresql/…
Craig Ringer
1
Eine weitere hervorragende Antwort! Eigentlich eines der besten, die ich bisher gesehen habe. Ich habe die Erweiterung nicht getestet, aber ich vertraue darauf, dass sie wie angekündigt funktioniert.
Erwin Brandstetter
5

Ich möchte zufällige Bytea-Felder beliebiger Länge erzeugen können

Diese Funktion wird es tun, aber 1 GB wird lange dauern, da es nicht linear mit der Ausgabelänge skaliert:

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

Ausgangstest:

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 hier

Jack Douglas
quelle
Gute Verwendung von width_bucket. Praktisch.
Craig Ringer
1
Ich habe Ihren Ansatz verbessert, um PL / PgSQL und teure Verkettungsschleifen zu vermeiden. siehe neue antwort. Durch die Verwendung von string_agg über generate_series anstelle einer PL / PgSQL-Verkettungsschleife auf bytea wird die Leistung um das 150-fache verbessert.
Craig Ringer