Khi nào thì tốt hơn nên lưu trữ cờ dưới dạng bitmask thay vì sử dụng bảng kết hợp?


76

Tôi đang làm việc trên một ứng dụng mà người dùng có các quyền khác nhau để sử dụng các tính năng khác nhau (ví dụ: Đọc, Tạo, Tải xuống, In, Phê duyệt, v.v.). Danh sách các quyền sẽ không thay đổi thường xuyên. Tôi có một số tùy chọn về cách lưu trữ các quyền này trong cơ sở dữ liệu.

Trong những trường hợp nào thì Lựa chọn 2 sẽ tốt hơn?

lựa chọn 1

Sử dụng một bảng kết hợp.

Người dùng
----
UserId (PK)
Tên
Phòng ban
Sự cho phép
----
PermissionId (PK)
Tên
User_Permission
----
UserId (FK)
PermissionId (FK)

Lựa chọn 2

Lưu trữ một bitmask cho mỗi người dùng.

Người dùng
----
UserId (PK)
Tên
Phòng ban
Quyền
[Flags]
enum Permissions {
    Read = 1,
    Create = 2,
    Download = 4,
    Print = 8,
    Approve = 16
}

Câu trả lời:


63

Câu hỏi lộng lẫy!

Trước hết, hãy đưa ra một số giả định về "tốt hơn".

Tôi cho rằng bạn không quan tâm nhiều đến dung lượng đĩa - bitmask có hiệu quả theo quan điểm không gian, nhưng tôi không chắc điều đó quan trọng lắm nếu bạn đang sử dụng máy chủ SQL.

Tôi cho rằng bạn quan tâm đến tốc độ. Một bitmask có thể rất nhanh khi sử dụng các phép tính - nhưng bạn sẽ không thể sử dụng một chỉ mục khi truy vấn bitmask. Điều này không quan trọng lắm, nhưng nếu bạn muốn biết người dùng nào có quyền truy cập, truy vấn của bạn sẽ giống như

select * from user where permsission & CREATE = TRUE

(chưa có quyền truy cập vào SQL Server hôm nay, trên đường). Truy vấn đó sẽ không thể sử dụng chỉ mục do phép toán - vì vậy nếu bạn có một số lượng lớn người dùng, điều này sẽ khá khó khăn.

Tôi cho rằng bạn quan tâm đến khả năng bảo trì. Từ quan điểm về khả năng bảo trì, bitmask không biểu đạt như miền vấn đề cơ bản khi lưu trữ các quyền rõ ràng. Bạn gần như chắc chắn phải đồng bộ hóa giá trị của các cờ bitmask trên nhiều thành phần - bao gồm cả cơ sở dữ liệu. Không phải là không thể, nhưng đau ở mặt sau.

Vì vậy, trừ khi có một cách khác để đánh giá "tốt hơn", tôi muốn nói rằng tuyến bitmask không tốt bằng việc lưu trữ các quyền trong cấu trúc cơ sở dữ liệu chuẩn hóa. Tôi không đồng ý rằng nó sẽ "chậm hơn vì bạn phải tham gia" - trừ khi bạn có một cơ sở dữ liệu hoàn toàn không hoạt động, bạn sẽ không thể đo lường điều này (trong khi truy vấn mà không có lợi ích của chỉ mục hoạt động có thể trở nên đáng chú ý chậm hơn với thậm chí vài nghìn bản ghi).


5
Vì bản số của cột boolean (hoặc bit trong trường hợp SQL Server) là cực kỳ thấp, một chỉ mục trên các cột đó hoàn toàn vô dụng. Vì vậy, giải pháp chuẩn hóa cũng sẽ không có sẵn tính năng tối ưu hóa đó.
Clodoaldo Neto,

SQL Server không đóng gói các trường bit liền kề thành byte, về cơ bản lưu trữ nó dưới dạng bitmask.
nghiền nát

12

Cá nhân tôi sẽ sử dụng một bảng kết hợp.

Một trường bitmask rất khó để truy vấn và tham gia.

Bạn luôn có thể ánh xạ điều này với enum C # flags của bạn và nếu hiệu suất trở nên và vấn đề tái cấu trúc cơ sở dữ liệu.

Khả năng đọc qua tối ưu hóa sớm;)


