Pull to refresh

Comments 38

Почему нельзя задать диапазоны в базе 10-значным числом и просто делать поиск по диапазону?

И такой метод имеет право на жизнь:

CREATE INDEX ON phonecodes USING gist(int8range((code || numb)::bigint, (code || nume)::bigint, '[]'));

SELECT
  *
FROM
  phonecodes
WHERE
  int8range((code || numb)::bigint, (code || nume)::bigint, '[]') @> '[4852262000, 4852262000]'::int8range;

Правда, с теми же номерами с «цифрой A» обломится.

У вас АТС прямо в базу ходит? Отрезать что-то не проблема, думаю.
Опять же, зачем все так сложно?
Вы все-равно парсите номера скриптом, можно же склеить префикс и диапазон в одно число в момент загрузки данных в базу. В итоге будет два числа границ диапазона и обычный поиск по дереву where phone >= numb and phone <= nume.

Какой «обычный поиск по дереву» позволяет сделать val BETWEEN col1 AND col2, если имеется в виду btree?

Да, тут я ступил. Ну сразу делать колонку int8range.
Или, с учетом того, что диапазоны у нас не пересекаются, просто nume >= phone order by nume limit 1 и потом уже проверить на numb эту одну строку.

А вот не факт, что они не пересекаются. Тут надо проверять конкретную выгрузку, а то бывало.

Правда, с обратной задачей так уже не получится.

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

Добавил в статью, спасибо за идеи.
Зачем так сложно? Берем любой kv и заливаем все коды в него.
378 тысяч ключей это ерунда.
Допустим, залили — а дальше что? Искать-то какими алгоритмами?
Запрос от всего номера, запрос от номера минус последняя цифра и так далее пока не найдется. Номер предварительно приведен к стандартному виду.
10 запросов на звонок в худшем случае. Ерунда.
Если нужны миллисекунды паралелим. Тут чуть сложнее, но тоже тривиально все.

В качестве бонуса в тот же kv кладем всех клиентов. Если у вас их сколь либо адекватное количество работать отлично будет.
Так чем это отличается от описанного в статье, кроме использования kv вместо sql?

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

Вы точно посмотрели приведенный SQL-запрос?
substr(pfx, 1, length(pfx) - 1) -- "отщипываем" последнюю цифру

Это же ровно тот же алгоритм:
запрос от номера минус последняя цифра и так далее пока не найдется

Сложность это не только конечный код. Сложность это все от тикета с задачей до готового кода.


В моем варианте негде ошибаться, понятно как писать тесты, понятно что с производительностью, производительность не надо оптимизировать, да и сама задача джуну на день написать код, и мне 10 минут на написать тикет.
У вас все гораздо сложнее.

Что сложнее написать — один SQL-запрос или кусок кода на бизнес-логике, который сделает то же самое? Зависит от навыков исполнителя.

Точно ли не надо оптимизировать производительность? Поставим какой-нибудь Redis на площадке в Мск и будем делать к нему последовательно те самые 10 запросов из Владивостока, получая на каждом задержку до 150мс… Делать параллельно? Так это же уже оптимизация, и не для всякого джуна.

Это не имеет никакого отношения к теме статьи, а исключительно холивар SQL vs NoSQL. Есть задачи, где выигрывает NoSQL, есть — где SQL, есть где они равноприменимы, как тут.

У вас абстракции протекли.


Возможность без спец ухищрений сделать базу в 150мс от бека это ошибка другого уровня. Ее тоже надо исправлять, но к этой задаче это не имеет отношения.

Странно, что не имеет — мне казалось, что мы обсуждаем…
Мое решение описывается двумя предложениями, реализуется джуном за день.
В моем варианте негде ошибаться, понятно как писать тесты, понятно что с производительностью, производительность не надо оптимизировать, да и сама задача джуну на день написать код, и мне 10 минут на написать тикет.

А оказывается, что именно решение, как цельный законченный функционал — не работает!
150мс от бека это ошибка другого уровня. Ее тоже надо исправлять
Ну так и где оно «проще», если там нужно еще дополнительную кучу граничных условий учитывать? То есть это заведомо не «постановка за 10 минут».

За 10. Исходим из разумных предположений. Предположить что небольшая kv база рядом с бекендом разумно. Предполагать что она через океан неразумно.


Исправление ошибок архитектуры, а kv база в 150мс от бекенда это ошибка архитектуры, никак не связано с разработкой бизнес логики. Тут же чистая разработка бизнес логики.
Смешивать такие вещи это и есть протекающие абстракции.

Продолжу мысль:
— делать 10 запросов к БД вместо 1 — ошибка архитектуры приложения, поскольку увеличивает нагрузку на нее, плюс лишний трафик, плюс задержки
— делать запросы последовательно — тоже ошибка архитектуры
— взять KV без поддержки префиксного поиска — туда же

Ими мы не занимаемся. Поэтому к концу дня приходит гордый джун: "Смотри, я сделал ничего. Оно идеально [не] работает — все тесты это показывают. Но у меня нет ни одной ошибки! Зато вот в архитектуре — сплошные..."

