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

StackOverflow https://stackoverflow.com/questions/1120371

  •  13-09-2019
  •  | 
  •  

Вопрос

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

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

Решение 2

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

Другие советы

Мы пошли на 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 и ищете удобный путь обновления в будущем.

ДЕСЯТИЧНЫЙ(39)

Плюсы:

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

Минусы:

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

ДВОИЧНЫЙ(16)...

Плюсы:

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

Минусы:

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

ДВОИЧНЫЙ(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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top