Преимущества и недостатки использования битовых масок в базе данных

22

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

Алекс Овечкин
источник
2
Возможно, более разумно, чтобы база данных создавала битовые маски внутри себя и представляла биты как отдельные столбцы. Ваши требования могут измениться.
Саймон Рихтер
1
Если вы не используете объединения, вы не используете свою реляционную базу данных так, как она предназначена.
Питер Б

Ответы:

38

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

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

Существует такая вещь, что объединяется слишком много таблиц, но реляционные базы данных созданы для этого. Многие из них имеют дополнительные функции, если производительность становится проблемой: индексы, индексированные представления и т. Д. Даже если искомые значения меняются не очень часто, что является преимуществом для Bitmask, накладные расходы на управление индексацией довольно легко в базе данных.

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

Мой последний аргумент против этого будет простота для других разработчиков. У вас есть пользователи, роли и назначения. Это набор отношений «многие ко многим» (поскольку существует более одного отношения), который настолько распространен, что им легко управлять. Это просто CRUD материал.

JeffO
источник
8
Реляционная база данных - худшее место для битовой маски. Затраты на хранение уже не так плохи, что несколько соединений и дополнительная таблица могут сломать вас. Это, конечно, делает все сложнее рассуждать. Сохраните разрешения в виде битов (1/0) в базе данных в их собственной таблице и представьте их в коде с флагами «но». Кажется довольно уместным и выполнимым. Разработчики получают простые флаги, а dbas имеют нормализованные таблицы. Все довольны.
Майк МакМэхон
3
Согласен, я поддерживал приложение, которое использовало битовые маски для пользовательских ролей и привилегий в своей базе данных. Это был кошмар. Используя 32-битное целое число, у нас закончились биты, поэтому у кого-то возникла прекрасная идея добавить больше битовых масок, а затем с перекрытиями, поэтому бит 4 в одном столбце означал бит 8 в этом другом столбце, и они вышли из синхронизации. Есть. Индексировать было сложно, потому что индексы хранят отдельные значения столбцов, а не отдельные биты в них, поэтому вы не можете искать строки where some_bit_mask & 12 > 0без построчного сканирования.
Брэндон
В конце дня достаточно было бы многим ко многим user_role_mapили user_priv_mapстолу.
Брэндон
@MikeMcMahon, не могли бы вы погрузиться глубже в дизайн таблицы и как мне отобразить ее в коде, чтобы достичь результата, о котором вы говорите?
Алексей Овечкин
2
@usr - Никогда не говори никогда. Конечно, вы можете использовать битовые маски, но я бы не стал использовать их в приложении, которое использует реляционную базу данных. Вероятно, есть некоторые крайние случаи, когда имеешь дело с устаревшими данными или супер потребностью в скорости.
Джеффо
24

Вы уже назвали соответствующие плюсы и минусы:

  • Битовые поля экономят место.
  • Они хранят данные в самой записи, поэтому вам не нужны СОЕДИНЕНИЯ для их поиска. (Но отдельные поля флага в записи будут делать то же самое.)
  • Они плохо читаются, если вы хотите продуктивно работать с необработанным выводом SQL.

Чтобы решить, что делать, нужно больше информации:

  • Насколько мало места на диске для вашего варианта использования?
  • Вы действительно так часто читаете роли пользователей, что время присоединиться к ним является узким местом?
  • Собираетесь ли вы читать вывод SQL и принимать решения на основе этого - или нечитаемая запись в базе данных несущественна, как тот факт, что машинный код вашей системы нечитаемый?

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

