Как хранить и искать IP-адрес - PullRequest
3 голосов
/ 19 января 2009

У меня есть 4 источника IP-адресов, я хочу сохранить их в SQL Server и разрешить указывать диапазоны, которые можно классифицировать по исходному коду страны, в списке исключений по странам.

Для этого у меня есть 2 таблицы.

IPAddressRange CountryCode

Что мне нужно знать, так это то, что если эти данные были возвращены клиенту, а затем кэшированы для быстрого запроса, то каков наилучший способ хранения возвращенных данных для запроса определенного IP-адреса в пределах диапазонов. Я хочу знать, есть ли указанный IP-адрес в списке.

Причина, по которой список находится в БД, заключается в простоте хранения.

Причина, по которой я хочу кешировать и затем использовать данные на клиенте, заключается в том, что я слышал, что поиск по IP-адресам происходит быстрее в структуре дерева. Итак, я думаю, что мне нужно получить список из базы данных, хранить в кэше в структуре, которая очень быстро ищет.

Любая помощь в А) Структура SQL для хранения адресов и б) Код для поиска IP-адресов.

Я знаю решение для проекта кода, в котором есть алгоритм кода для поиска, не уверенный, как смешать это с аспектом хранения.

В идеале без использования сторонней библиотеки. Код должен быть на нашем собственном сервере.

Ответы [ 7 ]

3 голосов
/ 20 января 2009

Я сделал фильтр по стране, как вы описали.

Однако, поэкспериментировав некоторое время, я обнаружил, что это невозможно сделать быстрым способом с SQL. Вот почему базы данных IP, такие как , эта (та, которую я использую) предлагает двоичную базу данных, которая на намного быстрее, поскольку она оптимизирована для данных такого типа.

Они даже прямо говорят:

Обратите внимание, что запросы, сделанные против CSV Данные, импортированные в базу данных SQL, могут займет до нескольких секунд. Если производительность является проблемой, двоичный формат гораздо быстрее, и может обрабатывать тысячи просмотров в секунду.

Кроме того, они даже дают код для запроса этой базы данных.

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

1 голос
/ 19 января 2009

Предполагая, что ваши IP-адреса являются IPV4, вы можете просто сохранить их в целочисленном поле. Создайте 2 поля, одно для нижней границы диапазона, а другое для верхней границы. Затем убедитесь, что эти поля проиндексированы. При поиске значений просто ищите, где значение больше или равно нижней границе и меньше или равно верхней границе. Я бы поэкспериментировал с чем-то таким простым, прежде чем пытаться программировать что-то более сложное самостоятельно, что на самом деле не дает заметно более быстрых результатов.

0 голосов
/ 20 января 2009

Для IPv4 обычно администратор БД рекомендует 4 поля tinyint, но вы используете диапазоны, которые больше подходят для ранее предоставленных целочисленных решений хранения. В этом случае вы должны сохранить начальный IP-адрес и конечный IP-адрес для диапазона. Тогда сделать сравнение просто.

0 голосов
/ 20 января 2009

Вы можете сделать это эффективно, если вы сохраните свои начальные адреса IPv4 в правильном типе данных. Varchar (или другой тип строки) не подходит - вам нужно использовать int.

Для IPv4 сохраните IP-номер в неподписанном виде, который достаточно велик, а затем сохраните его в формате INET_ATON (который достаточно прост для генерации; я не уверен, как в C #, но это не сложно).

После этого вы можете легко и эффективно определить, к какому диапазону относится IP-адрес, настроив базу данных для сканирования диапазона.

Используя LIMIT (или SELECT TOP 1 в MSSQL), вы можете остановить его, когда найдет запись.

SELECT TOP 1 networkidorwhatever, IPNumber, IPNumberUpperBoundOrWhateverYouCallIt 
FROM networks 
WHERE IPNumber <= IPNUMBERTOQUERY ORDER BY IPNumber DESC 

Если найти номер сети с наибольшим номером, который <= IP-номер, то это тривиальная проверка, чтобы определить, находится ли этот IP-адрес внутри него. </p>

Это должно быть эффективно при условии наличия обычного индекса IPNumber.

Для IPv6 типы различны, но принцип тот же.

0 голосов
/ 20 января 2009

IPv6-адрес может быть восьмибайтовым. целое число без знака (ulong в C #)

IPv6-адреса 128-разрядные (16 байт), а не 8, как это предлагается. Я сейчас занимаюсь этой проблемой для диапазонов IP-адресов.

Я хочу попробовать дополненные или шестнадцатеричные строки и просто выполнить <и> сравнения

0 голосов
/ 19 января 2009

Я никогда не пытался это сделать, поэтому возьмите мой ответ с небольшим количеством соли, но я думаю, что на самом деле три не то, что вы хотите, если вы не собираетесь хранить каждый IP-адрес, который вы хотите заблокировать (в отличие от диапазонов или подсетей / маска). Я думаю, что btree подойдет лучше, и в этом случае просто продолжайте и используйте свою обычную базу данных (многие базы данных реализованы с помощью btrees или одинаково хороших структур данных). Я бы сохранил каждый из 4 байтов IP в отдельном столбце, чтобы помочь в поиске по подсетям класса A / B / C со значениями «пофиг», равными NULL, но нет причин, по которым вы не могли сохранить он представляет собой один 32-битный целочисленный столбец и сокращает числа, чтобы выяснить, в какой диапазон он должен попадать (хранение маскированных значений в этом случае будет немного сложнее).

0 голосов
/ 19 января 2009

IPv4-адрес может быть сохранен как четырехбайтовое целое число без знака (uint в C #). Адрес IPv6 может представлять собой восьмибайтовое целое число без знака (удлиненное в C #). Создайте столбцы соответствующей ширины в SQL, затем извлеките и сохраните их в переменных. Затем вы используете простую целочисленную математику для проверки нужных диапазонов, предполагая, что диапазоны на самом деле смежны.

Более сложным решением было бы создание класса IPAddress, который дает вам доступ к более знакомой структуре из точечных квадратов, но под покровами он будет делать то же самое, что и у вас.

...