Search
Write a publication
Pull to refresh

Comments 51

>>Если обсудить недостатки этого метода, то они таковы:

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

Common, Это сколько же новых пользователей вы собираетесь в секунду привлекать?

>> Частое выполнение запросов SELECT для проверки уникальности имен пользователей, и каждый запрос потребляет ресурсы базы данных, включая ресурсы процессора и ввода-вывода. Иными словами, нагрузка на базу данных м.б. чрезмерна.

Никто для этого SELECT не использует. Добавьте CONSTRAINT - и вы получите эксепшн, что запись не уникальна, а т.к для этого используется индекс, что чтение будет ничтожно.

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

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

И если вы будете использовать клиент, как точку контроля уникальности, а таких клиентов будет несколько - вот здесь вас ждет развлечение.

Добавьте CONSTRAINT - и вы получите эксепшн, что запись не уникальна

если там 2+ уникальных поля (username, email, какой нить uuid) вы станете текст ошибки бд парсить, чтобы понять че именно не так и вывести пользователю текст ошибки?

во приколист.

выборка по уникальному столбцу не стоит ничего, нафига сразу руины строить чтоб на спичках сэкономить

Справедливости ради, парсить не надо. Например, в postgres это можно понять по определенному коду ошибки (23505) + имени констрейнта, эта инфа есть в эксепшене. Согласен, что проверка уникальности с select будет дешевой по меркам СУБД, но подход с определением нарушения констрейнта пост-фактум тоже имеет место быть в некоторых ситуациях

У тебя всегда будет имя констреинта, который сработал
Ну и кто тебе мешает использовать Use of Detailed Error Handling в самом SQL?
Опять жеж в разных движках баз данных бывают разные синтетические сахара, для примера PostgreSQL есть ON CONFLICT
решений просто море

ну иди и пробрасывай теперь в контроллеры имена констрейнтов и маппинги `constraint` <-> `column_name` , чтобы вывести пользователю внятную ошибку, что юзернейм занят, а не email.

более наркоманского ничего не придумали ещё?

> решений просто море

ох уж эти моряки :)

А в чем проблема? У вас. Есть DAL для преобразования этого в нормальный эксепшн например. Дальше делайте с ним что хотите.

UFO landed and left these words here
UFO landed and left these words here

выборка по уникальному столбцу не стоит ничего

Ну да. Установить соединение, отправить запрос, сервер его обработает, дождаться ответа - это всё, конечно, совершенно бесплатно. Да, на фоне сетевого обмена непосредственно выполнение запроса - может, и спички, но не стОит забывать и выделение сервером памяти, и работу планировщика-оптимизатора, и запись статистики. По совокупности - набежит.

>> Никто для этого SELECT не использует.

Есть системы, которые показывают уникальность имени еще до попытки сохранения - сразу по мере ввода пользователем. Но и в этом случае разумность применения фильтров Блума надо доказывать.

Констрейнт обязателен, но не решает бизнес-задачу: создание пользователя обычно подтверждается некоей кнопкой "сохранить" (которая отправляет на сервер не только логин), а что имя занято, хотят видеть ещё до ее нажатия

что имя занято, хотят видеть ещё до ее нажатия

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

Но обычно такого никто не делает - во всяком случае, навскидку вспомнить продукты с таким функционалом как-то не получается.

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

На каждое изменение – как пример, выбор не занятого имени пользователя в почтовом сервисе, Телеграмме, ГитХабе, ВК или другом месте, где пользователь сам может выбрать себе никнейм.

а может сразу попытаться добавить пользователя в базу данных? Пусть "она" думает за уникальность. И с многопоточностью проблем уже не будет

Ну вменяемые разработчики именно так и делают. Если удастся отбрыкаться от невменяемого заказчика с его "но для очень уважаемых людей дубликаты должны быть разрешены".

Для особых людей стоит выделять отдельную таблицу где они будут храниться, что бы не нарушать этим схему. По крайней мере я бы так делал, ради этого ломать constraint таблицы юзеров как-то глупо что-ли ¯⁠\⁠_⁠(⁠ツ⁠)⁠_⁠/⁠¯

Мда... я ничего не смыслю в SQL, но знаю Redis, поэтому я заплюю первое и восхвалю второе. Ну а то, что про SQL я по незнанию напишу явную глупость, мне простят...

Стыдно должно быть за подобные публикации.

вот почему в одной игре постоянно на релизе аддона очереди ))) потомучто пока залогинится, пока сверится и прочее и прочее )

В базе же по-любому будет btree-индекс для гарантии уникальности имен. Зачем еще дополнительно bloom-индекс содержать? И откуда уверенность, что bloom в Редисе + контроль ложноположительных срабатываний по БД дадут меньшую нагрузку, чем простая безусловная проверка по btree в БД?

