Как мне это сделать?
Прямо сейчас IPv6 не будет использоваться, но мне нужно спроектировать приложение, чтобы оно было готово к IPv6. Необходимо хранить IP-адреса и блоки CIDR (также BGP NLRI, но это другая история) в базе данных MySQL. Я всегда использовал INT для IPv4 + TINYINT для masklen, но IPv6 - 128 бит.
Какой подход подойдет для этого лучше всего? 2xBIGINT? CHAR(16) для двоичного хранилища? CHAR(39) для хранения текста? 8xSMALLINT в выделенную таблицу?
Что ты посоветуешь?






Я не уверен, что является ответом верно для MySQL, учитывая, что он еще не поддерживает форматы адресов IPv6 изначально (хотя, хотя «WL # 798: поддержка MySQL IPv6» предполагает, что он будет в MySQL v6.0, текущая документация не поддерживает это вверх).
Однако из тех, что вы предложили, я бы предложил выбрать 2 * BIGINT, но убедитесь, что они НЕ ПОДПИСАНЫ. Существует своего рода естественное разделение на границе адреса / 64 в IPv6 (поскольку / 64 - это наименьший размер сетевого блока), которое хорошо согласуется с этим.
на самом деле - если бы я делал это, я бы оставил первый bigint как NULL для IPv4 или использовал бы отдельное поле. Таким образом, часть IPv4 появляется в наименее значимом слове.
И если бы я делал это, я бы сохранил адреса IPv4 в формате адресов IPv6 V4COMPAT, то есть в диапазоне :: / 96.
Вы не можете просто использовать два BIGINT, вы должны использовать два BIGINT UNSIGNED. Если вы возьмете FFFF: FFFF: FFFF: FFFF: FFFF: FFFF: FFFF: FFFF, например, разделение его пополам и преобразование каждой стороны в ее целочисленное представление приведет к двум значениям 18,446,744,073,709,551,615. Это максимальное значение 64-битного целого числа без знака.
@james: теперь IPv4 сопоставленные адреса требовать :: ffff: / 96 range
Будет ли IP-адрес использоваться программой, для которой имеет смысл двоичный файл? Или вам лучше сохранить текстовое представление? Кроме того, с IPv6 вы с меньшей вероятностью будете использовать адрес в целом и с большей вероятностью будете использовать имена хостов. Насколько это актуально, частично зависит от приложения. CHAR (16) - плохой выбор; char предназначен для символьных данных и не любит большие потоки нулевых байтов, которые преобладают в адресах IPv6. 2 x BIGINT было бы неудобно - два поля, которые на самом деле являются одним (плюс, значение хранится с прямым порядком байтов или прямым порядком байтов?). Я использовал тип BINARY фиксированного размера или, если он недоступен, тип blob.
Если вы сохраните его в BINARY, то никогда не будет никаких шансов выполнить побитовые операторы в самой БД, чтобы найти совпадающие адреса (то есть все те адреса, которые соответствуют определенной подсети)
Вот почему я предложил текстовый формат, в котором вы можете выполнять сопоставление регулярных выражений (хотя я не упоминал об этом). Хранить его в любом двоичном формате будет непросто, если вы не обновите его до полного определяемого пользователем типа (поддерживает ли MySQL это?) И не предоставите соответствующие операторы.
Если вы склоняетесь к char (16), определенно используйте вместо него двоичный (16). binary (n) не имеет понятия сопоставления или набора символов (или, скорее, это char (n) с набором символов / сопоставлением 'binary'). По умолчанию для char в mysql используется latin1_swedish_ci, что означает, что он будет пытаться выполнять сортировку и сравнение без учета регистра для значений байтов, которые являются допустимыми кодовыми точками в latin1, что вызовет у вас всевозможные неожиданные проблемы.
Другой вариант - использовать decimal (39, 0) zerofill unsigned, что не так эффективно, как два bigint (десятичное будет использовать 4 байта на девять цифр в текущих версиях mysql), но позволит вам сохранить все это в одном столбце и распечатать красиво.
В проекте, над которым я сейчас работаю, мы используем десятичный (39,0) подход, и он работает хорошо, за некоторыми исключениями. Иногда вам нужно ОТНОСИТЬ значения для сравнения. т.е. WHERE ipv6 = CAST ('строка, содержащая числовой ipv6' AS decimal (39,0)). Специально для языков, которые не поддерживают 128-битные числовые типы, и привязка параметров библиотеки mysql недостаточно умна.
Я бы выбрал полный "стандартный" печатный формат из 39 символов: -
"2001:0db8:85a3:0000:0000:8a2e:0370:7334"
40 с нулевым ограничителем.
Это формат, используемый инструментами командной строки * nix, и формат, в котором обычно указывается IPV6-адрес (?).
Я бы не стал. Адреса IPv6 часто отображаются в сокращенном формате, и было бы также довольно неэффективно искать в таблице любой адрес, который находится в определенной подсети, особенно если подсеть охватывает малую границу.
@ Джеймс Андерсон: И что еще хуже, вы не могли сравнить, лежит ли данный IPv6 в указанном диапазоне (геолокация, запрет диапазона IP-адресов).
@Quandary Это было верно с форматом IPv4 с точками. Однако для сравнения можно использовать распечатанный формат IPv6 в развернутом виде (39 символов).
Я работаю над проектом сопоставления самого длинного префикса, поэтому я разделяю адрес на 4 целых числа для адресов IPv4. Это работает хорошо. Я бы расширил это до адресов IPv6.
Обратите внимание, что максимальная длина IPv6-адреса, включая идентификатор области, составляет 46 байтов, как определено INET6_ADDRSTRLEN в стандартных заголовках C. Для использования Интернета вы должны иметь возможность игнорировать идентификатор зоны (% 10, # eth0 и т. д.), Но просто помните, когда getaddrinfo возвращает более длинный результат, чем ожидалось.
Принято окончательное решение: 2xBIGINT, если второй bigint равен NULL, то это означает IPv4.