Килиан Фот
источник
Спасибо за ваш ответ, полностью согласен с вашими мыслями, но в целом это анти-паттерн или нет? А вы используете маски в своих проектах?
Алексей Овечкин
12
@ Алекс Нет такой вещи как «лучшая практика», которая могла бы решить, что делать в вашем случае. Если у вас слишком мало места, лучше всего использовать битовые поля. Если вы хотите использовать вывод SQL в отчетах генеральному директору, лучше всего использовать говорящие имена. Но вы единственный, кто знает эти обстоятельства, поэтому сообщество не может дать вам рецепт, который всегда действует.
Килиан Фот
Принимая космический аргумент в качестве "дай мне". Вопрос о том, стоит ли использовать битовую маску, стоит или падает в зависимости от того, дает ли она какую-либо выгоду сверх этого.
Робби Ди
Кроме того, вам нужно обрабатывать информацию в базе данных КАЖДОЙ, или она всегда читается в приложении перед ее использованием.
Ян
1
«Собираетесь ли вы читать выходные данные SQL и принимать решения на основе этого - или нечитаемая запись в базе данных несущественна, как тот факт, что машинный код вашей системы нечитаемый?» Думаю, я не могу говорить за всех разработчиков, но когда я занимаюсь разработкой, я очень часто начинаю выбирать данные из БД, чтобы что-то понять или проверить. Поэтому я бы сказал, что обычно ответом будет «Да, кто-то».
jpmc26
18

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

Если вы не являетесь Amazon или Netflix, объем данных, связанных с разрешениями пользователей, будет незначительным по сравнению со всем, что у вас есть.

Любая серьезная СУБД может справиться с этим «дополнительным соединением», даже не моргнув.

Фил В.
источник
7
+1: Хорошие реляционные базы данных разрабатываются людьми, которые действительно очень хороши в том, что они делают. Любой, кто нуждается в том, чтобы выжать последний бит производительности, который вы можете получить, используя битовые поля, не должен задавать этот вопрос. Смоделируйте данные, затем найдите части, которые не выполняют.
Blrfl
Наличие объединения сделает код приложения более сложным, поэтому многое зависит от того, ГДЕ обрабатываются роли.
Ян
4
@ Иметь соединение не кажется более сложным, чем умение расшифровывать разрешения с битовой маской.
Брэд
@Brad, Подумайте о перечислении, которое является набором флагов в C #, с его значением, хранящимся «как есть» в базе данных, C # cold не может быть проще. Если используется соединение, то код C # должен справляться с отношением «1 ко многим».
Ян
Я также должен добавить, что если у вас есть несколько логических столбцов в таблице, большинство баз данных поймут, как их раздвинуть на как можно меньше места, и позаботятся о том, чтобы их перепутать.
Blrfl
8

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

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

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

Робби Ди
источник
2

Единственное преимущество использования битовых масок состоит в том, что значение битовых полей не является статическим. Реляционные таблицы работают хорошо только тогда, когда вы заранее знаете, что представляет собой каждое поле в записи: в CREATE TABLEконце концов, вы должны идентифицировать поля в выражении DDL.

Если значение каждого битового поля настраивается во время выполнения или иным образом не известно заранее, тогда может иметь смысл хранить логические значения в виде битового поля. Даже тогда, можно определить таблицу с произвольными полями: field_1, field_2и т.д. Это дает более чистый реляционный дизайн, хотя по- прежнему не идеальны. Является ли это преимуществом для битового поля, во многом зависит от мнения, поскольку ни одно из решений не является идеальным.

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

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


источник
2

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

Преимущество, не упомянутое выше, заключается в возможности добавления нового значения к битовым маскам без потенциально трудоемкого добавления нового столбца. Наши дизайнеры БД (которые предшествовали мне) хранят их в таблице, которая ежедневно получает 5 миллионов новых записей. Добавление нового столбца для представления нового поведения заняло бы много времени, в то время как определение нового бита (мы использовали 33 из 64) не требует перестройки таблицы.

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

гигабайт
источник
Это интересный случай. Я полагаю, что вы могли бы добиться того же самого в кошерном и явном виде, определив «запасные» столбцы в таблице, а затем применив их по мере необходимости. Затем вы можете по крайней мере выборочно индексировать эти столбцы, если вы решите это сделать.
Стив
1

Если цель состоит в том, чтобы просто сэкономить место на диске, я думаю, что это плохая идея:

  • посмотрите на стоимость ГБ сегодня,
  • Сравните это с затратами времени тех, кто пишет отчеты и запросы и должен выяснить, что находится в поле и как обращаться с конкретным битом, сравнение затрат и выгод может закончиться не с той стороны.
  • если вы работаете с базой данных SQL, дополнительные операции доступа к битам, необходимые во многих запросах, могут также потребовать больше вычислительного времени, чем необходимо

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

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