6
Quản lý và bảo trì. Sẽ khó khăn hơn bao nhiêu để duy trì và quản lý dữ liệu được lưu trữ trong cơ sở dữ liệu khi thông tin quan trọng bị xáo trộn trong cột bitmask? Và bất kỳ mức tăng hiệu suất nào gần như chắc chắn sẽ không đủ lớn để tạo ra sự khác biệt thực sự.
Philip Kelley

5

Lưu trữ các quyền được chuẩn hóa (tức là không phải trong mặt nạ bit). Mặc dù rõ ràng đây không phải là yêu cầu đối với kịch bản của bạn (đặc biệt nếu các quyền sẽ không thường xuyên thay đổi), nhưng nó sẽ giúp việc truy vấn trở nên dễ dàng và rõ ràng hơn nhiều.


5

Không có câu trả lời chắc chắn , vì vậy hãy làm những gì phù hợp với bạn . Nhưng đây là bí quyết của tôi:

Sử dụng tùy chọn 1 nếu

  • Bạn mong đợi các quyền sẽ tăng lên nhiều
  • Nếu bạn có thể cần thực hiện kiểm tra quyền trong chính các thủ tục được lưu trữ trong cơ sở dữ liệu
  • Bạn không mong đợi hàng triệu người dùng để các bản ghi trong bảng không phát triển ồ ạt

Sử dụng tùy chọn 2 nếu

  • Quyền sẽ bị giới hạn ở một số ít
  • Bạn mong đợi hàng triệu người dùng

Hàng triệu hàng là một con số không đáng kể trong (thừa và thậm chí phong nha) hiện đại RDBMS của
Adam Robinson

Có nhưng xem xét các chỉ mục bạn có thể cần và khả năng đánh dấu chỉ mục trong quá trình tìm kiếm sẽ làm chậm quá trình, tôi thích lựa chọn thứ hai hơn.
Aliostad

1

Lần duy nhất tôi có thể nghĩ đến khi tôi sử dụng trường bitmask để lưu trữ các quyền, là khi bạn thực sự bị hạn chế về lượng bộ nhớ vật lý mà bạn có .... giống như trên một thiết bị di động cũ. Trên thực tế, dung lượng bộ nhớ bạn tiết kiệm được không đáng là bao. Ngay cả ở hàng triệu người dùng, dung lượng ổ cứng vẫn rẻ và bạn có thể mở rộng quyền, v.v. dễ dàng hơn nhiều bằng cách sử dụng phương pháp không bitmask (điều này về việc báo cáo ai có quyền nào, v.v.)

Một trong những vấn đề đau đầu nhất mà tôi từng gặp phải đó là gán quyền cho người dùng trực tiếp trong cơ sở dữ liệu. Tôi biết bạn nên thử và sử dụng ứng dụng để quản trị chính nó và không-nhiều với dữ liệu ứng dụng nói chung, nhưng đôi khi, nó chỉ cần thiết. Trừ khi bitmask thực sự là một trường ký tự và bạn có thể dễ dàng xem ai đó có quyền gì thay vì một số nguyên, hãy thử giải thích với nhà phân tích, v.v. cách cấp quyền ghi, v.v. cho ai đó bằng cách cập nhật trường ..... và cầu nguyện số học của bạn là chính xác.


1

Nó sẽ hữu ích khi chúng không thay đổi cấu trúc và sẽ luôn được sử dụng cùng nhau. Bằng cách đó, bạn có ít chuyến đi vòng quanh máy chủ. Chúng cũng có hiệu suất tốt vì bạn có thể ảnh hưởng đến tất cả các quyền trong một lần gán một biến.

Cá nhân tôi không thích chúng ... Trong một số ứng dụng hiệu suất cao, chúng vẫn được sử dụng. Tôi nhớ đã triển khai cờ vua-AI bằng cách sử dụng những thứ này vì bạn có thể đánh giá một bàn cờ trong một phép so sánh duy nhất .. Thật khó để làm việc với.


1

Tôi sẽ luôn lưu trữ nó ở trạng thái bình thường trừ khi cơ sở dữ liệu chỉ đơn thuần lưu giữ hồ sơ cho bạn và bạn sẽ không bao giờ làm bất cứ điều gì với điều này ngoài việc truy xuất và lưu. Một tình huống cho điều này là nếu khi đăng nhập, chuỗi quyền của người dùng của bạn được tìm nạp và trong mã máy chủ, nó được xử lý và lưu vào bộ nhớ cache. Trong trường hợp đó, nó thực sự không quan trọng quá mức mà nó không chuẩn hóa.