И таки он прав — это не его ошибки, а того, кто его отправил неправильной дорогой за те самые 10 минут — если под «джуном» мы понимаем обычного простого кодера, а не того, кто стремится вырасти до «сеньора».

10, да и любое другое разумное фиксированное число обращений к kv это правильно и хорошо. Kv созданы для такой нагрузки и хорошо умеют ее обрабатывать. Это типовой паттерн использования.


За все фичи, в том числе префиксные поиски, надо платить. Как правило это не имеет смысла. У нас типичный О(1) с разумной константой. Смысл что-то там городить?


Если джун нашел ошибки архитектуры на уровне планирования ДЦ баз и бекендов то надо что-то в консерватории менять. И это что-то никак не связано с реализацией бизнес логики.

У нас типичный О(1) с разумной константой. Смысл что-то там городить?
Вы вторую часть статьи про префиксный поиск по reverse точно прочитали?

Если джун не смог нормально решить поставленную задачу указанными инструментами, то не факт, что проблема в нем или в архитектуре. Возможно, она в том, кто ему указал решать эту задачу именно этими инструментами.

Я это и называю жутким переусложнением.
Задача решается однострочником. С нормальным и предсказуемым быстродействием. При этом не делается ни одного неразумного предположения о проекте или инфраструктуре.
Ну что еще надо?


IntStream.range(0, str.length()).mapToObj(i -> str.substring(0, str.length() - i)).parallel().map(this::redisReq).filter(Objects::nonNull).findFirst();
А теперь давайте посмотрим, во что вырождается попытка таким же способом реализовать префиксный поиск на том же Redis:
oldblog.antirez.com/post/autocomplete-with-redis.html

И сравните с краткостью и выразительностью CREATE INDEX (varchar_pattern_ops) / SELECT ... LIKE.
Именно поэтому так делать не надо.
Творить из хорошей kv плохую «любую другую» базу не надо.
Надо пользоваться преимущесвами kv. Она держит безумный рпс на смешном железе. И при этом очень быстро работает. Надо этим пользоваться.
Если «так делать не надо» (от самого автора Redis), но «мое решение… реализуется джуном за день» — есть какой-то магический способ «как надо»?

Или все-таки все зависит от задачи?
Это не имеет никакого отношения к теме статьи, а исключительно холивар SQL vs NoSQL. Есть задачи, где выигрывает NoSQL, есть — где SQL, есть где они равноприменимы
Я чуть выше написал однострочник. Который используя плюсы kv делает что надо.
День на перекладывание джейсонов, тесты и прочий бойлерплейт.

Конечно зависит. sql великолепен. Но тут он не нужен.
Равноприменимы — не верю. Слишком оно разное. Использование не того что нужно для конкретной задачи вызывает как минимум усложнение кода. Где код и проектирование получаются проще, то и надо использовать.

Ваш пример это отлично иллюстрирует. Варианты реализации, профайлеры, планы запросов. Море всего. У меня же одна строка. Понятно как она работает, понятно быстродействие, понятны ограничения. Не надо вообще ничего больше делать.

Общие ограничения любой БД знать полезно. Чтобы случайно ужаса не сделать.

Изивините, но ваше решение выглядит сложнее в коде, чем простой sql заропс с поиском по специально предназначенному для таких целей индексу. Редис тут имеет смысл использовать только если для нас важно время ответа. Про rps я бы не был столь уверен, было бы интересно потестировать… равно как и потребление памяти.

За все фичи, в том числе префиксные поиски, надо платить. Как правило это не имеет смысла. У нас типичный О(1) с разумной константой. Смысл что-то там городить?

Если чо, то вы просто сделали префиксный поиск на редисе со сложностью O(n).

Ну да, а потом условный джун внезапно узнает, что ключи в данной kv по префиксу не ищутся, ибо там хешмап.

У него тесты не сработают. Он пойдет почитает доку и все сделает правильно.
Для того и пишем интеграционные тесты.

Ага, и сделает поиск по маске в редисе, например ;)

Код ревью для кого придумали?


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

И что ревьювер в данном кейсе предложит сделать?

Предложит переписать код так чтобы не искать по маске? Можно приложить ссылочку где описано что так не оптимально делать.

Потом окажется, что искать на редисе по префиксу не так и просто, что нужно или сложную структуру придумывать или дублировать ключи, что увеличит объем данных раз 10. Что нужно искать другое nosql решение, которое хотя бы умеет btree, что бы искать по префиксу… и внезапно придете к тем же запросам, что и в статье, только может не на sql. Вы все еще считаете, что nosql решение сильно "элементарнее", чем написать один sql запрос на постгресе, который давно работает, проверен, по нему хорошая экспертиза?

Все бы хорошо, только исходные данные у вас не актуальные!
Актуальные данные мобильных операторов нужно брать здесь: zniis.ru/bdpn/check
Я так понимаю, это только по перенесенным мобильным номерам? С точки зрения определения региона, это ничего не меняет, но за ссылку спасибо.
Sign up to leave a comment.