مستوى القارئ: متوسط. المقال يفترض إنك تعرف الأساسيات في لغة برمجة وقاعدة بيانات، وسمعت عن النظام الثنائي من بعيد. لو أنت مبتدئ تمامًا، فيه مثال مبسّط في النص هيوصّلك الفكرة قبل الشرح العلمي.
لو عندك 8 صلاحيات لكل مستخدم وبتخزّن كل واحدة في عمود منطقي منفصل، أنت بتضيّع مساحة وبتصعّب الاستعلامات بلا داعي. الـ Bitmask بيخلّيك تخزّن الـ 8 (أو حتى 64) صلاحية في رقم صحيح واحد، ويفحص أي صلاحية في عملية واحدة.
المشكلة باختصار
تخيّل جدول مستخدمين فيه أعمدة زي can_read وcan_write وcan_delete وis_admin وهكذا. كل صلاحية جديدة معناها عمود جديد، وتعديل في الـ schema، وALTER TABLE على جدول ممكن يكون فيه ملايين الصفوف. بعد سنة بيبقى عندك 20 عمود بوليان، والاستعلام اللي بيسأل "مين عنده صلاحية النشر والحذف مع بعض" بيبقى شكله وحش.
الفكرة بمثال بسيط قبل الكلام العلمي
تخيّل لوحة فيها 8 مفاتيح كهرباء في صف واحد. كل مفتاح إمّا مطفي (0) أو مولّع (1). لو حبيت تحكي لصاحبك حالة اللوحة كلها في التليفون، مش هتقوله "الأول مطفي والتاني مولّع..."، هتقوله رقم واحد بيلخّص الصف كله.
كل مفتاح ليه "وزن": المفتاح الأول بـ 1، اللي بعده بـ 2، اللي بعده بـ 4، وهكذا كل مفتاح ضعف اللي قبله (1، 2، 4، 8، 16، 32، 64، 128). عشان تعرف الرقم اللي بيمثّل اللوحة، بتجمع أوزان المفاتيح المولّعة بس. لو المولّع هو مفتاح الـ 64 والـ 8 والـ 2 والـ 1، يبقى الرقم 64+8+2+1 = 75. الرقم 75 لوحده بيحكي حالة الـ 8 مفاتيح كاملة.
الشرح العلمي
ده بالظبط اللي بيحصل جوّه الكمبيوتر. أي رقم صحيح متخزّن كسلسلة بِتّات (أصفار وواحدات)، وكل بِت له قيمة تساوي أُس للعدد 2 حسب موقعه. الـ Bitmask هو إننا نحجز لكل صلاحية بِت واحد، ونستخدم المعاملات على مستوى البِت (bitwise operators) للقراءة والكتابة:
OR(|): يشغّل بِت معين (يضيف صلاحية).AND(&): يفحص هل بِت معين مشغّل (يتأكد من صلاحية).ANDمع النفي (& ~): يطفّي بِت معين (يسحب صلاحية).
الافتراض إن عدد الصلاحيات ثابت ومحدود نسبيًا: SMALLINT بيديك 16 بِت أي 16 صلاحية، وBIGINT بيديك 64. لو محتاج أكتر من كده، الطريقة دي مش المناسبة.
مثال تنفيذي بالكود
الكود ده بايثون شغّال يعرّف الصلاحيات، يبني القيمة، ويفحصها. جرّبه بنفسك:
# كل صلاحية = بِت واحد (أس للعدد 2)
CAN_READ = 1 # 0b00000001
CAN_WRITE = 2 # 0b00000010
CAN_DELETE = 4 # 0b00000100
CAN_SHARE = 8 # 0b00001000
IS_ADMIN = 16 # 0b00010000
# نبني صلاحيات مستخدم: قراءة + كتابة + أدمن
perms = CAN_READ | CAN_WRITE | IS_ADMIN
print(perms) # 19 (1 + 2 + 16)
print(bin(perms)) # 0b10011
# فحص صلاحية: هل يقدر يكتب؟
if perms & CAN_WRITE:
print("يقدر يكتب") # يتنفّذ
# إضافة صلاحية الحذف
perms |= CAN_DELETE
print(perms & CAN_DELETE) # 4 (أي: صح)
# سحب صلاحية الأدمن
perms &= ~IS_ADMIN
print(perms & IS_ADMIN) # 0 (أي: خلاص اتشالت)
ركز في نقطة مهمة: perms & CAN_WRITE بترجع رقم (2) مش True، لكن بايثون بيعتبر أي رقم غير صفر بمثابة صح. لو بتقارن صراحةً، قارن بـ != 0 عشان تتجنب مفاجآت.
على قاعدة البيانات: عمود واحد بدل ثمانية
بدل 8 أعمدة بوليان، بتخزّن عمود واحد اسمه perms من نوع SMALLINT. الصورة الجاية بتوضح الفرق:
في PostgreSQL الاستعلام عن "مين عنده صلاحية الكتابة" بيبقى سطر واحد نضيف:
-- CAN_WRITE = 2
SELECT id, name FROM users WHERE perms & 2 = 2;
بالأرقام: 8 أعمدة BOOLEAN بتاخد قرابة 8 بايت لكل صف (بايت لكل عمود في أغلب المحرّكات). عمود SMALLINT واحد بياخد 2 بايت. على جدول فيه 10 ملايين صف، ده فرق تقريبي من 80 ميجابايت لـ 20 ميجابايت في تخزين الصلاحيات وحدها — توفير حوالي 75%، من غير حساب توفير الفهارس.
الـ trade-offs
مفيش حل ببلاش. الـ Bitmask بيكسّبك مساحة أقل، وفحص مركّب في عملية واحدة، وإضافة صلاحية جديدة من غير ALTER TABLE (بتحجز بِت جديد وخلاص). في المقابل بتخسر الوضوح: الرقم 19 مش بيقولك حاجة لوحده، محتاج طبقة تترجمه. وبتخسر سهولة الاستعلام المقروء لغير المتخصص، والفهرسة المباشرة على صلاحية واحدة. الـ trade-off هنا: كثافة تخزين ومرونة، مقابل قابلية قراءة وفهرسة.
متى لا تستخدم هذه الطريقة
متستخدمش الـ Bitmask لو:
- الصلاحيات بتتغيّر أسماؤها وعددها كتير، أو محتاج علاقات معقّدة بينها (وقتها جدول صلاحيات منفصل أوضح وأمرن).
- محتاج فهرسة وبحث سريع على صلاحية واحدة بعينها بشكل متكرر.
- الفريق اللي هيقرا الكود مش مرتاح للعمليات على مستوى البِت — الوضوح أهم من توفير 60 ميجابايت.
- عدد الصلاحيات ممكن يعدّي 64 (خرجت عن حدود الـ
BIGINT).
الخطوة التالية
افتح أقرب مكان في كودك بتخزّن فيه أكتر من 4 أعلام بوليان مترابطة (إعدادات مستخدم، حالات طلب، فلاتر). حوّلهم لثوابت بأوزان 1، 2، 4، 8، واستبدل الشرط المتشعّب بـ value & FLAG. قِس حجم الجدول قبل وبعد، ولو الفرق واضح ابنِ عليه.
المصادر
- Python Documentation — Bitwise Operations on Integer Types: docs.python.org
- PostgreSQL Documentation — Numeric Types & Bitwise Operators: postgresql.org
- MDN Web Docs — Bitwise operators: developer.mozilla.org
- Wikipedia — Mask (computing): en.wikipedia.org