چرا امنیت به طراحی دیتابیس تعلق دارد، نه فقط کد اپلیکیشن
تکیه صرف بر کد اپلیکیشن برای کنترل اینکه چه کسی میتواند داده را ببیند یا تغییر دهد، یک دیتابیس را در برابر هر باگ، پیکربندی نادرست، یا دسترسی مستقیم دیتابیس که لایه اپلیکیشن را کاملاً دور میزند آسیبپذیر میکند. سیستمهای دیتابیس مدرن مکانیزمهای کنترل دسترسی را مستقیماً درون خود دیتابیس فراهم میکنند، که یک لایه محافظتی اضافه مستقل از اپلیکیشن تشکیل میدهد.
کاربران، نقشها، و مجوزها
کنترل دسترسی دیتابیس معمولاً حول سه مفهوم ساخته میشود. یک User (کاربر) (یا Database Account) یک فرد یا سرویسی که به دیتابیس متصل میشود را نشان میدهد. یک Role (نقش) یک مجموعه نامگذاریشده از مجوزها است که میتواند به چند کاربر همزمان اختصاص یابد، و از نیاز به پیکربندی جداگانه مجوزها برای هر تک کاربر اجتناب میکند. یک Permission (یا Privilege) (مجوز) توانایی انجام یک عمل خاص، مانند خواندن، درج، بهروزرسانی، یا حذف داده در یک جدول خاص را اعطا میکند.
-- ساخت یک نقش با دسترسی فقط-خواندنی
CREATE ROLE report_viewer;
GRANT SELECT ON orders TO report_viewer;
GRANT SELECT ON customers TO report_viewer;
-- اختصاص یک کاربر خاص به آن نقش
GRANT report_viewer TO analyst_account;این رویکرد مبتنیبر-نقش از Principle of Least Privilege (اصل کمترین امتیاز) پیروی میکند: هر حساب باید فقط حداقل دسترسی لازم برای انجام وظیفهاش را داشته باشد، نه بیشتر. یک حساب تحلیلی که فقط نیاز دارد داده را بخواند هرگز نباید همچنین توانایی حذف رکوردها را داشته باشد، چون آن مجوز غیرضروری صرفاً ریسک منفی است بدون هیچ بهره متناظری.
امنیت سطح-ستون و سطح-ردیف
فراتر از مجوزهای سطح-جدول، بسیاری سیستمهای دیتابیس کنترل دقیقتری پشتیبانی میکنند. Column-Level Security (امنیت سطح-ستون) دسترسی به ستونهای خاص درون یک جدول را محدود میکند، مانند اجازهدادن به یک نقش برای دیدن نامهای مشتری اما نه جزئیات کارت پرداختشان. Row-Level Security (امنیت سطح-ردیف) محدود میکند کدام ردیفهای خاص یک کاربر میتواند ببیند، مانند اطمینان از اینکه یک نماینده فروش فقط میتواند سفارشهای متعلق به مشتریان اختصاصدادهشده به خودش را ببیند، نه سفارشهای هر مشتری.
-- مثال: محدودکردن یک نقش به دیدن فقط
-- نامها و ایمیلهای مشتری، نه جزئیات پرداخت
GRANT SELECT (customer_id, name, email) ON customers
TO customer_service_role;این کنترلهای دقیقتر اجازه میدهند یک جدول واحد با امنیت به چند نقش مختلف با نیازهای مشروع متفاوت خدمت کند، بدون نیاز به تکرار داده در جداول جداگانه صرفاً برای اهداف امنیتی.
چرا کارایی کوئری در مقیاس اهمیت دارد
یک کوئری که بهطور آنی روی یک جدول تست با صد ردیف اجرا میشود میتواند روی یک جدول تولیدی با میلیونها ردیف بهطور غیرقابلقبولی کند شود، اگر دیتابیس هیچ راه کارآمدی برای مکانیابی ردیفهای مرتبط بدون بررسی هر ردیف تک نداشته باشد. اینجاست که ایندکسگذاری ضروری میشود.
ایندکسها چگونه کوئریها را سریعتر میکنند
یک Index (ایندکس) یک ساختار داده جداگانه است، از نظر مفهومی مشابه با ساختارهای درخت متوازن که پیشتر در این مجموعه درباره درختهای قرمز-سیاه و درختهای B بحث شد، که به دیتابیس اجازه میدهد ردیفهای منطبق با یک شرط را بدون اسکن کل جدول مکانیابی کند.
-- بدون یک ایندکس، یافتن یک مشتری با ایمیل
-- نیازمند اسکن هر ردیف در جدول است
SELECT * FROM customers WHERE email = '[email protected]';
-- ساخت یک ایندکس اجازه میدهد دیتابیس مستقیماً
-- به ردیفهای منطبق بپرد
CREATE INDEX idx_customer_email ON customers(email);بیشتر دیتابیسهای رابطهای ایندکسها را با استفاده از یک ساختار درخت B پیادهسازی میکنند، که پیشتر در این مجموعه درباره ساختارهای داده پیشرفته بحث شد، بهطور خاص چون درختهای B تعداد دسترسیهای دیسک مورد نیاز برای مکانیابی یک مقدار را کمینه میکنند، که دقیقاً گلوگاهی است که بیشترین اهمیت را برای کارایی دیتابیس دارد.
مبادله: ایندکسها رایگان نیستند
ایندکسها کوئریهایی که بر اساس ستون ایندکسشده جستجو یا مرتب میشوند را بهطور چشمگیری سریعتر میکنند، اما هزینههای واقعی دارند: هر ایندکس فضای ذخیرهسازی اضافی مصرف میکند، و هر درج، بهروزرسانی، یا حذف روی جدول باید همچنین هر ایندکس تعریفشده روی آن را بهروزرسانی کند، که عملیاتهای نوشتن را کند میکند.
مبادله ایندکسگذاری:
جدول خواندن-سنگین (مثلاً یک کاتالوگ محصول که مداوماً
جستجو میشود اما بهندرت بهروزرسانی میشود): سخاوتمندانه ایندکس بگذار
جدول نوشتن-سنگین (مثلاً یک جدول لاگینگ با فرکانس-بالا
که بهندرت پرسوجو میشود): با احتیاط ایندکس بگذار،
فقط روی ستونهایی که واقعاً در بندهای WHERE
یا شرایط JOIN استفاده میشوندانتخاب اینکه کدام ستونها ایندکس شوند بنابراین یک تصمیم طراحی واقعی است، نه چیزی که بیرویه روی هر ستونی اعمال شود — انتخاب درست به درک این بستگی دارد که کدام ستونها واقعاً در پرمکررترین و حساس-به-کاراییترین کوئریهای اپلیکیشن استفاده میشوند.
اصول پایه بهینهسازی کوئری
فراتر از ایندکسگذاری، چند اصل کلی کمک میکنند کوئریها کارآمد بمانند. انتخاب فقط ستونهای خاص مورد نیاز واقعی، بهجای هر ستون با SELECT *، مقدار دادهای که دیتابیس باید بازیابی و منتقل کند را کاهش میدهد. فیلترکردن داده تا حد ممکن زود در یک کوئری، پیش از joinکردن جداول اضافی، مقدار دادهای که عملیاتهای بعدی نیاز دارند پردازش کنند را کاهش میدهد. درک ترتیب و نوع JOIN، که پیشتر در این مجموعه بحث شد، نیز اهمیت دارد — یک JOIN غیرضروری یا یک شرط join ناکارآمد میتواند دیتابیس را مجبور کند ترکیبهای ردیف بسیار بیشتری از آنچه واقعاً نیاز است مقایسه کند.
چرا امنیت و کارایی فرآیند طراحی را کامل میکنند
با بازگشت به فرآیند طراحی کلی که پیشتر در این مجموعه بحث شد — نیازمندیها، طراحی مفهومی، طراحی منطقی، و پیادهسازی فیزیکی — امنیت و بهینهسازی نگرانیهایی هستند که بر این مرحله نهایی پیادهسازی فیزیکی غالباند. یک اسکیما میتواند کاملاً نرمالشده و از نظر منطقی درست باشد، اما همچنان در تولید شکست بخورد اگر داده حساس را بیشازحد گسترده افشا کند یا تحت بارهای کاری واقعی بیشازحد کند عمل کند. رفتار با کنترل دسترسی و ایندکسگذاری بهعنوان بخشهای جداییناپذیر فرآیند طراحی، بهجای فکرهای بعدی که وقتی مسائل ظاهر میشوند وصله میشوند، چیزی است که یک طراحی دیتابیس که صرفاً در تست کار میکند را از یکی که واقعاً برای استفاده دنیای واقعی آماده است جدا میکند.