Nếu bạn đang lưu trữ nó trong một chuỗi và cố gắng thực hiện nó ở cấp DB, bạn sẽ phải thực hiện một số bài tập thể dục để có được quyền cho trang X, điều này có thể gây khó chịu.


1

Tôi khuyên bạn không nên sử dụng mặt nạ bit vì những lý do sau:

  • Chỉ mục không thể được sử dụng hiệu quả
  • Truy vấn khó khăn hơn
  • Khả năng đọc / Bảo trì bị ảnh hưởng nghiêm trọng
  • Các nhà phát triển trung bình ngoài đó không biết bitmask là gì
  • Tính linh hoạt bị giảm (giới hạn trên cho nr bit trong một số)

Tùy thuộc vào các mẫu truy vấn của bạn, tập hợp tính năng được lập kế hoạch và phân phối dữ liệu, tôi sẽ chọn tùy chọn 1 của bạn hoặc thậm chí một cái gì đó đơn giản như:

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

Thêm một cột là một sửa đổi lược đồ, nhưng tôi đoán rằng việc thêm một đặc quyền "Purge", sẽ yêu cầu một số mã đi kèm với nó, vì vậy các đặc quyền có thể không phải động như bạn nghĩ.

Nếu bạn có một số phân phối dữ liệu không hợp lý, chẳng hạn như 90% cơ sở người dùng không có một quyền duy nhất, thì mô hình sau cũng hoạt động tốt (nhưng bị hỏng khi thực hiện quét lớn hơn (một tham gia 5 chiều so với một bảng đầy đủ duy nhất quét).

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)
)

-2

Các truy vấn của bạn sẽ chạy nhanh hơn bằng cách sử dụng kiểu liệt kê cờ (bitmask), vì bạn sẽ không cần bao gồm một phép nối vào bảng được liên kết để hiểu giá trị.


4
-1 Điều này không chính xác ngụ ý rằng nó sẽ không chạy nhanh khi sử dụng một phép nối. Bạn cũng không giải thích cho truy vấn là gì . Nếu nó đang kiểm tra sự hiện diện của một quyền cụ thể nào đó, thì một phép nối trên cột được lập chỉ mục đúng cách sẽ thổi bay cánh cửa của trường bitmask, mà các hoạt động bitwise sẽ yêu cầu quét bảng.
Adam Robinson

@Adam Robinson, (1) Không, nó thực sự không ngụ ý điều đó chút nào. Nó ngụ ý rằng truy vấn sẽ chạy nhanh hơn , điều này là chính xác. (2) Bạn đang so sánh truy vấn được tối ưu hóa cao nhất trên bảng kết hợp với truy vấn được tối ưu hóa kém nhất trên trường số nguyên. Điều đó thực sự không thực tế lắm.
smartcaveman,

1
Mặc dù chắc chắn có khả năng mã bạn viết để diễn giải bitmask sẽ hiệu quả hơn so với một phép nối vào USER_PERMISSIONbảng, nhưng có vẻ như sự khác biệt về hiệu suất sẽ không có ý nghĩa - đây không phải là hoạt động tắc nghẽn - và có một mất rõ ràng đáng kể trong mã.
Justin Cave

Phiên bản gốc của bạn nói "nhanh", không phải "nhanh hơn", như bây giờ, do đó nhận xét đầu tiên của tôi. Có, tôi đang so sánh "truy vấn được tối ưu hóa cao nhất" cho phiên bản liên kết, nhưng đó cũng là phiên bản có nhiều khả năng được áp dụng. Tôi đang so sánh điều đó với truy vấn "được tối ưu hóa kém nhất" trên trường bitmask bởi vì, một lần nữa, đó là những gì có thể sẽ xảy ra. Không có cách nào để tạo chỉ mục bitwise trên một trường và nếu bạn định kiểm tra quyền như một phần của truy vấn, thì thao tác theo chiều bit là không thể tránh khỏi. Bạn có lựa chọn nào tốt hơn để làm điều đó không?
Adam Robinson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.