Сообщение id integer vs uuid от 19.08.2022 14:27
Изменено 22.08.2022 20:01 vsb
id integer vs uuid
Наверное набивший оскомину вопрос, извините.
У каждой таблицы с данными должен быть синтетический ключ id. Ссылки на эту таблицу должны быть по этому ключу.
Два основных подхода:
1. Использовать целочисленный ключ (обычно 1, 2, 4, 8 байтов в зависимости от предполагаемого числа строк), который генерируется в виде глобально возрастающего значения.
2. Использовать 128-битный UUID.
2.1. Генерировать UUID случайным образом.
2.1. Генерировать UUID в виде локально возрастающей последовательности (она возрастает, но может в какой-то момент "переполняться").
2.2. Генерировать UUID в виде глобально возрастающей последовательности (для простоты можно считать, что левые 64 бита это timestamp, правые 64 бита случайны).
На что это влияет:
1. Чем меньше байтов, тем быстрей оно работает. Ну это, наверное, очевидно. И читать меньше и памяти меньше занимает и кешей меньше требует и индексы меньше, и промахов меньше. Разница между типичным 4-байтовым целым и 16-байтовым UUID в 4 раза. Понятно, что упереться в производительность в этом месте — надо постараться, с другой стороны вопрос про то, что будет в каждой таблице, так что какая-то доля процента производительности тут объективно будет.
2. Возрастающие значения в индекс ложатся гораздо быстрей, чем случайные. Возрастающие просто в конец узла B-дерева добавляются, изредка его немного перестраивая. Дерево компактное. Случайные значения летят в случайные узлы, дерево будет более "разбитое". Разница в производительности на больших объёмах вставок тоже объективно будет. Вариант 2.2 собственно только ради этого и придумывался.
3. Запросы order by id. Понятно, что обычно есть что-то вроде created_at, но всё равно удобно иметь возможность упорядочить строки в порядке создания, переиспользуя уже существующий индекс.
4. Читабельность. Ну тут целые числа однозначно приятней глазу, чем UUID.
5. Безопасность. В некоторых сценариях UUID хорош тем, что его нельзя перебирать. Обычно для таких сценариев делают отдельный столбец с UUID, если в базе id целочисленный, но иметь этот столбец уже готовым удобней. Также, когда внешние идентфикаторы всегда UUID, это в целом безопасней, т.к. про безопасность отдельных эндпоинтов не всегда люди думают, тут оно "нахаляву". Однако с вариантом 2.3 с безопасностью есть нюансы — утекает timestamp так или иначе.
6. Генерация ID на стороне клиента. Порой это бывает удобно. Я бы сказал, это вообще всегда удобно. Генерация ID на стороне сервера накладывает достаточно ощутимые ограничения на используемые протоколы и во многих случаях усложняет код.
7. Распределённая БД. К примеру у нас есть древовидная структура БД, когда в каждом филиале стоит отдельная БД (для возможности автономной работы) и данные уходят в центральную БД. В случае UUID всё выглядит гораздо проще. В случае id нужно думать чуть больше.
8. Совпадения id для разных сущностей. К примеру можно сделать join по неправильному полю. Если id не совпадают, запрос ничего не вернёт, что вероятно насторожит и не даст багу идти дальше. В противном случае может вернуться бессмысленный, но ненулевой результат.
Также есть проблема с реализацией вариантов 2.2 и 2.3, это не слишком часто используемые подходы и возможно придется использовать редкие библиотеки или писать самому. Это не великая проблема, но всё же.
Я сейчас работаю над best practices, которыми будем руководствоваться в будущем для создания сервисов. И нужно этот вопрос решить. Плюсы и минусы там и там есть. Скорей склоняюсь к UUID 2.3, т.к. считаю, что проблемы с производительностью будут малозаметны, а удобство читаемых id не перевешивает плюсов UUID. Заранее спасибо за конструктивные отзывы.
У каждой таблицы с данными должен быть синтетический ключ id. Ссылки на эту таблицу должны быть по этому ключу.
Два основных подхода:
1. Использовать целочисленный ключ (обычно 1, 2, 4, 8 байтов в зависимости от предполагаемого числа строк), который генерируется в виде глобально возрастающего значения.
2. Использовать 128-битный UUID.
2.1. Генерировать UUID случайным образом.
2.1. Генерировать UUID в виде локально возрастающей последовательности (она возрастает, но может в какой-то момент "переполняться").
2.2. Генерировать UUID в виде глобально возрастающей последовательности (для простоты можно считать, что левые 64 бита это timestamp, правые 64 бита случайны).
На что это влияет:
1. Чем меньше байтов, тем быстрей оно работает. Ну это, наверное, очевидно. И читать меньше и памяти меньше занимает и кешей меньше требует и индексы меньше, и промахов меньше. Разница между типичным 4-байтовым целым и 16-байтовым UUID в 4 раза. Понятно, что упереться в производительность в этом месте — надо постараться, с другой стороны вопрос про то, что будет в каждой таблице, так что какая-то доля процента производительности тут объективно будет.
2. Возрастающие значения в индекс ложатся гораздо быстрей, чем случайные. Возрастающие просто в конец узла B-дерева добавляются, изредка его немного перестраивая. Дерево компактное. Случайные значения летят в случайные узлы, дерево будет более "разбитое". Разница в производительности на больших объёмах вставок тоже объективно будет. Вариант 2.2 собственно только ради этого и придумывался.
3. Запросы order by id. Понятно, что обычно есть что-то вроде created_at, но всё равно удобно иметь возможность упорядочить строки в порядке создания, переиспользуя уже существующий индекс.
4. Читабельность. Ну тут целые числа однозначно приятней глазу, чем UUID.
5. Безопасность. В некоторых сценариях UUID хорош тем, что его нельзя перебирать. Обычно для таких сценариев делают отдельный столбец с UUID, если в базе id целочисленный, но иметь этот столбец уже готовым удобней. Также, когда внешние идентфикаторы всегда UUID, это в целом безопасней, т.к. про безопасность отдельных эндпоинтов не всегда люди думают, тут оно "нахаляву". Однако с вариантом 2.3 с безопасностью есть нюансы — утекает timestamp так или иначе.
6. Генерация ID на стороне клиента. Порой это бывает удобно. Я бы сказал, это вообще всегда удобно. Генерация ID на стороне сервера накладывает достаточно ощутимые ограничения на используемые протоколы и во многих случаях усложняет код.
7. Распределённая БД. К примеру у нас есть древовидная структура БД, когда в каждом филиале стоит отдельная БД (для возможности автономной работы) и данные уходят в центральную БД. В случае UUID всё выглядит гораздо проще. В случае id нужно думать чуть больше.
8. Совпадения id для разных сущностей. К примеру можно сделать join по неправильному полю. Если id не совпадают, запрос ничего не вернёт, что вероятно насторожит и не даст багу идти дальше. В противном случае может вернуться бессмысленный, но ненулевой результат.
Также есть проблема с реализацией вариантов 2.2 и 2.3, это не слишком часто используемые подходы и возможно придется использовать редкие библиотеки или писать самому. Это не великая проблема, но всё же.
Я сейчас работаю над best practices, которыми будем руководствоваться в будущем для создания сервисов. И нужно этот вопрос решить. Плюсы и минусы там и там есть. Скорей склоняюсь к UUID 2.3, т.к. считаю, что проблемы с производительностью будут малозаметны, а удобство читаемых id не перевешивает плюсов UUID. Заранее спасибо за конструктивные отзывы.
id integer vs uuid
Наверное набивший оскомину вопрос, извините.
У каждой таблицы с данными должен быть синтетический ключ id. Ссылки на эту таблицу должны быть по этому ключу.
Два основных подхода:
1. Использовать целочисленный ключ (обычно 1, 2, 4, 8 байтов в зависимости от предполагаемого числа строк), который генерируется в виде глобально возрастающего значения.
2. Использовать 128-битный UUID.
2.1. Генерировать UUID случайным образом.
2.2. Генерировать UUID в виде локально возрастающей последовательности (она возрастает, но может в какой-то момент "переполняться").
2.3. Генерировать UUID в виде глобально возрастающей последовательности (для простоты можно считать, что левые 64 бита это timestamp, правые 64 бита случайны).
На что это влияет:
1. Чем меньше байтов, тем быстрей оно работает. Ну это, наверное, очевидно. И читать меньше и памяти меньше занимает и кешей меньше требует и индексы меньше, и промахов меньше. Разница между типичным 4-байтовым целым и 16-байтовым UUID в 4 раза. Понятно, что упереться в производительность в этом месте — надо постараться, с другой стороны вопрос про то, что будет в каждой таблице, так что какая-то доля процента производительности тут объективно будет.
2. Возрастающие значения в индекс ложатся гораздо быстрей, чем случайные. Возрастающие просто в конец узла B-дерева добавляются, изредка его немного перестраивая. Дерево компактное. Случайные значения летят в случайные узлы, дерево будет более "разбитое". Разница в производительности на больших объёмах вставок тоже объективно будет. Вариант 2.2 собственно только ради этого и придумывался.
3. Запросы order by id. Понятно, что обычно есть что-то вроде created_at, но всё равно удобно иметь возможность упорядочить строки в порядке создания, переиспользуя уже существующий индекс.
4. Читабельность. Ну тут целые числа однозначно приятней глазу, чем UUID.
5. Безопасность. В некоторых сценариях UUID хорош тем, что его нельзя перебирать. Обычно для таких сценариев делают отдельный столбец с UUID, если в базе id целочисленный, но иметь этот столбец уже готовым удобней. Также, когда внешние идентфикаторы всегда UUID, это в целом безопасней, т.к. про безопасность отдельных эндпоинтов не всегда люди думают, тут оно "нахаляву". Однако с вариантом 2.3 с безопасностью есть нюансы — утекает timestamp так или иначе.
6. Генерация ID на стороне клиента. Порой это бывает удобно. Я бы сказал, это вообще всегда удобно. Генерация ID на стороне сервера накладывает достаточно ощутимые ограничения на используемые протоколы и во многих случаях усложняет код.
7. Распределённая БД. К примеру у нас есть древовидная структура БД, когда в каждом филиале стоит отдельная БД (для возможности автономной работы) и данные уходят в центральную БД. В случае UUID всё выглядит гораздо проще. В случае id нужно думать чуть больше.
8. Совпадения id для разных сущностей. К примеру можно сделать join по неправильному полю. Если id не совпадают, запрос ничего не вернёт, что вероятно насторожит и не даст багу идти дальше. В противном случае может вернуться бессмысленный, но ненулевой результат.
Также есть проблема с реализацией вариантов 2.2 и 2.3, это не слишком часто используемые подходы и возможно придется использовать редкие библиотеки или писать самому. Это не великая проблема, но всё же.
Я сейчас работаю над best practices, которыми будем руководствоваться в будущем для создания сервисов. И нужно этот вопрос решить. Плюсы и минусы там и там есть. Скорей склоняюсь к UUID 2.3, т.к. считаю, что проблемы с производительностью будут малозаметны, а удобство читаемых id не перевешивает плюсов UUID. Заранее спасибо за конструктивные отзывы.
У каждой таблицы с данными должен быть синтетический ключ id. Ссылки на эту таблицу должны быть по этому ключу.
Два основных подхода:
1. Использовать целочисленный ключ (обычно 1, 2, 4, 8 байтов в зависимости от предполагаемого числа строк), который генерируется в виде глобально возрастающего значения.
2. Использовать 128-битный UUID.
2.1. Генерировать UUID случайным образом.
2.2. Генерировать UUID в виде локально возрастающей последовательности (она возрастает, но может в какой-то момент "переполняться").
2.3. Генерировать UUID в виде глобально возрастающей последовательности (для простоты можно считать, что левые 64 бита это timestamp, правые 64 бита случайны).
На что это влияет:
1. Чем меньше байтов, тем быстрей оно работает. Ну это, наверное, очевидно. И читать меньше и памяти меньше занимает и кешей меньше требует и индексы меньше, и промахов меньше. Разница между типичным 4-байтовым целым и 16-байтовым UUID в 4 раза. Понятно, что упереться в производительность в этом месте — надо постараться, с другой стороны вопрос про то, что будет в каждой таблице, так что какая-то доля процента производительности тут объективно будет.
2. Возрастающие значения в индекс ложатся гораздо быстрей, чем случайные. Возрастающие просто в конец узла B-дерева добавляются, изредка его немного перестраивая. Дерево компактное. Случайные значения летят в случайные узлы, дерево будет более "разбитое". Разница в производительности на больших объёмах вставок тоже объективно будет. Вариант 2.2 собственно только ради этого и придумывался.
3. Запросы order by id. Понятно, что обычно есть что-то вроде created_at, но всё равно удобно иметь возможность упорядочить строки в порядке создания, переиспользуя уже существующий индекс.
4. Читабельность. Ну тут целые числа однозначно приятней глазу, чем UUID.
5. Безопасность. В некоторых сценариях UUID хорош тем, что его нельзя перебирать. Обычно для таких сценариев делают отдельный столбец с UUID, если в базе id целочисленный, но иметь этот столбец уже готовым удобней. Также, когда внешние идентфикаторы всегда UUID, это в целом безопасней, т.к. про безопасность отдельных эндпоинтов не всегда люди думают, тут оно "нахаляву". Однако с вариантом 2.3 с безопасностью есть нюансы — утекает timestamp так или иначе.
6. Генерация ID на стороне клиента. Порой это бывает удобно. Я бы сказал, это вообще всегда удобно. Генерация ID на стороне сервера накладывает достаточно ощутимые ограничения на используемые протоколы и во многих случаях усложняет код.
7. Распределённая БД. К примеру у нас есть древовидная структура БД, когда в каждом филиале стоит отдельная БД (для возможности автономной работы) и данные уходят в центральную БД. В случае UUID всё выглядит гораздо проще. В случае id нужно думать чуть больше.
8. Совпадения id для разных сущностей. К примеру можно сделать join по неправильному полю. Если id не совпадают, запрос ничего не вернёт, что вероятно насторожит и не даст багу идти дальше. В противном случае может вернуться бессмысленный, но ненулевой результат.
Также есть проблема с реализацией вариантов 2.2 и 2.3, это не слишком часто используемые подходы и возможно придется использовать редкие библиотеки или писать самому. Это не великая проблема, но всё же.
Я сейчас работаю над best practices, которыми будем руководствоваться в будущем для создания сервисов. И нужно этот вопрос решить. Плюсы и минусы там и там есть. Скорей склоняюсь к UUID 2.3, т.к. считаю, что проблемы с производительностью будут малозаметны, а удобство читаемых id не перевешивает плюсов UUID. Заранее спасибо за конструктивные отзывы.