Как я могу сгенерировать случайную байту

18

Я хотел бы иметь возможность генерировать случайные byteaполя произвольной длины (<1 ГБ) для заполнения тестовых данных.

Каков наилучший способ сделать это?

Джек Дуглас
источник

Ответы:

20

Расширяя ответ Джека Дугласа, чтобы избежать необходимости в зацикливании PL / PgSQL и конкатенации байтов, вы можете использовать:

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

Это простая SQLфункция, которую дешевле вызывать, чем PL / PgSQL.

Разница в производительности из-за измененного метода агрегации огромна для больших byteaзначений. Хотя исходная функция на самом деле в 3 раза быстрее для размеров <50 байтов, эта функция гораздо лучше масштабируется для больших значений.

Или используйте функцию расширения C :

Я реализовал генератор случайных байтов в виде простой функции расширения Си. Это в моем хранилище скрап-кода на GitHub . Смотрите README там.

Это снижает производительность вышеупомянутой версии SQL:

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
Ну, я придумала почти то же самое решение, но проверила только на более низкие значения. Там решение @ Джека было явным победителем. +1 за то, что вы не остановились здесь :)
dezso
Спасибо - это отлично и заставляет задуматься. Я думаю, что FROM generate_series(0, $1);должно быть FROM generate_series(1, $1);. Вы пробовали рекурсию? Мое ограниченное тестирование подразумевает, что это масштабируется лучше:
Джек Дуглас
2
Я попытался символической ссылка /dev/urandomв /var/lib/pgsql/dataи читать его с pg_read_file()бонусным сумасшедшими очками, но , к сожалению , pg_read_file()читаю textввод с помощью преобразования кодирования, поэтому он не может читать BYTEA. Если вам действительно нужна максимальная скорость, напишите функцию Cрасширения, которая использует быстрый генератор псевдослучайных чисел для получения двоичных данных и оборачивает данные байта вокруг буфера :-)
Крейг Рингер
1
@JackDouglas Я не мог с этим поделать. C расширение версии random_bytea. github.com/ringerc/scrapcode/tree/master/postgresql/…
Крейг Рингер
1
Еще один отличный ответ! На самом деле один из лучших, которые я видел до сих пор. Я не тестировал расширение, но надеюсь, что оно работает так, как рекламируется.
Эрвин Брандстеттер
5

Я хотел бы иметь возможность генерировать случайные поля байтов произвольной длины

Эта функция сделает это, но 1 Гб займет много времени, потому что она не масштабируется линейно с выходной длиной:

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

выходной тест:

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 здесь

Джек Дуглас
источник
Хорошее использование width_bucket. Handy.
Крейг Рингер
1
Я усовершенствовал ваш подход, чтобы избежать PL / PgSQL и дорогостоящего цикла конкатенации; увидеть новый ответ. Используя string_agg вместо generate_series вместо конкатенационного цикла PL / PgSQL в bytea, я вижу улучшение производительности в 150 раз.
Крейг Рингер