Comments 51
>>Если обсудить недостатки этого метода, то они таковы:
Возможны проблемы с производительностью при относительно высокой задержке.
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.
более наркоманского ничего не придумали ещё?
> решений просто море
ох уж эти моряки :)
выборка по уникальному столбцу не стоит ничего
Ну да. Установить соединение, отправить запрос, сервер его обработает, дождаться ответа - это всё, конечно, совершенно бесплатно. Да, на фоне сетевого обмена непосредственно выполнение запроса - может, и спички, но не стОит забывать и выделение сервером памяти, и работу планировщика-оптимизатора, и запись статистики. По совокупности - набежит.
>> Никто для этого SELECT не использует.
Есть системы, которые показывают уникальность имени еще до попытки сохранения - сразу по мере ввода пользователем. Но и в этом случае разумность применения фильтров Блума надо доказывать.
Констрейнт обязателен, но не решает бизнес-задачу: создание пользователя обычно подтверждается некоей кнопкой "сохранить" (которая отправляет на сервер не только логин), а что имя занято, хотят видеть ещё до ее нажатия
что имя занято, хотят видеть ещё до ее нажатия
Довольно редкая бизнес-задача. Потому что это динамический поиск в процессе ввода... в худшем случае - на каждое нажатие клавиши, в лучшем - на события ухода фокуса с поля ввода. И в этом случае действительно без отдельных запросов на существование значения в поле ввода не обойтись.
Но обычно такого никто не делает - во всяком случае, навскидку вспомнить продукты с таким функционалом как-то не получается.
Ну можно бахнуть на каждое нажатие клавиши и добавить адекватный дебаунс, так что не так все страшно, но вопрос- зачем- всё ещё остаётся. Выглядит как довольно редкий кейс.
Github
Да, не забываем про debounce
На каждое изменение – как пример, выбор не занятого имени пользователя в почтовом сервисе, Телеграмме, ГитХабе, ВК или другом месте, где пользователь сам может выбрать себе никнейм.
а может сразу попытаться добавить пользователя в базу данных? Пусть "она" думает за уникальность. И с многопоточностью проблем уже не будет
Ну вменяемые разработчики именно так и делают. Если удастся отбрыкаться от невменяемого заказчика с его "но для очень уважаемых людей дубликаты должны быть разрешены".
Мда... я ничего не смыслю в SQL, но знаю Redis, поэтому я заплюю первое и восхвалю второе. Ну а то, что про SQL я по незнанию напишу явную глупость, мне простят...
Стыдно должно быть за подобные публикации.
вот почему в одной игре постоянно на релизе аддона очереди ))) потомучто пока залогинится, пока сверится и прочее и прочее )
В базе же по-любому будет btree-индекс для гарантии уникальности имен. Зачем еще дополнительно bloom-индекс содержать? И откуда уверенность, что bloom в Редисе + контроль ложноположительных срабатываний по БД дадут меньшую нагрузку, чем простая безусловная проверка по btree в БД?
Ну, серьёзно, в веб-разработке столько интересных и настоящих задач, помимо проверки уникальности имени пользователя НАСТОЛЬКО надуманным, время- и денежно- затратным способом😅
Время - это деньги, новые фичи и прочая-прочая, неужели заняться нечем? Чем не устраивает использование уникальных индексов, кэширование, хэш-таблицы, в конце концов?
Разработка - это всегда поиск баланса между качеством-стоимостью-сроками, модуль регистрации пользователей идёт "в коробке" во многих фреймворках, и заказчик вообще не платит за этот, уже готовый функционал, а разрабы - не изобретают колесо.
Надеюсь, на практике, SSP-soft не стал бы впаривать заказчику этот велосипед 🙂
в веб-разработке столько интересных и настоящих задач
Няп это вопрос для собеседования. А задачу под это требование, обрабатывать много новых регистраций, придумать можно. Перепись населения, например;)
я бы не брал в команду человека, который на собеседовании ответит подобным образом (как в статье).
опыт в разработке, кругозор в технологиях, умение писать приемлемый код - дело наживное, но есть надёжные признаки профнепригодности аналитика и разработчика
отсутствие здравого смысла
непонимание применимости интструментов и цены их использования в проекте
Если у вас вдруг есть юзернеймы, то почти всегда почти точно будет какой-то функционал, который потребует по этим самым юзернеймам находить саму сущность пользователя в базе.
Если вас не атакуют, то почти всегда почти точно количество регистраций будет пренебрежимо мало по сравнению с числом легальных действий. Банально потому что пользователь регистрируется лишь однажды, а пользуется приложением часто.
Отсюда простой вывод — у вас уже должен быть 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: ты не уникален.
С фильтром блюма прям "отличное" решение:
если фильтр "сказал", что имя занято - лезем в базу, что бы перепроверить;
если "сказал", что свободно - лезем в базу что бы добавить пользователя.
Зачем тогда этот блюм, если в любом случае лезем в базу? Проще сразу в базу посылать 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. Здравствуйте, дубли.
Так что без серверного констрейнта, вероятности его срабатывания, перехвата соответствующего исключения и вменяемой реакции на клиенте все равно не обойтись. А вот для предварительного контроля по каждому введенному символу и быстрого отлупа - кеширования действительно могут быть хороши, но лишь как дополнение.
Вопрос на техинтервью аналитика и разработчика: “Назовите способы проверки username на уникальность”