LINUX.ORG.RU

Какая БД подходит под частые выборки IP

 ,


1

4

Приветствую друзья! Подскажите, правильно ли мыслю в сторону NoSQL redis в следующей ситуации.

Есть база на mysql IP диапазона адресов:

A____________B________________BLOCK___

192.168.1.0___192.168.1.255___1_______

100.100.5.0___100.100.255.0___0_______

Некоторые диапазоны А-В сгруппированы по 3 и 4 октету и содержат более 255 адресов. Такой подход дал экономию в размере базы, но существенно увеличил время запроса.

Select выбирает ip в диапазоне от A до B. Столкнулись с тем, что частые выборки (в пике 15 запросов в секунду) тормозят сервер.

Начали присматриваться в сторону NoSQL redis без группировки адресов, планируется увеличить базу по объему, но значительно повысить скорость выборки.

Обновления в базе происходят регулярно, до 1000 update, delete в сутки.

Правильный ли подход в планировании задачи и поможет ли redis в увеличении количества select в данной ситуации?

Перемещено hobbit из general



Последнее исправление: morkovkin (всего исправлений: 5)
Ответ на: комментарий от iliyap

Хм, sqlite оказывается умеет делать табличные выражения без уродского select union select:

sqlite> explain query plan
with p (prefix) as (
    values
    (substr(:ip,1,6)),
    (substr(:ip,1,5)),
    (substr(:ip,1,4)),
    (substr(:ip,1,3)),
    (substr(:ip,1,2)),
    (substr(:ip,1,1))
)
select geoip.* from p cross join geoip on p.prefix=geoip.prefix
where :ip between lo and hi limit 1;

QUERY PLAN
|--CO-ROUTINE 6
|  `--SCAN 6 CONSTANT ROWS
|--SCAN SUBQUERY 6
`--SEARCH TABLE geoip USING INDEX geoip$prefix (prefix=?)
iliyap ★★★★★
()
Ответ на: комментарий от crutch_master

Какая разница в какую сторону проходить массив, главное чтобы порядок был

Не важно в какую сторону, важно что его придётся просматривать весь. Весь - потому что сортировка по min и сортировка по max - разные и мы никак не можем их взаимно использовать, кроме подсказок. Они совпадают только если нет пересечений (ну и тогда всё намного проще).

a>5 && b<17
1:[3,4], 2:[5,7], 3:[7,10], 4:[9,15], 5:[14,15], 6:[18, 35], 7:[47, 50]

Для b тебя потерялись два значения (15 и 4 на ключах 4 и 1). Но это не суть. Для наглядности поменяем исходные данные так:

1:[3,4], 2:[5,7], 3:[7,50], 4:[9,15], 5:[14,15], 6:[18, 35], 7:[47, 10]

получается

a: 5,7,9,14,18,47 (ключи: 2,3,4,5,6,7)
b: 15,15,10,7,4 (ключи 5,4,7,2,1)
А дальше хоть разворачивай b хоть не разворачивай, а параллельное его сканирование с a твоим способом уже невозможно - его надо сначала отсортировать в том же порядке. А делается это только с брутфорсом всей последовательности. Получить же список сразу по-нужному отсортированным тоже невозможно - потому что нативная сортировка возможно только по тому же параметру, по которому условие диапазона.

firkax ★★★★★
()
Ответ на: комментарий от morkovkin

‘IP’ >= A AND ‘ip’ <= B

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

etwrq ★★★★★
()
Ответ на: комментарий от morkovkin

Он про то, что они преобразованы в знаковый инт, а не в беззнаковый. В результате у тебя

127.255.255.255 = 0x7fffffff =  2147483647
128.0.0.0       = 0x80000000 = -2147483648

Т.е. 128.0.0.0 < 127.255.255.255. Но из-за специфики IPv4 (127.0.0.0/8 создаёт разрыв в адресном пространстве публичных адресов) в базе нет диапазонов, которые от этого страдают.

iliyap ★★★★★
()
Ответ на: комментарий от iliyap

я, скромно, предполагал что-то на уровне unsingned long[ long] и про несколько видов записи адресcации. ну и концептуально проще и быстрее сразу число с числом сравнвать. чем строку форматировать, распознавать, приводить к числам и уже потом сравнивать. тупо, оверхед в условных 16 коллбеков, и неизвестно сколько байткода из участвующих .so
*сорри что ipv6 привлёк, о нём и правда в тз ни слова не было..

etwrq ★★★★★
()
Последнее исправление: etwrq (всего исправлений: 1)
Ответ на: комментарий от morkovkin

Вот почитайте - полезно:

https://dev.maxmind.com/geoip/importing-databases/mysql

какие настройки базы

Не оптимизирована, по умолчанию

Ну и конечно требуется грамотная настройка СУБД. Читайте документацию по MySQL - у Oracle она замечательная.

