Как конвертировать IPv6 из двоичного для хранения в MySQL
Я пытаюсь эффективно хранить IPv6-адреса в MySQL 5.0. Я читал другие вопросы, связанные с этим, , такие как этот. Автор этого вопроса в конечном итоге выбрал для двух полей BIGINT. Мои поиски также нашли другой часто используемый механизм: использование десятичного числа (39,0) для хранения адреса IPv6. У меня есть два вопроса по этому поводу.
-
Каковы преимущества и недостатки использования десятичной системы счисления (39,0) по сравнению с другими методами, такими как 2 * БИГИНТ?
- Как я могу преобразовать (в PHP) из двоичного формата, возвращаемого inet_pton () в десятичный строковый формат, используемый MySQL, и как я могу конвертировать обратно, чтобы я мог красиво печатать с помощью inet_ntop()?
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.