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


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

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

3 ответа:

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

Https://bitbucket.org/skion/mysql-udf-ipv6

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

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

Вот функции, которые я теперь использую для преобразования IP-адресов из и в десятичный (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;
}

Десятичное число(39)

Плюсы:

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

Минусы:

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

Двоичный (16)...

Плюсы:

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

Минусы:

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

Двоичный (39)...

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

Плюсы:

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

Минусы:

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

Странности:

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

БИГИНТ Без подписи * 2

Плюсы:

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

Минусы:

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

Странности:

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

Резюме

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

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

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

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

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