Определение страны по IP: тестируем скорость алгоритмов

    Для определения страны по IP необходимы специальные базы данных, состоящие из диапазонов IP адресов и соответствующих им стран. Обычно такие базы данных распространяются в виде CSV или SQL файлов для использования в СУБД, либо бинарных файлов специального формата.

    Для проведения тестирования была выбрана февральская база GeoLite Country, бесплатная версия GeoIP Country от MaxMind.

    В тестировании приняли участие несколько популярных решений и мой «велосипед» на эту тему.

    Участники тестирования


    MySQL

    В качестве подопытной СУБД будет использоваться MySQL. В которой создана таблица, состоящая из IP-диапазонов и номеров стран, IP преобразованы в integer и по ним построены индексы. Структура таблицы выглядит так:

    CREATE TABLE `ip2country ` (
    `ipn1` INT(10) UNSIGNED NOT NULL,
    `ipn2` INT(10) UNSIGNED NOT NULL,
    `num` TINYINT(3) UNSIGNED NOT NULL,
    PRIMARY KEY (`ipn1`),
    INDEX `ipn2` (`ipn2`)
    ) ENGINE=MyISAM;

    Для MySQL будут протестированы 3 запроса.
    1. Simple.
      SELECT `num` FROM `ip2country` 
      WHERE `ipn1` <= INET_ATON(' IP ') AND `ipn2` >= INET_ATON('IP')
    2. Between.
      SELECT num FROM `ip2country` 
      WHERE INET_ATON('IP') BETWEEN `ipn1` AND `ipn2`
    3. Subselect.
      SELECT num FROM 
      (SELECT * FROM ip2country WHERE `ipn1` <= INET_ATON('IP') ORDER BY `ipn1` DESC LIMIT 1) AS t 
      WHERE `ipn2` >= INET_ATON('IP')
    GeoIP API
    Для GeoIP будет использоваться родное API для PHP, и база данных в бинарном формате. Тестироваться он будет в двух режимах:
    • Standart — режим по умолчанию.
    • Memory — кэширование базы в памяти.
    SxGeo v2
    Немного слов о моем «велосипеде». Лет 6 назад, после изучения доступных на тот момент решений определения страны по IP, был впечатлен скоростью бинарного формата GeoIP. Но у него, как мне показалось, был недостаток в большом количестве перемещений по файлу для нахождения нужного IP. Появилась интересная идея по поводу своей реализации. Которая довольно быстро была реализована и, на удивление, оказалась значительно быстрее, чем я ожидал. Долгое время Sypex Geo использовался в своих проектах.

    На днях решил реализовать еще некоторые идеи по оптимизации. В итоге появилась версия Sypex Geo 2 (сокращено SxGeo). Файл с базой данных стал на 25% меньше, чем у первой версии, и при этом скорость увеличилась в 1,7-2 раза.

    Основные преимущества перед GeoIP и другими решениями.
    • Маленький размер базы чуть больше 4 байт на диапазон. К примеру, бинарная база GeoIP весит 1,2 МБ, у SxGeo 2 – 0.62 МБ.
    • Очень высокая скорость обработки (смотрим результаты тестирования).
    • Минимальное количество чтений с диска (3 + 1*N, где N – количество IP).
    • Дополнительные режимы для пакетной обработки.
    Тестироваться SxGeo будет в трех режимах:
    • File – обычный режим, рекомендуемый для одиночной обработки IP.
    • Batch – режим пакетной обработки, предназначен для обработки множества IP адресов за раз.
    • Batch + Memory – в этом режиме дополнительно используется кэш базы в памяти. Самый быстрый режим, но требует больше памяти, т.к. весь файл с базой загружается в память.
    Geobaza
    Также вне конкурса был протестирован алгоритм Geobaza. Вне конкурса, потому что использовался родной бинарный файл, со значительно большим количеством диапазонов. Geobaza показала около 2000-3000 IP/сек, был очень большой разброс по результатам. Если создатели Geobaza прочитают эту статью и пришлют файл, сгенерированный по февральскому GeoLite Country, то с удовольствием добавлю в тестирование.

    Тестирование


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

    Тестилось на серваке под управлением FreeBSD 8 и PHP 5.2.17. Также тестилось на локалке Win 7 x64, PHP 5.3.9, пропорции примерно те же, поэтому в таблицах представлены только результаты FreeBSD.
    Тест прогонялся 10 раз, усредненные данные приведены на графике.


    Самыми медленными оказались простые запросы MySQL. Причина столь медленной работы становится очевидной, если посмотреть EXPLAIN этих запросов.

    
    EXPLAIN SELECT num FROM `ip2country` 
    WHERE ipn1 <= INET_ATON('88.88.88.88') AND ipn2 >= INET_ATON('88.88.88.88') LIMIT 1;

    id   select_type   table	type	possible_keys	key	key_len	ref	rows	Extra
    1	SIMPLE	ip2country	range	PRIMARY		PRIMARY     4	NULL	51059  Using where
    1	SIMPLE	ip2country	range	PRIMARY	        PRIMARY     4	NULL	53852  Using where
    1	SIMPLE	ip2country	range	PRIMARY,ipn2	PRIMARY     4	NULL	51587  Using where


    Первый результат простого индекса PRIMARY KEY (`ipn1`), второй для составного индекса PRIMARY KEY (`ipn1`, `ipn2`), третий для двух индексов PRIMARY KEY (`ipn1`), INDEX `ipn2` (`ipn2`). Как видим для составного индекса строк для последующего перебора больше, чем в других случаях. Я же тестил без LIMIT 1, и в этом случае EXPLAIN пишет, что индекс не используется, хотя реально работает такой вариант быстрее, чем с LIMIT.

    Вариант с вложенным SELECT — значительно быстрее. Запрос показывает, что индексы в MySQL, когда они нормально используются, работают очень быстро, приближаясь к специализированным бинарным форматам.

    GeoIP показал, что его всё же использовать предпочтительнее, чем MySQL. Меня смутило, что использование кэширования в памяти дает столь малый прирост, меньше 10%. Поковырявшись в geoip.inc, нашел виновника. Им оказался следующий код:

    if ($gi->flags & GEOIP_MEMORY_CACHE) {
          // workaround php's broken substr, strpos, etc handling with
          // mbstring.func_overload and mbstring.internal_encoding
          $enc = mb_internal_encoding();
           mb_internal_encoding('ISO-8859-1');
    
          $buf = substr($gi->memory_buffer,
                                2 * $gi->record_length * $offset,
                                2 * $gi->record_length);
    
          mb_internal_encoding($enc);
    }

    Если закомментировать строки с mb_internal_encoding то в итоге скорость поднимется до 6600 IP/сек — уже более ощутимый прирост от использования кэширования в памяти. Кодировки в данном случае нас не волнуют, возможно были какие-то глюки с GeoIP City.

    Что касается SxGeo, то тут думаю, комментарии излишни. Он и в обычном режиме работает очень быстро, а в режиме Batch + Memory позволяет получить прибавку еще 40%.

    Желающие могут скачать и потестить SxGeo 2. Пожелания и багрепорты приветствуются.

    UPD. Потестил еще запросы в разных комбинациях индексов, оказалось, что если в первые 2 запроса добавить LIMIT 1, то MySQL начинает очень тупить, где-то в 3-5 раз медленнее.
    Поделиться публикацией
    Комментарии 93
      –1
      PRIMARY KEY (`ipn1`),
      INDEX `ipn2` (`ipn2`)
      Я бы на вашем месте снёс свой обзор в черновики и пошёл учить мат. часть про индексы в MySql
        +1
        Хм, и что не так с индексами хоть бы намекнули. Да и тут вообще-то рассматриваются популярные решения, встречающиеся в инете.
          –2
          Нормальная структура для использования индекса и запрос должны быть примерно такими:

          CREATE TABLE `worldip` (
            `start` int(10) UNSIGNED NOT NULL default '0',
            `end` int(10) UNSIGNED NOT NULL default '0',
            `code` varchar(2) NOT NULL default '',
            PRIMARY KEY (`start`,`end`)
          ) ENGINE=MyISAM;
          
          SELECT code FROM `worldip` WHERE `start` <= INET_ATON('IP') AND `end` >= INET_ATON('IP') LIMIT 1
          


          Индекс должен быть сдвоенным в общем.
            +2
            Я в статье не правильно описал проблему, индекс то используется, только можно сказать не полноценно.
            И составные индексы тут погоды не делает.
            Вот explain для двух вариантов, первый для составного ключа, второй вариант для двух отдельных индексов
            id	select_type    table	                   type	possible_keys  key        key_len    ref	rows        Extra
            1	SIMPLE	       ip2country_copy  range	PRIMARY		PRIMARY	4	NULL	1648	Using where
            1	SIMPLE	       ip2country_copy  range	PRIMARY,idn2	PRIMARY	4	NULL	1579	Using where
            

            Второй вариант даже чуть лучше
                +1
                А что именно там? Как раз запрос с вложенным SELECT который там советуют для определения страны по IP и участвует в этом тесте. Просто, есть еще два более простых запроса, хотелось наглядно показать насколько они тормозные.
                  0
                  Честно-говоря, не совсем понятно зачем этот sub-select вообще нужен, т.е. зачем усложнять запрос.
                  Когда два года назад решал эту же задачу на базе MaxMind, то обошелся
                  просто запросом:
                  SELECT * FROM ip2country WHERE `ipn1` <= INET_ATON('IP') ORDER BY `ipn1` DESC LIMIT 1

                  где индекс по полю `ipn1` будет использоваться как для фильтра, так и для сортировки.
                  Зачем вообще добавлять условие
                  WHERE `ipn2` >= INET_ATON('IP')
                  ?
                    0
                    Там «дырки» есть в диапазонах, в итоге при попадании IP в «дырку» будет неправильно определена страна.
                      0
                      Теоретически вы правы, но думаю, что такое если и будет случаться, то крайне редко
              0
              В таком запросе индекс не будет использоваться.
              mysql> EXPLAIN SELECT code FROM `worldip` USE INDEX (PRIMARY)
              WHERE `start` <= INET_ATON('8.8.8.8') AND `end` >= INET_ATON('8.8.8.8') LIMIT 1;
              +----+-------------+---------+--------+---------------+------+---------+------+------+-------+
              | id | select_type | table   | type   | possible_keys | key  | key_len | ref  | rows | Extra |
              +----+-------------+---------+--------+---------------+------+---------+------+------+-------+
              |  1 | SIMPLE      | worldip | system | PRIMARY       | NULL | NULL    | NULL |    1 |       |
              +----+-------------+---------+--------+---------------+------+---------+------+------+-------+
              
                +4
                Для такого запроса вторая часть индекса не будет использоваться никогда.

                Два разных индекса в этом случае будут удобнее в том смысле, что оптимизатор сможет выбирать.
                  0
                  SELECT code FROM `worldip` WHERE `start` <= INET_ATON('IP') AND `end` >= INET_ATON('IP') LIMIT 1
                  Индекс должен быть сдвоенным в общем.

                  Второй сегмент индекса будет задействован в случае, если на первый наложено условие по равенству, а в вашем случае это не так, будет скан диапазона по первому полю от минимальных значений до искомого IP, и чем больше значение INET_ATON('IP'), тем хуже.
              0
              структура mysql базы необязательно должна быть такой:
              можно хранить 1.1.1.0\24, и запрос тогда будет совсем другим.
                0
                Не очень представляю как в таком случае сделать быструю выборку.
                  0
                  Вы не правильно их готовите.
                  Вы пробуете искать в базе по открытому интервалу, те в начале ищем ВСЕХ левее, потом ВСЕХ правее. Потом пересекаем.
                  А можно и проще.
                  Вариант 1 — WHERE ip1<$ip ORDER BY ip1 DESC LIMIT 1.
                  Потому что нет пересекающихся регионов, есть вложенные.
                  Это это открытый интервал.
                  Найдем максимальную ширину IP адреса. те MAX(ip2-ip1). В зависимости от базы это значение разное, но вообще — не более 32к те /16
                  Вариант 2 — WHERE ip1 between ($ip-$maxWidth) and $ip ORDER BY ip1 DESC LIMIT 1.
                  Таким образом поиск производиться в закрытом диапазоне.

                  Также есть вариант когда у нас составной индекс, и можно искать как WHERE (ip1>$1 and ip2<$2) and(ip1<$ip and ip2>$ip) еще более ограничивая вариативность операций в базе.
                    0
                    На самом деле диапазоны бывают и даже по 30 млн. к примеру (28.0.0.0-30.255.255.255 — US)
                    Да я и не говорил, что не могу такую выборку сделать, я имел ввиду, что она не получится быстрее запроса с вложенным SELECT.

                    По-поводу составного индекса, выше уже замечали, что нормально работает, если по одному из столбцов стоит равенство, в случае когда у обоих значений стоит больше/меньше, то лучше вообще индекс по одному полю строить.
                0
                Добавьте пожалуйста в обзор эту базу:
                www.wipmania.com/ru/base/

                Там как раз нормально с индексом используется mysql
                  0
                  Там просто объединенный индекс, он работает так же как два индекса отдельных. Я в статье несколько неправильно сформулировал насчет индекса. Сейчас подправлю.
                    0
                    В том то и дело, что работает он не как два отдельных индекса, а эффективнее. Вот и интересно, изменит ли график и на сколько именно такой вариант.
                      0
                      Я проверил, составной индекс в данном случае работает медленнее примерно процентов на 10-15%.
                      Выше explain привел, который проясняет почему, в составном индексе получается больше строк для перебора.
                        +2
                        Ясно, большое спасибо )
                        И вообще за SxGeo огромное спасибо — очень полезная библиотека.
                • НЛО прилетело и опубликовало эту надпись здесь
                    +2
                    А если таблицу в MySQL уже отсортированной хранить? Если использовать в MySQL таблицы в памяти?
                      +1
                      Если памяти достаточно и буфера настроены так, как полагается — тогда innodb и так будет держать всё в памяти. Сортировка есть в индексах, а вы предлагаете по какому полю её хранить сортированной?
                        0
                        По ipn1, тогда в subquery не потребуется сортировка.
                          +1
                          Сортировка не потребуется, если субд будет знать, что сортировка уже есть. А без order by порядок ничем не гарантируется.
                            0
                            Что-то не понял вашу мысль.
                              0
                              Основная идея: чем хранение таблицы в отсортированном состоянии поможет? Какую операцию это упростит?
                                0
                                Очевидно же: уйдёт сортировка по полю в подзапросе.
                                  0
                                  Не уйдёт — субд не гарантирует порядок в резалтсете без указания сортировки.

                                  Оптимизация, способ хранения данных на диске, кеширование и прочее вполне могут всё испортить.
                                    +1
                                    Вы это сами придумали или в документацию всё-таки посмотрели?
                                    ORDER BY enables you to create the new table with the rows in a specific order. Note that the table does not remain in this order after inserts and deletes. This option is useful primarily when you know that you are mostly to query the rows in a certain order most of the time. By using this option after major changes to the table, you might be able to get higher performance. In some cases, it might make sorting easier for MySQL if the table is in order by the column that you want to order it by later.

                                    dev.mysql.com/doc/refman/5.1/en/alter-table.html
                                      0
                                      WHERE `ipn1` <= INET_ATON('IP') ORDER BY `ipn1` DESC

                                      Для этого вложенного запроса индекс по `ipn1` разрулит и сортировку.

                                      Что именно вы хотите улучшить, убрав сортировку и добавив указанный выше костыль?
                                        0
                                        Он «разрулит» сортировку не каким-то магическим образом. Это работа, менее серьёзная, но работа. «Костыль», как вы изволили выразиться, призван избавить MySQL от этой работы. Конечно, у него есть недостатки (описанные в документации выше), поэтому он применяется для данных, которые меняются редко (как списки geoip).

                                        С чем вы пытаетесь поспорить, я не понимаю.
                                          0
                                          «менее серьёзная, чем сортировка таблицы, но работа»
                                            +1
                                            Отнюдь нет, он НИКАКОЙ работы производить не будет.

                                            `WHERE `ipn1` <= INET_ATON('IP')` — для этого условия мы в дереве находим первый ряд, для которого условие истинно (поиск в B-Tree, LogN)
                                            `ORDER BY `ipn1` DESC` — по индексу мы будем идти от больших к меньших (mysql не поддерживает desc, так что всегда именно так)
                                            `LIMIT 1` — берём первый ряд (тот самый, который мы уже нашли в шаге 1)

                                            никакой дополнительной работы тут нет. Сортировка тут несёт декларативную задачу, просто подсказывая в какую сторону отсчитывать ряды.

                                            С чем я пытаюсь поспорить — с тем, что ваше предложение имеет хоть какую-то практическую пользу
                                              0
                                              Ок. Вы правы.
                                0
                                По умолчанию в MySQL нет никакой сортировки. Сортировка будет только тогда, когда мы в запросе напишем ORDER BY.
                                  0
                                  Прошу прощения, это должен был быть ответ к этому комментарию
                                    0
                                    По-умолчанию — нет. Можно добиться, чтобы была.
                            0
                            Спасибо, отличный инструмент.

                            А тоже самое, но уже с точностью до города?
                            • НЛО прилетело и опубликовало эту надпись здесь
                                +1
                                Пока в этом направлении не смотрел. Думаю для начала прикручу поддержку городов, потом глянем на ipv6.
                                0
                                ежу понятно, что древовидные алгоритмы в разы быстрее линейных.
                                сам как-то писал велосипед по определению города по IP
                                а алгоритме использовался RBTree с ключом IPmin в котором лежала структура: IPmax, CityId
                                Тестирование показывало отличные результаты (по Городам RF, BY, UA) но база занимала много памяти
                                Была идея использовать его в качестве модуля для nginx
                                к сожалению инвестор решение отверг и я переключился на реализацию других проектов
                                  0
                                  Автор, скоро уже PHP 6 выйдет, а вы еще в PHP4 стиле пишите. А за обзор спасибо.
                                    0
                                    К сожалению есть пару старых клиентов, которые никак не обновятся :(
                                      0
                                      Документацию класса и методов в PHPDoc или смежном формате никто не отменял :)
                                    –1
                                    Как не ухищряйся, а быстрее памяти не прыгнешь, так что все равно в чем хранить и как выбирать.
                                    Самое главное формат хранения адресов.
                                      +1
                                      Я бы предложил свести поиск к
                                      select country from table where table.ip>= '11.22.33.44' order by table.ip limit 1
                                      ну и кластерный индекс по ip, храинть конец диапазона.
                                        0
                                        ну и ip хранить и сравнивать как число для нормализации
                                          0
                                          «нормализация» — не в смысле «нормальной модели»
                                        0
                                        А с PostgreSQL не пробовали тесты проводить? У них есть расширение ip4r, которое достаточно шустро ищет по IP.
                                          0
                                          С PostgreSQL не работал, потому не пробовал :(
                                            0
                                            А расскажите, как вы тестировали, тогда я попробую PostgreSQL потестить.
                                          0
                                          Кстати, на СИ скорость определения более 1 миллиона адресов в секунду на одном потоке. И всё дело в первую очередь в том, что база двоичная и закеширована в памяти. По этому проще наверное использовать спец модули для php/apache/nginx которые будут кешировать базу и реализовывать алгоритм поиска.
                                            0
                                            Это Вы про GeoIP? Никто не спорит, что СИ быстрее PHP, но скорее всего SxGeo будет быстрее если его портировать на СИ, алгоритм более быстрый, да и база меньше, т.е. с меньшим объемом данных работать нужно.
                                            +1
                                            Сам не так давно возился с базами геотаргетинга.
                                            Было бы интересно, если бы автор рассказал про свой формат бинарной базы.
                                            Когда я пытался воссоздать бинарную базу GeoIPCity, то смог сделать это по алгоритму с этой страницы Compressing dictionaries with a DAWG.

                                            Ну а про скорость, php — странное решение в вопросах скорости. У того же MaxMind есть C-API и доступ к нему из PHP. Я вот взял питоновскую обёртку вокруг С-API и получил скорость 310 000 запросов в секунду, а ваш тест 50 000.

                                            P.S. Кстати у вас ошибка в sxgeo_bench.php, неправильно указано имя класса
                                              +1
                                              О формате еще напишу, если будет многим интересно. В дальнейшем также думаю выложу скрипт для конвертирования, пока сыроват, и еще в доработке.

                                              Что касается PHP, это просто мой основной язык, потому на нем привычнее. Но думаю потом вполне можно портировать на другие языки, алгоритм довольно простой.

                                              P.S. Спасибо ошибку поправил.
                                                0
                                                нас (wipmania) много спрашивали базу в бинарном формате, но сделать свой — руки не доходили, можно ли пользовать ваш? Тогда следующие базы выложим дополнительно и в этом виде.
                                                У нас есть база в бинарном виде, но она для модуля geoip из xtables-addons для iptables.
                                                  0
                                                  Да конечно, в планах популяризировать формат.
                                                  Вот конвертнул базу WIPmania в формат SxGeo можно потестить.
                                              0
                                              Автору однозначно плюс за то, что поделился создал качественный продукт и поделился им.
                                              Скажите, пожалуйста, а как можно сконвертировать базу от MaxMind, например, в формат вашей базы? В статье не нашёл. Спасибо
                                                0
                                                Пока, что скрипт конвертилки не выкладываю в открытый доступ, т.к. еще планируется доделать работу с базами для городов, и он в очень девелоперском состоянии, стрёмно выглядит. Если интересно могу сгенерить бинарник по любой другой базе, кидайте в личку.
                                                  0
                                                  А вообще планируете выкладывать? Базы то постоянно обновляются. Каждый раз не хотелось бы вас дёргать :)
                                                    +2
                                                    Да планирую выложить как закончу с городами. В принципе процесс можно автоматизировать, чтобы кроном проверялось обновление и генерился файл.
                                                    Вообще в TODO еще над системой обновления поработать, чтобы не нужно было в ручную дергать, в теории файл, должен отлично обновляться по частям.
                                                      0
                                                      Отлично! Спасибо
                                                    0
                                                    Стрёмно выглядит — выкладывайте на гитхаб и принимайте пулл-реквесты. Социальное программирование оно такое :-)

                                                    (Не иронизирую, на самом деле так и есть — чем раньше продукт в любом состоянии появится на публике — тем раньше получит полезный фидбэк)
                                                  0
                                                  нужна версия на С и перекодировщик из других форматов в ваш, иначе в реально высоконагруженом проекте не имеет применения
                                                    0
                                                    совершенно некорректно сравнивать БД и специальные программные средства — работающие в памяти
                                                    для начала посмотрите как именно работает between в MySQL, в статье как раз показано как делать такое на БД
                                                    habrahabr.ru/blogs/mysql/125467/
                                                    извинияюсь что ссылка на мой пост — зато на хабр.
                                                    А второе засуньте все индексы и таблицы в кэш и посмотрите на скорость — поверьте она вас приятно удивит (10000/sec для MySQL это далеко не предел)
                                                      0
                                                      Почему это сравнивать некорректно? Я сравнивал решения, которые часто встречаются в инете и применяют для одной и той же задачи. У меня не стояло задачи добиться максимальной производительности каждого из вариантов.
                                                        0
                                                        В таком случае извиняюсь, просто судя по тому, что что в MySQL вы использовали три конструкции для поиска я было решил, что вы ищете наиболее быстрый вариант для работы с БД.
                                                          0
                                                          Попробую протестить еще Ваш вариант на днях. Просто у Вас же еще тестирование было внутри MySQL, при тестировании из PHP или другого клиента будет скорость явно ниже.
                                                      +2
                                                      Из спортивного интереса набросал тест, делающий выборку стран по случайным IP из массива в памяти — получилось около 10000000 IP/sec. Подозреваю, что основное влияние на результаты оказал собственно скрипт тестирования.
                                                        0
                                                        На каком языке этот тест был? Железо же тоже отличается.
                                                          0
                                                          Delphi 7, Core i3 2120 в одном потоке. Это не php, но раз тестируется скорость — полезно для понимания результатов. Было интересно почему такие маленькие цифры.
                                                            0
                                                            Ну это уже видимо издержки скриптовых языков с динамической типизацией, ради интереса попробовал загнать базу полностью в массив, прибавка получилась 20%, но и памяти больше жрёт.
                                                            Интересно будет портировать SxGeo, на какой-нибудь компилируемый язык.
                                                              0
                                                              Кого сейчас волнует 10 Мб памяти? К тому же скорость никому кроме анализаторов логов не особо нужна.
                                                                0
                                                                Ну к примеру, на тестовом серваке, как раз скрипт уперся в ограничения памяти, пришлось поменьше сделать тестовых айпишек, когда я попытался просто загнать базу в массив.
                                                                А что будет когда база будет для городов, там уже речь о 2,5-3 млн. диапазонов.
                                                          0
                                                          Если я не ошибаюсь, то автор определяет по диапазону ip, а вы по ключу в массиве, что конечно быстрей, но вам придется тогда хранить вообще все ip в этом массиве, что лишено смысла.
                                                            0
                                                            Я взял ту же самую базу GeoLite Country. Дают IP — возвращаю страну, город тоже попробовал — там база побольше, немножко медленнее выходит.
                                                            Все хранить не надо, только концы диапазонов. Следующий начинается там, где предыдущий закончился. Поиск дихотомией. Можно и пооптимизировать если захочется.
                                                              0
                                                              Если оптимизировать и поменять комплятор на понимающий инлайны, будет 150 000 000 IP/sec с одного треда.
                                                              И да, мне самому интересно куда столько девать ;)
                                                          +2
                                                          Понравилась статья, все подробно описано, а главное, т.е. вывод — просто няшка, ничего не навязывается, а хочется сразу взять и даже не тестировать, а уже использовать.
                                                            0
                                                            Конечно понятно, что чем больше IP/сек, тем меньше ресурсов сервера используется. Но меня интересует вопрос с другой стороны. Сколько IP/сек может потребоваться огромному сайту (гугл, фэйсбук или что-то такое)? Ведь для каждого пользователя это нужно определить только в редких случаях. Например, при первом входе, на этапе регистрации или при смене его места нахождения (гео-сервисы). Как мне кажется, этот максимум получится не таким и большим, например, 10 000 IP/сек
                                                              0
                                                              zapimir, а Вы можете выложить скрипт-конвертер GeoIP->SxGeo? Хочется иметь возможность переодически «освежать» базу, не завися от графика выхода новых версий.
                                                                0
                                                                Доделаю расширением формата, чтобы была поддержка городов, после чего выложу скрипт для конвертирования в формат SxGeo. Также планируется выпуск модулей для Apache и nginx.
                                                                0
                                                                Порадовала молниеносная скорость даже на SXGEO_FILE.

                                                                Теперь о пожеланиях: хотелось бы видеть список стран в виде полного названия, а не только двухбуквенного кода. Вообщем-то, можно добавить файл с определением массива, вроде такого $sxgeoCountries = array('EN' => 'England', 'RU' => 'Russia', 'US' => 'United States'). На этой странице было бы неплохо повесить черные tooltip'ы на флажки: driversworld.us/app/user/profile/?id=1
                                                                  0
                                                                  А что на счёт mod_geoip?

                                                                  На сайте есть сравнение, если я правильно понял, то разница между PHP и C API колоссальная www.maxmind.com/app/benchmark

                                                                  И ещё такой момент, у вас используется небольшая GeoIP Country. Работают ли ваши графики так же со 150-мегабайтной базой GeoIPOrg?
                                                                    0
                                                                    На тему скорости определения принадлежности IP к некоему IP range пробегал в интернетах замечательный пост: nponeccop.livejournal.com/225261.html

                                                                    и вдогонку к нему: swizard.livejournal.com/177303.html
                                                                      0
                                                                      Определять то надо не 10000 ip за один запуск. А по 1 ip 10000 раз! А это значит открывать и считывать файл не 1 раз а 10,000. И соответственно совсем другие цифры.
                                                                      Решение на С держит все в памяти (т.к. работает в фоне). Скрипт же на РНР вынужден считывать каждый раз заново.
                                                                      Хотя за скрипт в целом большое спасибо!
                                                                        0
                                                                        Тут тестировались алгоритмы, так что пропорции на других языках будут близкими, ничто не мешает мой алгоритм переписать на Си.
                                                                        Тут так же как с алгоритмами поиска, поиск перебором, будет всегда медленнее бинарного поиска, независимо от языка.
                                                                          0
                                                                          спасибо за минус в карму.
                                                                          все таки на хабре в основном душевно больные люди с комплексами
                                                                            0
                                                                            С чего это мне минусы Вам ставить, еще и в карму? Не страдаю подобным.
                                                                              0
                                                                              Извините, я редко тут чтото пишу, но именно после комментария к вашему топику карма упала еще на один пункт.

                                                                              По сабжу — реально не хватает скрипта для перевода MindMax в ваш формат. Уже подумываю о том чтобы переписать ваш алгоритм как РНР-расширение
                                                                                0
                                                                                Скрипт для конвертации будет, как и подробное описание формата. Формат будет открытым и универсальным (универсальным в том плане, что можно свои данные хранить в БД).

                                                                                Я уже сделал новую версию с поддержкой как стран, так и городов, работает в 4-5 раз быстрее GeoIP.

                                                                                Сейчас готовлю к запуску сайт (на этой неделе запустим), на котором будет и документация, и свежие базы данных. В том числе будет собственная БД, так как в GeoLite City куча мусора и неточностей.

                                                                                Так что пока с PHP-расширением лучше подождать.

                                                                                P.S. Карму плюсанул ;)
                                                                                  0
                                                                                  Спасибо! Не за карму конечно, а за работу над вашим проектом. Сейчас пишем баннерку, скорее всего в ней придётся использовать ваше решение т.к. нагрузки будут большие.

                                                                                  PS до вчерашнего дня у нас использовались запросы с where ip_min={$ip}
                                                                                  — до прочтения этого топика не задумывался о том какие они на самом деле тормозные)

                                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                      Самое читаемое