Ну, серьёзно, в веб-разработке столько интересных и настоящих задач, помимо проверки уникальности имени пользователя НАСТОЛЬКО надуманным, время- и денежно- затратным способом😅

Время - это деньги, новые фичи и прочая-прочая, неужели заняться нечем? Чем не устраивает использование уникальных индексов, кэширование, хэш-таблицы, в конце концов?

Разработка - это всегда поиск баланса между качеством-стоимостью-сроками, модуль регистрации пользователей идёт "в коробке" во многих фреймворках, и заказчик вообще не платит за этот, уже готовый функционал, а разрабы - не изобретают колесо.

Надеюсь, на практике, SSP-soft не стал бы впаривать заказчику этот велосипед 🙂

в веб-разработке столько интересных и настоящих задач

Няп это вопрос для собеседования. А задачу под это требование, обрабатывать много новых регистраций, придумать можно. Перепись населения, например;)

я бы не брал в команду человека, который на собеседовании ответит подобным образом (как в статье).

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

  1. отсутствие здравого смысла

  2. непонимание применимости интструментов и цены их использования в проекте

это же fizz-buzz а не техзадание;) Сразу можно переспросить, какие side effects в предложенном решении.

UFO landed and left these words here

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

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

Отсюда простой вывод — у вас уже должен быть general-purpose механизм поиска юзернейма в базе. Чертовски хорошо отлаженный, потому что от него зависит работа примерно всего. Который умеет как-то адекватно жить с ограничениями на количество подключений и решает множество других проблем. Возможно даже с кешированием.

Опять-таки, если вас не атакуют, то почти всегда почти точно юзернеймы будут только валидными. А поскольку регистрации составляют ничтожный объём от всей нагрузки — поддерживать только для них фильтр Блума будет накладно. Вы же помните, что всякий дополнительный индекс — ускоряет некоторые операции чтения, но замедляет все операции записи? Не считая того, что в данном случае он банально тратит ресурсы, увеличивает latency и раздувает общую сложность системы.

К слову о кешировании — почти точно ваш Redis не будет источником правды, а будет лишь кэшировать данные из чего-то более классического (и с более приятными гарантиями). И тут в полной мере встаёт вопрос «а как?». Если мы хотим не просто хранить список имён, то задача поддержания кэша в актуальном состоянии — чертовски нетривиальная с множеством разных компромиссов и тонкостей. Кажется немного опрометчивым советовать в статье для самых новичков добавить в систему кэширующий слой (особенно когда не все соки выжаты из БД).

Вот думаю смеяться или плакать... "Лишний select запрос делаем". Ну да у вас же в секунду 100к регистраций будет проводиться... Ещё в комментариях предлагали constrained. Ну тут 50 на 50 в зависимости от того какое приложение. Если новостной блог, то Окей. Если что-то более сложное, то выносить бизнес логику на инфраструктурный слой ошибка.

А потом тебя берут на работу, где 15 пользователей в месяц.

Особенно порадовал пример с миллиардом пользователей) Очень актуально)

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

Если в sql на поле username не навешан constraint, то я б порекомендовал добавить limit 1, чтоб остановить поиск при нахождении первого.

Если в sql на поле username не навешан constraint, то я б порекомендовал добавить limit 1, чтоб остановить поиск при нахождении первого.

LIMIT 1 обрабатывается не так. Сервер получает весь набор данных, а потом применяет LIMIT - так работает подавляющее большинство СУБД.

Тут надо использовать EXISTS - вот он и правда обрывает поиск сразу, как найдёт соответствующую условиям запись.

Насколько я знаю это только при наличии сортировки. Без order by будет вычитана однастрока

Возможно, не проверял. Но всё одно - угадайка. Тогда как в случае EXISTS гадать не требуется.

Никакой угадайки. Зачем базе в запросе с лимитом без сортировки загружать все строки?

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

Ага, а потом без констрейнта, но с лимит 1 всё же по какой-то причине (например, баг фронта с дублированием запросов на создание- ну, скажем, useEffect с напортаченными депсами) таки будет дубль в бд и будет веселая ловля шрединбага "а почему у юзера покупка не прошла/прошла бесплатно"?)

Я, наверное, отстал от жизни и трендов, но объясните кто-нибудь - какую проблему решают все эти танцы с бубном вместо лобового constraint unique в базе как источнике истины и обработке эксепшена вставки? Если мы констрейнт не сделали(а почему? что, настолько всё плохо с производительностью?), то риски того, что все эти проверки рано или поздно дадут сбой, все равно остаются и если реализуются- возможны очень неприятные баги. Если сделали- то так и так какая-то обработка эксепшена вставки не помешает(ну в лог хоть кинуть, да и просто правило хорошего тона), так почему бы и не сделать в коде отдельную "штатную" веточку для такого кейса? При этом количество запросов будет минимально возможное- ну то есть буквально ровно тот один, который нам по-любому делать для вставки. Не, ну я могу в теории придумать кейс, когда проверки до вставки могут иметь смысл - например, если мы хотим валидировать пользовательский ввод на уникальность ещё до сабмита формы, ну, тогда да. Но такое надо бы отдельно уточнить- и накладно, и с ТЗ информбезопасности могут быть вопросики.

