Можете ли вы объяснить эти тревожные аномалии md5 и по модулю?

Хорошо, заголовок действительно очень субъективен. Но вот в чем проблема для меня.

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

Я также хочу убедиться, что один и тот же URL-адрес всегда обслуживается одним и тем же сервером.

Поэтому я просто определил маленькая функция, которая возвращает хост для использования путем хеширования URL-адреса запроса и вычисляет по модулю количество используемых серверов:

function pseudocode_statify($url) { // $url looks like /folder1/folder2/file.jpg
 return 'http://' . md5($url) % $num_of_servers .'.mydomain.com' . $url;
}

Сначала у меня было что-то вроде шестнадцатеричного декодирования и подстроки для предотвращения переполнения на месте, но обнаружил, что это просто работает хорошо, как описано выше.

Однако моя проблема в том, что если я запустил следующий тестовый сценарий:

for($i=0;$i<100000;$i++) {
  $md5 = md5(uniqid($i).microtime().rand(1,999999999999));
  $result[$md5%2]++;
}

Я ожидал равномерного распределения. это означает, что $ result [0] будет близко к значению $ result [1];

Это не тот случай.

Хорошо, здесь нет ничего особенного. Я бы просто согласился с тем фактом, что md5 не так равномерно распределен, как я думал, и пошел бы на другой алгоритм хеширования, такой как sha1 или что-то в этом роде.

Но я попытался воспроизвести результаты и нашел шаблон, который я не могу объяснить.

Соотношение всегда было около 2/1. На самом деле соотношение всегда было примерно от 1 / 2,16 до 1 / 2,17

Пример вывода некоторых запусков приведенного выше скрипта:

output was generated by: echo "ratio: ".$result[0]/$result[1]."\n";

ratio: 2.1757121534504
ratio: 2.1729411578062
ratio: 2.1726559360393
ratio: 2.1676895664225
ratio: 2.1667416128848
ratio: 2.1667115284133
ratio: 2.1677791605385
ratio: 2.1658969579688
ratio: 2.1668508131769
ratio: 2.1689292821741

Странно было то, что отношение сумм% 2 равно 1 и сумма% 2, равная 0 иногда чередовалась!

for($j = 0; $j<100;$j++) {
    for($i=0;$i<100000;$i++) {
      $md5 = md5(uniqid($i).microtime().rand(1,999999999999));
      $result[$md5%2]++;
    }
var_dump($result);
}

Я запускал сценарий из командной строки два раза и прервал его после трех запусков, и он произвел два следующих вывода:

joe@joe-laptop:/home/flimmit/httpdocs$ php test.php
PHP Notice:  Undefined variable: result in /home/flimmit/httpdocs/test.php on line 6
PHP Notice:  Undefined offset: 0 in /home/flimmit/httpdocs/test.php on line 6
PHP Notice:  Undefined offset: 1 in /home/flimmit/httpdocs/test.php on line 6
array(2) {
  [0]=>
  int(68223)
  [1]=>
  int(31777)
}
array(2) {
  [0]=>
  int(136384)
  [1]=>
  int(63616)
}
array(2) {
  [0]=>
  int(204498)
  [1]=>
  int(95502)
}
^C
joe@joe-laptop:/home/flimmit/httpdocs$ php test.php
PHP Notice:  Undefined variable: result in /home/flimmit/httpdocs/test.php on line 6
PHP Notice:  Undefined offset: 1 in /home/flimmit/httpdocs/test.php on line 6
PHP Notice:  Undefined offset: 0 in /home/flimmit/httpdocs/test.php on line 6
array(2) {
  [1]=>
  int(31612)
  [0]=>
  int(68388)
}
array(2) {
  [1]=>
  int(63318)
  [0]=>
  int(136682)
}
array(2) {
  [1]=>
  int(94954)
  [0]=>
  int(205046)
}
^C
joe@joe-laptop:/home/flimmit/httpdocs$ 

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

Странно то, что я могу воспроизвести это поведение ТОЛЬКО при запуске скрипта несколько раз.

Я написал этот небольшой скрипт, чтобы воспроизвести «подкачку» и сгенерировать достаточно данных измерения:

for($j = 0; $j<100;$j++) {
  for($i=0;$i<rand(1000,10000);$i++) {
    $md5 = md5(uniqid($i).microtime().rand(1,99999999));
    $result[$md5%2]++;
    }
    #var_dump($result);
    echo "ratio: ".$result[0]/$result[1]." ".(($result[0]<$result[1]) ? "A":"B")."\n";
    sleep(rand(2,5));
}

Но здесь он печатает только b , никогда не пятерки. Это заставило меня подумать, что в сценарии может быть семантическая ошибка, но я ее не обнаружил.

Я действительно застрял, и это меня очень беспокоит.

Итак, мои вопросы:

  • Вы можете порекомендовать любую литературу / веб-ссылки, где я мог прочитать о md5 немного глубже, включая дистрибутивы и т. д.

  • Вы можете объяснить / воспроизвести поведение? У меня здесь ошибка? (на самом деле это очень вероятно, но я не могу его найти)

  • Можете ли вы порекомендовать какой-либо другой алгоритм, который подходит для моего случая использования? Он не должен быть криптографическим или сильным, а должен быть быстрым, детерминированным и равномерно распределенным.

6
задан The Surrican 30 March 2011 в 18:27
поделиться