Когда лучше хранить флаги как битовую маску, а не использовать ассоциативную таблицу? - PullRequest
75 голосов
/ 19 апреля 2011

Я работаю над приложением, в котором пользователи имеют разные права на использование разных функций (например, «Чтение», «Создание», «Загрузка», «Печать», «Утверждение» и т. Д.).Не ожидается, что список разрешений будет часто меняться.У меня есть несколько вариантов хранения этих разрешений в базе данных.

В каких случаях вариант 2 будет лучше?

Вариант 1

Использовать ассоциативную таблицу.

User
----
UserId (PK)
Name
Department
Permission
----
PermissionId (PK)
Name
User_Permission
----
UserId (FK)
PermissionId (FK)

Вариант 2

Сохранение битовой маски для каждого пользователя.

User
----
UserId (PK)
Name
Department
Permissions
[Flags]
enum Permissions {
    Read = 1,
    Create = 2,
    Download = 4,
    Print = 8,
    Approve = 16
}

Ответы [ 9 ]

63 голосов
/ 19 апреля 2011

Великолепный вопрос!

Во-первых, давайте сделаем некоторые предположения о «лучшем».

Я предполагаю, что вам не очень важен объем дискового пространства - битовая маска эффективна из пространстваточка зрения, но я не уверен, что это имеет большое значение, если вы используете сервер SQL.

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

select * from user where permsission & CREATE = TRUE

(у вас нет доступа к SQL Server сегодня, в дороге).Этот запрос не сможет использовать индекс из-за математической операции - поэтому, если у вас огромное количество пользователей, это будет довольно болезненно.

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

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

11 голосов
/ 19 апреля 2011

Лично я бы использовал ассоциативную таблицу.

Поле битовой маски очень сложно запрашивать и присоединять.

Вы всегда можете сопоставить это с вашим перечислением флагов C # и, если производительность повысится, и выполнить рефакторинг базы данных.

Читаемость по преждевременной оптимизации;)

5 голосов
/ 19 апреля 2011

Нет однозначного ответа , поэтому делает то, что работает для вас .Но вот мой улов:

Используйте вариант 1, если

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

Используйте вариант 2, если

  • Разрешениябудет ограничено горсткой
  • Вас ожидают миллионы пользователей
5 голосов
/ 19 апреля 2011

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

1 голос
/ 19 апреля 2011

Я не рекомендую использовать битовую маску по следующим причинам:

  • Индекс не может использоваться эффективно
  • Запросы это сложнее
  • Читаемость / Техническое обслуживание сильно пострадали
  • Обычный разработчик не знает, что такое битовая маска
  • Гибкость уменьшена (верхний предел на количество бит в числе)

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

user_permissions(
   user_id
  ,read     
  ,create   
  ,download 
  ,print    
  ,approve  
  ,primary key(user_id)
);

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

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

user_permission_read(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)

user_permission_write(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)

user_permission_etcetera(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)
1 голос
/ 19 апреля 2011

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

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

1 голос
/ 19 апреля 2011

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

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

1 голос
/ 19 апреля 2011

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

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

0 голосов
/ 19 апреля 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...