UUID затмевают идентификаторы с автоинкрементом, и это не близко
3 марта 2023 г.Еще в ноябре 2021 года, когда я начал создавать бэкэнд-архитектуру Bounty, я решил использовать UUID для всех первичных ключей в нашей базе данных postgres, обеспечивающей работу большинства текущих приложений. Результаты были замечательными.
Совсем недавно, в начале 2021 года, я работал над большой системой, связанной с деньгами. Система использовала целочисленные идентификаторы в RDMS, и я очень нервничал, видя все, что могло пойти не так…< /p>
Сначала недостатки.
На протяжении многих лет в школе людей учили использовать автоинкрементные идентификаторы BigInt
в качестве первичных ключей. Впервые я увидел что-то другое, когда впервые использовал Mongo в 2013 году. У них была система идентификаторов объектов, которая представляла собой случайную строку. Я считаю, что это было сделано для того, чтобы вы могли легко разбивать базу данных между экземплярами записи.
Если над одной и той же коллекцией работали несколько авторов, и им нужно было координировать свои действия, чтобы определить, кто будет увеличивать целое число для первичного ключа, это может быть непросто. В качестве решения этой проблемы была введена система случайных строковых идентификаторов объектов.
Но есть и недостатки.
UUID выглядит следующим образом: 7c82deda-9461-4128-af05-d8c3acd16c47
Очевидно, что оно больше, чем целое число, и для его случайного генерирования требуется некоторое время процессора. Вам также потребуется поле временной метки createAt
и проиндексируйте это поле, если вы хотите упорядочить по времени, тогда как с автоинкрементным целочисленным первичным ключом вы сможете просто сортировать по первичному ключ. Но помимо эффективности целочисленного PK, на этом преимущества, на мой взгляд, заканчиваются..
Преимущество в плане безопасности — отсутствие атак с увеличением идентификатора
Одной из наиболее распространенных уязвимостей в веб-приложениях является увеличение идентификатора объекта. Официально эта уязвимость называется «IDOR» — небезопасная прямая ссылка на объект.
Пример: вы смотрите в свой браузер и видите, что он говорит /customer/12345/invoice/23399430
, что произойдет, если вы измените это и нажмете /customer/12345/ инвойс/23399431
? Ну, а если ваш разработчик об этом не подумал, вы можете увидеть чужой счет.
Как насчет того, чтобы сделать это с UUID? Вы не можете. Пространство ключей слишком велико, чтобы угадать, какие другие могут существовать.
Эта уязвимость никогда не должна существовать, если у вас есть осторожные программисты (всегда следует проверять разрешения на доступ к объектам в API), но поверьте мне, она повсюду в современных веб-приложениях.
Преимущество в эксплуатации — никаких ошибок при копировании и вставке
У нас есть много клиентов, которые делают странные вещи и подают заявки в службу поддержки. Мы занимаемся платежами, поэтому всегда есть крайний случай.
Позвольте мне сказать вам, что ваша операционная команда копирует идентификаторы и делает вещи на ваших внутренних панелях управления, которые вы им сделали (у нас около 40 приложений для перенастройки 😂), и они будут делать ошибки, потому что они люди.
Вполне возможно совершить ошибку, если они перепутают значение userId
10001 со значением orderId
10001. Но если это UUID? Нет, они не могут перекрываться.
Вам потребуется 2,71 квинтиллиона поколений UUID, чтобы 1ccccb76-206f-4abc-930b-09ee47764874
столкнулся с тем же UUID в другой таблице.
Преимущества кода: ошибки разработчиков уменьшаются
Подобно тому, как ваша операционная команда БУДЕТ путать копирование и вставку идентификаторов из одной таблицы в другую, вы, разработчики, в какой-то момент будете делать то же самое. У вас будет функция с такой подписью: freezeBankAccount(bankAccountId: number)
В миллионе строк кода кто-то передаст ему userId
вместо bankAccountId
, и он заморозит банковский счет случайного пользователя, потому что основной ключевое пространство идентификатора ключа типа integer почти наверняка будет перекрываться между таблицами с именами User
и BankAccount
.
Что произойдет, если вы допустите эту ошибку с UUID?
Ничего, вы увидите ошибку в журналах после развертывания «BankAccount not found» в функции с именем freezeBankAccount
, потому что нет перекрытия. Вы определяете причину (в вызов функции был передан неправильный идентификатор) и вносите исправление.
Что, если бы вы использовали целочисленные идентификаторы и допустили эту ошибку?
Через две недели вы узнаете об этом, потому что было получено 100 запросов в службу поддержки от людей, не имеющих доступа к своим банковским счетам, и теперь вы столкнулись с серьезной проблемой очистки операций. В худшем случае в крупной организации она месяцами работает в производственной среде, пока вы об этом не узнаете, а теперь приходится иметь дело с массой негативных последствий.
Также опубликовано здесь.
Оригинал