P. S. Для того, чтобы максимально быстро находить нужные записи (просто двоичным поиском по индексу), придется избавляться от перекрытия диапазонов IP-шников в разных записях - ну т. е. усложнять логику UPDATE-кода.

vinvlad ★★
()
Ответ на: комментарий от crutch_master

Ну ты же сам должен понимать, что это несущественная придирка. Хорошо, пусть данные такие:

1:[7,8], 2:[5,9], 3:[3,10], 4:[9,15], 5:[14,15], 6:[18, 35], 7:[47, 50]

a: 5,7,9,14,18,47 (ключи: 2,1,4,5,6,7)
b: 15,15,10,7,4 (ключи 5,4,3,2,1) развёрнуто: 1,2,3,4,5

Повторю, проблема в том, что сортировка по min и сортировка по max для пересекающихся диапазонов в общем случае даёт разный порядок ключей. А для твоего мержа двух последовательностей он и там и там должен строго возрастать/убывать.

firkax ★★★★★
()

Схожую задачу, тоже про числовые диапазоны, решаем через связку postgresql+redis. В постгре лежит эталон, в редис ходят клиенты. В редисе две базы, одна активная, вторая «вчерашняя». Обновление данных синхронно поступает в постргю и в активную базу редиса. Раз в сутки «вчерашняя» база очищается, перезаливается из постгри и меняется местами с активной.

Данных по строкам не помню, но размер активной базы редиса примерно 5 гигов. На холодных тестах что-то в районе 100к рпс доставали.

l0stparadise ★★★★★
()
Ответ на: комментарий от firkax

Да, в редисе полная копия базы. В каком виде она там лежит - не знаю, я не разраб, доступа к коду нет.

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

l0stparadise ★★★★★
()
Ответ на: комментарий от lesopilorama

от проекта зависит конечно, но в 1GB влезть без мыла бывает тяжковато. как мининум для нормальной обработки хорошей энтерпрайзной нагрузки нужнен кластер из 3-4х нод t3 large. Какую нагрузку ОП собрался считать на дохлом VPS, даже без лоудбалансера, я вообще хз. А тут еще и база на одном с проектом инстансе запущена вангую. Что вообще-то харам

Unixson
()
Последнее исправление: Unixson (всего исправлений: 4)
Ответ на: комментарий от Unixson

Какую нагрузку ОП собрался считать на дохлом VPS,

Выглядит как кто-то начитался хабра или блогов корпораций. mysql/innodb держит тыщу RPS SELECT-ов примитивных из стартового поста на одном ядре самого задрипанного отстойного 386 проца обоссаного бомжами на помойке. Современный «дохный VPS за 300 руб/месяц» - это весьма мощно для указанной нагрузки.

lesopilorama
()
Ответ на: комментарий от lesopilorama

А если нагрузка внезапно вырастет? Как скейлить это все дело? А главное куда если гиг это потолок. Просто видимо действительно это у меня кубер головного мозга. У ОП-а вряд ли будет такая нагрузка

Unixson
()
Последнее исправление: Unixson (всего исправлений: 2)
Ответ на: комментарий от Unixson

А если нагрузка внезапно вырастет? Как скейлить это все дело? А главное куда если гиг это потолок. Просто видимо действительно это у меня кубер головного мозга. У ОП-а вряд ли будет такая нагрузка

Она у ТС не вырастет до таких объёмов. У него максимум 1 запрос в секунду наверное уже признак суперпопулярности ресурса.

Я бы советовал сначала сделать на одном обоссаном VDS всё это: nginx + mysql/madiadb с лимитом на память мегабайт в 200 (кеш сраниц) + php/go что там для бэкенда и померять производительность. Вангую, что будет летать и заддосить это можно будет только специально со злым умыслом.

lesopilorama
()

Правильный ли подход в планировании задачи и поможет ли redis в увеличении количества select в данной ситуации?

Поможет изучение матчасти:

Select выбирает ip в диапазоне от A до B. Столкнулись с тем, что частые выборки (в пике 15 запросов в секунду) тормозят сервер.

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

Маска подсети придумана не просто так а с целью быстрого определения принадлежности адреса в маршрутизаторах, где внезапно нет реляционных баз данных.

Вот пример программного определения:

contains("10.10.20.0/30", "10.10.20.3");
contains("10.10.20.0/30", "10.10.20.5");
contains("1::/64", "1::1");
contains("1::/64", "2::1");
contains("1::3-4:5-6", "1::4:5");       
contains("1-2::/64", "2::");
contains("bla", "foo");

static void contains(String network, String address) {
    IPAddressString one = new IPAddressString(network);
    IPAddressString two = new IPAddressString(address);
    System.out.println(one +  " contains " + two + " " + one.contains(two));
}

Вот библиотека: https://seancfoley.github.io/IPAddress/

alex0x08 ★★★
()