Подтвердить что ты не робот

Как конвертировать IPv6 из двоичного файла для хранения в MySQL

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

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

Ответ 1

Вот функции, которые я теперь использую для преобразования 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 IPv4
    if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) {
        return substr($ip_address, 2);
    }

    return $ip_address;
}

Ответ 2

Вместо этого мы inet_pton() столбец 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 и ищете удобный путь для обновления в будущем.

Ответ 3

ДЕСЯТИЧНАЯ (39)

Плюсы:

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

Минусы:

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

BINARY (16)...

Плюсы:

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

Минусы:

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

BINARY (39)...

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

Плюсы:

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

Минусы:

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

Странности:

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

BIGINT UNSIGNED * 2

Плюсы:

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

Минусы:

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

Странности:

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

Резюме

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

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

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

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

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