Как конвертировать IPv6 из двоичного файла для хранения в MySQL - PullRequest
16 голосов
/ 13 июля 2009

Я пытаюсь эффективно хранить адреса IPv6 в MySQL 5.0. Я прочитал другие вопросы, связанные с этим, , такие как этот . Автор этого вопроса в конечном итоге выбрал два поля BIGINT. Мои поиски также обнаружили другой часто используемый механизм: использование DECIMAL (39,0) для хранения адреса IPv6. У меня есть два вопроса по этому поводу.

  1. Каковы преимущества и недостатки использования DECIMAL (39,0) по сравнению с другими методами, такими как 2 * BIGINT?
  2. Как преобразовать (в PHP) из двоичного формата, возвращаемого inet_pton () , в формат десятичной строки, используемый MySQL, и как мне преобразовать обратно, чтобы я мог красиво печатать с помощью inet_ntop ()

Ответы [ 3 ]

30 голосов
/ 25 октября 2009

Вместо этого мы выбрали столбец VARBINARY(16) и используем inet_pton() и inet_ntop() для выполнения преобразований:

https://github.com/skion/mysql-udf-ipv6

Функции могут быть загружены на работающий сервер MySQL и предоставят вам INET6_NTOP и INET6_PTON в SQL, точно так же, как привычные функции INET_NTOA и INET_ATON для IPv4.

Редактировать: В MySQL теперь есть совместимые функции, только с различными именами . Используйте вышеперечисленное только в том случае, если вы используете MySQL до версии 5.6 и ищете удобный путь обновления в будущем.

19 голосов
/ 13 августа 2009

Вот функции, которые я сейчас использую для преобразования IP-адресов в формат DECIMAL (39,0) и обратно. Они называются inet_ptod и inet_dtop для «представления в десятичном формате» и «десятичного представления». Требуется поддержка IPv6 и bcmath в PHP.

/**
 * Convert an IP address from presentation to decimal(39,0) format suitable for storage in MySQL
 *
 * @param string $ip_address An IP address in IPv4, IPv6 or decimal notation
 * @return string The IP address in decimal notation
 */
function inet_ptod($ip_address)
{
    // IPv4 address
    if (strpos($ip_address, ':') === false && strpos($ip_address, '.') !== false) {
        $ip_address = '::' . $ip_address;
    }

    // IPv6 address
    if (strpos($ip_address, ':') !== false) {
        $network = inet_pton($ip_address);
        $parts = unpack('N*', $network);

        foreach ($parts as &$part) {
            if ($part < 0) {
                $part = bcadd((string) $part, '4294967296');
            }

            if (!is_string($part)) {
                $part = (string) $part;
            }
        }

        $decimal = $parts[4];
        $decimal = bcadd($decimal, bcmul($parts[3], '4294967296'));
        $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616'));
        $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336'));

        return $decimal;
    }

    // Decimal address
    return $ip_address;
}

/**
 * Convert an IP address from decimal format to presentation format
 *
 * @param string $decimal An IP address in IPv4, IPv6 or decimal notation
 * @return string The IP address in presentation format
 */
function inet_dtop($decimal)
{
    // IPv4 or IPv6 format
    if (strpos($decimal, ':') !== false || strpos($decimal, '.') !== false) {
        return $decimal;
    }

    // Decimal format
    $parts = array();
    $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0);
    $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336'));
    $parts[2] = bcdiv($decimal, '18446744073709551616', 0);
    $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616'));
    $parts[3] = bcdiv($decimal, '4294967296', 0);
    $decimal = bcsub($decimal, bcmul($parts[3], '4294967296'));
    $parts[4] = $decimal;

    foreach ($parts as &$part) {
        if (bccomp($part, '2147483647') == 1) {
            $part = bcsub($part, '4294967296');
        }

        $part = (int) $part;
    }

    $network = pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]);
    $ip_address = inet_ntop($network);

    // Turn IPv6 to IPv4 if it's IPv4
    if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) {
        return substr($ip_address, 2);
    }

    return $ip_address;
}
0 голосов
/ 04 марта 2018

DECIMAL (39)

Плюсы:

  • Работает с основными арифметическими операторами (такими как + и -).
  • Работает с базовой индексацией (точной или диапазона).
  • Формат удобен для отображения.