Отличный пример того, насколько вопросы и задачи на собеседованиях иррелевантны реальной работе.

Считаю, что приведенные решения слишком примитивны. И ежу понятно, что ник "vasya" будет вводиться часто, а ник "akakiy.petrovych1918" - редко. Жизненно необходимо накрутить логику, которая будет рассчитывать матожидание частоты коллизий по каждому нику и кэшировать только те, которые вводятся чаще раза в минуту в среднем. Очевидно, что остальные не создадут проблемной нагрузки на сиквел. При этом кэша наверняка потребуется немного. Ну что, берете на работу?

Вообще задача выглядит примитивной...

Нафига уникальность username, когда идентифицировать надо по мылу или номеру телефона (аналогично при использовании oauth), а их пользователь не сочиняет новые при регистрации... Имхо.

С фильтром блюма прям "отличное" решение:

  • если фильтр "сказал", что имя занято - лезем в базу, что бы перепроверить;

  • если "сказал", что свободно - лезем в базу что бы добавить пользователя.

Зачем тогда этот блюм, если в любом случае лезем в базу? Проще сразу в базу посылать insert и обрабатывать ошибку уникальности, которую в любом случае надо делать.

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

Ну допустим может вы хотите делать проверку на уникальность во время ввода имени уже. Хотя конечно, если у вас наполеоновские планы, что бы все люди на земле зарегистрировались, то да. Может и проще сразу вставку сделать. Тоже думаю, если есть уникальный индекс на username, то база сама сначала поищет в индексе, не обращаясь к таблице. Имхо кэш не для того, что бы кэшировать 8 миллиардов пользовательских имён. Бред, сначала грузить редис запросом, потом ещё базу данных при вставке.

Короче, способа всего 2 - поиск в БД/Кэше или быстрый поиск в БД/Кэше, когда содержимое колонки загоняется в структуру типа Set или HashMap.. хэш функция которой выполняет роль 'фильтра Блума'. И не надо мудрить.

"cтруктура типа Set или HashMap"

Такая структура уже есть в базе данных - индекс. При этом базы данных стараются держать "горячие" индексы в оперативе.

индекс в бд - не обяхательно основан на хэше, но если он такой, то - да больше ничего и не нужно

Обычно это BTree.
Даа, поиск в нём - это O(log(n)) в отличии от hash-table. Но с отдельным "кэшем" с HashMap проблема в том как надёжно поддерживать её актуальность и не выдавать ложно-положительных результатов. Особенно в предложенных условиях с миллионами записей и скорее всего с несколькими экземплярами бекенда.

Если количество регистраций столь велико, что это нужно учитывать, то вам нужно сохранять желаемый username здесь же, потому что иначе вы будете объяснять пользователю что груздь. А ещё должны вернуть возможные варианты для его выбора, т.к. при значимости нагрузки от такой операции шансов придумать username самостоятельно почти нет.

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

При такой нагрузке, скорее всего это будет вообще какое-то api какого-нибудь sso

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

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

Хватит ананизмом заниматься, каждый решит задачу по мере своих знаний и возможностей железа, программ и средств в деньгах, вложенных в эту хрень. В mysql 100 баз данных создайте и в каждой по 10 таблиц содержащей не более 5000 записей с начальными 2 буквами имён,если у вас возможно имя из 1 буквы, то и такие создайте и запрос select в нужную таблицу, из кода в котором вы вручную алгоритмом пропишите свои индексы для доступа к нужным таблицам, сделают часть работы и далее движок базы данных, выдаст вам даже на слабом железе мгновенный результат.

Если уж некто предложит обеспечивать уникальность пользователя не UNIQUE-констрейнтом в базе, а на клиенте, через SELECT и логику "если еще нет - то вставляем", (не знаю, кто это может быть, но например в качестве провокации на собеседовании), то тут прежде всего нужно кричать о необходимости явной транзакции, с блокировкой таблицы от запроса SELECT и до вcтавки INSERT. И вот это действительно будет тяжело для системы.

Но без этого рано или поздно придут регистрироваться два юзера с новым, но одинаковым именем одновременно. Одновременно выполнят свои IF SELECT, одновременно получат добро, что в базе их нет, и далее одновременно пойдут на INSERT. Здравствуйте, дубли.

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

Sign up to leave a comment.