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

У меня есть хэш Perl DBM, содержащий список URL-адресов, которые я хочу выбирать случайным образом для балансировки нагрузки сайтов-пауков. В результате я хочу выбрать ключ наугад или выбрать n-й элемент (чтобы я мог рандомизировать n).

Я знаю, что это противоречит концепции хеша, но возможно ли это?

ПРИМЕЧАНИЕ: упущен важный момент, что размер хеша будет слишком большим, чтобы загрузить все ключи для случайного выбора.


person Paul    schedule 27.01.2010    source источник
comment
Какой модуль DBM вы используете?   -  person Sinan Ünür    schedule 28.01.2010
comment
Стандартная DBM на Perl 5.8.x, созданная для Windows. Извините, у меня нет более подробной информации.   -  person Paul    schedule 28.01.2010


Ответы (4)


Я не думаю, что какой-либо из пакетов DBM имеет API для получения случайного ключа или для получения ключей по порядковому номеру. Вы можете найти конкретный ключ или прочитать все ключи в любом порядке, выбранном базой данных для их возврата (который может измениться, если база данных будет изменена, и может быть или не быть достаточно "случайным" для всего, что вы хотите сделать).

Вы можете прочитать все ключи и выбрать один, но это потребует каждый раз чтения всей базы данных (или, по крайней мере, значительной ее части), и это, вероятно, слишком медленно.

Я думаю, вам нужно изменить структуру данных.

  1. Вы можете использовать настоящую базу данных SQL (например, SQLite), чтобы вы могли найти строки как по порядковому номеру строки, так и по URL-адресу. Это будет максимально гибко.

  2. Вы можете использовать последовательное целое число в качестве ключа для вашего файла DBM. Это упростило бы выбор случайного, но вы больше не могли искать записи по URL-адресу.

  3. Вы можете использовать два файла DBM: тот, который у вас есть сейчас, и второй, содержащий последовательное целое число с URL-адресом в качестве значения. (На самом деле, поскольку URL-адреса не выглядят как целые числа, вы можете хранить оба набора записей в одном и том же файле DBM, но это усложнит любой код, использующий each.) Это займет вдвое больше места на диске и усложнит вставку/удаление. записи немного сложнее. Вам, вероятно, будет лучше с подходом № 1, если только вы не можете установить SQLite по какой-либо причине.

person cjm    schedule 27.01.2010
comment
связанный вопрос для «настоящих баз данных SQL»: stackoverflow.com/questions/19412/ - person plusplus; 28.01.2010

Выбор случайного элемента из массива проще, поэтому вы можете использовать keys(%foo) для получения массива ключей и случайного выбора из него.

Я считаю, что это вернет случайный элемент $x из массива:

$x = $array[rand @array];

Если вы хотите перетасовать массив, рассмотрите List::Util::shuffle. См. http://search.cpan.org/perldoc/List::Util#shuffle_LIST

person dreeves    schedule 27.01.2010
comment
Уже есть List::Util::shuffle. См. search.cpan.org/perldoc/List::Util#shuffle_LIST. - person Sinan Ünür; 28.01.2010
comment
Нет необходимости писать свой собственный shuffle, когда тот, что из List::Util, надежнее (и быстрее). - person friedo; 28.01.2010
comment
Спасибо за совет, это решение, в котором размер хэша управляем. Есть ли альтернатива для хэшей, слишком больших для загрузки всех ключей? - person Paul; 28.01.2010

Конечно, это возможно. Сначала получите список ключей. Затем рандомизируйте список, используя shuffle из List::Util.

Затем зациклитесь на клавишах.

Если ключей слишком много (поэтому хранить их все в списке и перетасовывать невозможно), просто помните, что вы используете связанные хэши. Просто используйте each для перебора пар ключ-значение.

Порядок будет детерминированным, но, насколько мне известно, он не будет алфавитным или порядком вставки. Это само по себе может дать вам то, что вы хотите.

person Sinan Ünür    schedule 27.01.2010
comment
Спасибо за совет. К сожалению, мой хэш слишком велик, чтобы загрузить все ключи. - person Paul; 28.01.2010
comment
PaulMdx - each возвращает ключ и значение итеративно по хешу. Таким образом, вы можете справиться с ними по одному. - person daotoad; 28.01.2010

Вы можете использовать DBM::Deep вместо традиционный файл БД для хранения ваших данных.

tie %hash, "DBM::Deep", {
    file => "foo.db",
    locking => 1,
    autoflush => 1
};

# $hash{keys} = [ ... ]
# $hash{urls} = { ... } <- same as your current DB file.

my $like_old = $hash{urls}; # a ref to a hash you can use like your old hashref.
my $count = @{$hash{keys}};

При этом вы можете извлекать случайные значения по мере необходимости.

person daotoad    schedule 28.01.2010