Минусы:

  • Может принимать значения вне диапазона для IPv6.
  • Не очень эффективный механизм хранения.
  • Может вызвать путаницу в отношении того, какие математические операторы или функции работают, а какие нет.

BINARY (16) ...

Плюсы:

  • Наиболее эффективный формат для точного представления.
  • Работает с базовой индексацией (точной и диапазона).
  • Работает с индексированием префиксов для префиксов, кратных 8 битам.
  • Хранит только действительные значения IPv6 (хотя и не гарантирует правильную адресацию).
  • MySQL в более поздних версиях имеет функции, которые поддерживают преобразования для этого формата в представления IPv6 и обратно (но не в 4in6).

Минусы:

  • Не подходит для отображения.
  • Не дружит с операторами или функциями, предназначенными для чисел.

BINARY (39) ...

Это для полных адресов (используя hexdec даже для 4in6). Может также быть ASCII, а не двоичным.

Плюсы:

  • Удобочитаемый для человека (если вы можете назвать это IPv6).
  • Поддерживает базовое индексирование (точное и диапазонное).
  • Поддерживает индексирование префиксов для кратных 4 битам.
  • Непосредственно совместим с IPv6. Преобразование не требуется.

Минусы:

  • Не работает с математическими функциями или операторами.
  • Самое неэффективное хранилище.
  • Может разрешить недопустимые представления.

Странности:

  • Становится сложным, если вам нужны такие вещи, как регистр.
  • IPv6 имеет другие форматы отображения, хотя их использование повышает сложность, например, вы можете иметь два представления одного и того же адреса или потерять поиск по диапазону. Может даже понадобиться сделать его длиной 45 байт или использовать varchar / varbinary.
  • Варианты этого могут поддерживать сохранение адреса как первоначально полученного. Это редко может быть желательным, но когда вы теряете много преимуществ.
  • Удалите разделители в полном формате и просто сохраните их в виде шестнадцатеричной строки, чтобы уменьшить количество хлопот и повысить эффективность. Вы можете пройти долгий путь, если индексация префиксов важна (BINARY (128)).

БОЛЬШОЙ НЕ ПОДПИСАНО * 2

Плюсы:

  • Работает с математическими операторами и функциями с оговоркой о необходимости выполнения дополнительных действий вокруг него, состоящей из двух столбцов.
  • Эффективно, но опять же с оговоркой, что он состоит из двух столбцов, добавит некоторые накладные расходы.
  • Работает с основными показателями (точные, диапазон).
  • Работает с индексом префикса, если префикс равен 64 битам.
  • Показать дружественный формат.

Минусы:

  • Два столбца делают его неатомарным и означают удвоение большого количества операций над ним.

Странности:

  • Многие современные языки и системы дают 64-битные числа, но не без знака. Подписано проблематично. Отрицательные числа представлены ниже положительных, но их битовые последовательности на самом деле выше. По этой причине обычно вместо этого используют 4 * INT UNSIGNED.
  • Точно так же люди могут разбить его для индексации префиксов, и вы можете пройти как минимум до 8 бит (TINYINT UNSIGNED). Некоторые люди могут также использовать тип BIT (1) для индексации полного префикса, при условии, что MySQL правильно совмещает индексы для битовых типов.
  • Опять-таки, аналогично с четырьмя столбцами некоторые операции, требующие таких вещей, как переход к другому, по иронии судьбы упрощаются из-за слабых битов во время вычислений (промежуточные значения в вычислениях все еще могут быть 64-разрядными).

Резюме

Люди будут использовать разные форматы по разным причинам. Обратная совместимость может быть одной из причин, и это зависит от того, что делается для IPv4. Другие зависят от того, как используются адреса и оптимизация вокруг этого. Вы можете увидеть более одного подхода.

B16 - это хороший подход по умолчанию, так как он наиболее эффективен и прост в использовании.

Для преобразований в PHP вы можете сделать их вручную, если исследуете:

  • gmp или bcmath
  • Обработка чисел в PHP и побитовые операторы, особенно помните об ограничениях на int или float, а также на зависящих от них функциях, которые в противном случае могут показаться полезными
  • Форматы IPv6
  • упаковка / распаковка, bin2hex / hex2bin.

Однако я бы порекомендовал использовать общую библиотеку для работы с различными форматами отображения IPv6.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...