آموزش گام‌به‌گام مانیتورینگ یا نظارت بر عملکرد MongoDB

آموزش مانیتورینگ یا نظارت بر عملکرد MongoDB
Avatar
نویسنده: علیرضا برزودی
جمعه 27 خرداد 1401
مطالعه: ۲۶ دقیقه ۰ نظر ۱۰۷۵ بازدید

در این مطلب، نحوه نظارت بر متریک‌های دیتابیس برحسب ریکوئست‌ها و با استفاده از دستورها و ابزارهای داخلی را آموزش خواهیم داد. همچنین، شما را با پروفایلر دیتابیس MongoDB آشنا خواهیم کرد که در شناسایی کوئری‌های بهینه‌نشده و نامناسب می‌تواند به شما کمک کند.

مانیتورینگ؛ بخش مهم مدیریت دیتابیس

مانیتورینگ و نظارت بر عملکرد بخش اصلی مدیریت دیتابیس است. این کار به شما امکان می‌دهد از عملکرد صحیح دیتابیس خود اطمینان پیدا کنید. نظارت با چندین هدف انجام می‌شود:

  1. با نظارت بر عملکرد دیتابیس، ظرفیت فعلی پایگاه داده را می‌توانید بهتر درک کنید و ببینید حجم کاری در طول زمان چگونه تغییر می‌کند؛ بنابراین، پیش از رسیدن دیتابیس به محدودیت‌هایی که برایش تعیین شده است، برای مقیاس‌بندی آن‌ها ازقبل برنامه‌ریزی خواهید کرد؛
  2. درصورت بروز مشکلات سخت‌افزاری یا رفتار غیرعادی مانند افزایش غیرمنتظره ترافیک، می‌توانید سریعاً متوجه آن‌ها شوید؛
  3. نظارت بر دیتابیس به تشخیص مشکلات برنامه‌‌های استفاده‌کننده از آن، مانند ریکوئست‌های برنامه‌‌ای که باعث ایجاد bottlenecks می‌‌شوند، کمک کند.
آموزش کار با mongodb
اهمیت نظارت بر عملکرد پایگاه‌ داده MongoDB

پیش‌نیازهای نظارت بر عملکرد MongoDB

برای درک بهتر و استفاده از این آموزش، پیش‌نیازهایی لازم است. برخی از این پیش‌نیازها عبارت‌اند از:

  • MongoDB روی سرور شما نصب شده باشد.
  • با نوشتن و فهمیدن کوئری‌های MongoDB و فیلترینگ نتایج آشنا باشید.
  • MongoDB سرور شما با فعال‌کردن احراز هویت و فرایندهای مشابه ایمن شده باشد.
  • سروری با کاربر معمولی و غیرروت با امتیازهای Sudo و فایروال پیکربندی‌شده با UFW (این آموزش با استفاده از سروری ارائه شده است که اوبونتو 20.04 را اجرا می‌کند).

این آموزش روی خودِ MongoDB متمرکز است، نه سیستم‌عامل. تا زمانی که احراز هویت فعال باشد، به‌طورکلی عملکرد MongoDB مستقل از سیستم‌عامل است و بدون توجه به سیستم‌عامل کار می‌کند.

مقاله‌ی زیر را برای آشنایی با بهترین ابزارها و نرم‌افزارهای مانیتورینگ عملکرد سرور بخوانید.

بهترین ابزارهای مانیتورینگ عملکرد سرور

گام اول: آماده‌سازی داده‌های آزمایش

برای توضیح نحوه نظارت بر عملکرد MongoDB، در این مرحله نحوه باز‌کردن شِل MongoDB برای اتصال به نمونه MongoDB لوکال و ایجاد مجموعه‌ای نمونه در آن توضیح داده شده است. برای ایجاد مجموعه نمونه استفاده‌شده در این بخش، به‌عنوان کاربر ادمین به شِل MongoDB متصل شوید.

این آموزش از پیش‌فرض‌های امنیتی MongoDB پیروی می‌کند؛ ازاین‌رو، در این بخش فرض می‌شود که نام این کاربر ادمین AdminSammy و نام احراز هویت دیتابیس آن admin است.

درصورت تفاوت، این نام‌ها را با استفاده از دستور زیر تغییر دهید تا درادامه تنظیمات با مشکل مواجه نشوید:

mongo -u AdminSammy -p --authenticationDatabase admin

رمزعبور تنظیم‌شده در حین نصب را وارد کنید تا به شِل دسترسی پیدا کنید. پس از وارد‌کردن رمزعبور، علامت اعلان < را مشاهده خواهید کرد.

دقت کنید که در New Connection، به‌طور پیش‌فرض شِل MongoDB به دیتابیس آزمایشی متصل می‌شود؛ درنتیجه، می‌توانید با خیال راحت از این دیتابیس برای آزمایش MongoDB و شلِ آن استفاده کنید.

به‌علاوه، این امکان نیز وجود دارد که برای اجرای دستورهایی که به‌عنوان مثال در این آموزش قرار داده شده‌اند، به دیتابیس‌ دیگری بروید. برای جابه‌جایی بین دیتابیس‌ها، از دستور use به‌همراه نام دیتابیس مدنظر خود می‌توانید استفاده کنید:

use database_name

هنگام کار با مجموعه داده‌های کوچک، استفاده از فرایند نظارت بر دیتابیس‌ ممکن است چندان مفید و کاربردی نباشد؛ بدین‌دلیل که در مجموعه داده‌های کوچک، سیستم دیتابیس فقط به اسکن چند رکورد برای هر ریکوئست داده‌شده نیاز دارد. برای نشان‌دادن ویژگی‌های نظارت بر عملکرد MongoDB، به دیتابیسی با داده‌های کافی نیاز دارید که در آن‌ها، اجرای کوئری‌ها زمان زیادی از MongoDB می‌گیرد.

دستورات مانگو دی بی
چگونه در مانگو دی‌بی داده‌ها را برای تست آماده کنیم؟

برای آموزش کامل Netdata و مانیتورینگ عملکرد سرور لینوکسی مقاله‌ی زیر را بخوانید.

مانیتورینگ عملکرد سرور لینوکس با Netdata

ایجاد مجموعه داده و ساختار آن

برای نظارت بر عملکرد، در این آموزش نمونه‌های موجود در مجموعه‌ای با نام Accounts در نظر گرفته شده‌اند که حاوی تعداد زیادی داکیومنت است. هر Document نشان‌دهنده یک حساب بانکی مربوط به فردی با موجودی حسابی است که به‌طور تصادفی ایجاد شده است. در این مجموعه، هر داکیومنت ساختاری مانند زیر خواهد داشت:

{
    "number": "1000-987321",
    "currency": "USD",
    "balance": 431233431
}

در این مثال، داکیومنت نمونه شامل اطلاعات زیر است:

  • number: این قسمت نشان‌دهنده شماره‌حساب برای حساب داده شده است. در این مجموعه، هر شماره‌حساب پیش‌شماره ۱۰۰۰- و به‌دنبال آن شناسه عددی افزایشی خواهد داشت.
  • currency: این فیلد نشان می‌دهد که موجودی هر حساب در چه نوع ارزی ذخیره می‌شود. نوع ارز هر حساب یا USD یا EUR خواهد بود.
  • balance: این بخش موجودی هر حساب بانکی را نشان می‌دهد. در این دیتابیس نمونه، فیلد موجودی هر سند مقداری دارد که به‌صورت تصادفی تولید شده است.

به‌جای درج دستی تعداد زیادی سند، کد جاوااسکریپت زیر را می‌‌توانید اجرا کنید تا هم‌زمان مجموعه‌‌ای به نام accounts ایجاد و میلیون‌ها داکیومنت را در آن وارد کنید:

for (let i = 1; i <= 1000000; ++i) {
    db.accounts.insertOne({
        "number": "1000-" + i,
        "currency": i > 500000 ? "USD" : "EUR",
        "balance": Math.random() * 100000
    })
}

کد ذکر‌شده یک حلقه for را اجرا می‌کند که این حلقه یک‌میلیون بار پشت‌سرهم اجرا می‌شود. در هربار تکرار، حلقه یک متد (insertOne) را اجرا می‌کند تا داکیومنتی جدید را در مجموعه حساب ایجاد کند.

متد معرفی‌شده در هر تکرار مقداری به فیلد number می‌دهد. این فیلد از پیشوند ۱۰۰۰ به‌همراه مقدار متغیر i (به‌عنوان شمارنده حلقه) تشکیل شده است؛ یعنی نخستین‌باری که این حلقه تکرار می‌شود، مقدار فیلد number روی ۱ تا ۱۰۰۰ و در آخرین تکرار، روی ۱۰۰۰ تا ۱۰۰۰۰۰۰ تنظیم خواهد شد.

currency همیشه برای حساب‌هایی با اعداد بیشتر از ۵۰۰۰۰۰ به‌صورت دلار آمریکا (USD) و برای حساب‌هایی با اعداد کمتر از آن به‌صورت یورو (EUR) نشان داده می‌شود. فیلد balance از تابع ()Math.random برای تولید یک عدد تصادفی بین ۰ و ۱ استفاده و سپس عدد تصادفی را در ۱۰۰۰۰۰ ضرب می‌کند تا مقادیر بزرگ‌تر و در یک بازه را به‌عنوان موجودی ثبت کند.

دقت کنید که اجرای این حلقه ممکن است بیش از ۱۰ دقیقه طول بکشد؛ بنابراین، بدون نگرانی اجازه دهید تا انتها اجرا شود.

خروجی این تابع موفقیت در انجام عملیات و ObjectId آخرین داکیومنت درج‌شده را برمی‌گرداند:

Output
{
        "acknowledged" : true,
        "insertedId" : ObjectId("61a38a4beedf737ac8e54e82")
}

با اجرای متد ()count بدون آرگومان، می‌توانید تأیید کنید که Documentها به‌درستی درج شده‌‌اند. این متد تعداد اسناد موجود در مجموعه را بازیابی می‌‌کند:

db.accounts.count()
Output
1000000

در این مرحله، فهرستی از Instance Document را با موفقیت ایجاد کرده‌‌اید. این داده‌ها برای توضیح و نحوه کار با ابزارهای MongoDB در فرایند نظارت بر عملکرد، به‌عنوان داده‌های آزمایشی استفاده می‌شوند. در مرحله بعد، یاد خواهید گرفت که چگونه آمار استفاده از سرور اصلی را بررسی کنید.

پایگاه داده مانگو دی بی
چگونه با مونگو دی‌بی کار کنیم؟

گام دوم: بررسی Server Usage Statistics

MongoD به‌طور خودکار مقداری از استاتیک‌های Performance مفید را ردیابی می‌کند. یکی از راه‌های مهم نظارت بر دیتابیس، بررسی منظم این آمارهاست. این آمارها نمی‌توانند اطلاعات آنی درباره آنچه در دیتابیس شما درلحظه اتفاق می‌افتد، نمایش دهند؛ اما استفاده از این مقادیر برای مشخص‌کردن نحوه عملکرد و وجود مشکلات بالقوه مفید خواهد بود.

دستورهای مانیتورینگ MongoDB که در این مقاله مشخص شده است، اطلاعات حساس درباره دیتابیس و عملکرد آن را برمی‌گرداند؛ به‌همین‌دلیل، برخی از این دستورها به مجوزهای پیشرفته نیاز دارند.

به‌طور خاص، متد ()serverStatus که در این مرحله مشخص شده است و دستورهای mongostat و mongotop که در مرحله بعد معرفی می‌شوند، همگی برای اجرا به نقش ClusterMonitor کاربر نیازمندند.

به‌همین‌ترتیب، متد ()setProfilingLevel که در مرحله چهارم مشخص شده است، به نقش dbAdmin نیاز دارد.

مقاله‌ی زیر آموزش کاملی است از مانیتورینگ پهنای باند در لینوکس به کمک ابزار vnSTAT

مانیتورینگ پهنای باند در لینوکس

اعطا و بررسی نقش کاربر در دیتابیس

ابتدا به دیتابیس احراز هویت کاربر خود بروید. در مثال زیر، این پایگاه داده با نام admin مشخص شده است؛ اما اگر نام دیتابیس شما متفاوت است، به دیتابیس احراز هویت کاربر خود متصل شوید:

use admin
Output
switched to db admin

سپس با استفاده از متد ()grantRolesToUser، به کاربر خود نقش clusterMonitor را همراه با نقش dbAdmin روی پایگاه داده‌ای اعطا کنید که مجموعه‌حساب‌ها را ایجاد کرده‌اید. مثال زیر فرض می‌کند که مجموعه accounts در پایگاه داده test است:

db.grantRolesToUser(
"AdminSammy",
[
"clusterMonitor",
{ role : "dbAdmin", db : "test" }
]
)

دقت کنید که داشتن یوزر پروفایلر برای اهداف خاص از امنیت بیشتری برخوردار خواهد بود. با این کار، هیچ‌یک از کاربران سیستم، دسترسی‌ها و امتیازهای اضافه و غیر‌ضروری نخواهند داشت. اگر در یک محیط کار از این قابلیت استفاده می‌کنید، بهتر است یک کاربر اختصاصی، تنها برای نظارت بر دیتابیس داشته باشید.

مثال زیر یک کاربر MongoDB به نام MonitorSammy ایجاد می‌کند و نقش‌های موردنیاز را برایتان به‌همراه مثال‌های این آموزش به آن‌ها می‌دهد. توجه کنید که این نقش‌ها شامل readWriteAnyDatabase نیز می‌شود که به کاربر اجازه می‌دهد داده‌ها را در هر دیتابیس در کلاستر بخواند و بنویسد:

db.createUser(
{
user: "MonitorSammy",
pwd: passwordPrompt(),
roles: [ { role : "dbAdmin", db : "test" }, "clusterMonitor", "readWriteAnyDatabase" ]
}
)

پس از اعطای نقش‌های مناسب به کاربر، به دیتابیسی بروید که مجموعه accounts شما در آن ذخیره می‌شود:

use test
Output
switched to db test

تابع stats و بررسی آمار دیتابیس

با بررسی آمار کلی دیتابیس با اجرای متد ()stats شروع کنید:

db.stats(1024*1024)

آرگومان این روش (۱۰۲۴*۱۰۲۴) ضریب مقیاس است و به MongoDB می‌گوید اطلاعات ذخیره‌سازی را برحسب مگابایت برگرداند. اگر این مقدار را حذف کنید، همه مقادیر به‌صورت بایت نمایش داده می‌شوند.

متد stats خروجی کوتاه و مختصر را با برخی آمارهای مهم مربوط به دیتابیس فعلی نمایش می‌دهد:

Output
{
        "db" : "test",
        "collections" : 3,
        "views" : 0,
        "objects" : 1000017,
        "avgObjSize" : 80.8896048767171,
        "dataSize" : 77.14365005493164,
        "storageSize" : 24.109375,
        "indexes" : 4,
        "indexSize" : 9.9765625,
        "totalSize" : 34.0859375,
        "scaleFactor" : 1048576,
        "fsUsedSize" : 4238.12109375,
        "fsTotalSize" : 24635.703125,
        "ok" : 1
}

این خروجی دیدی کلی از داده‌هایی را به ما نمایش می‌دهد که این نمونه MongoDB ذخیره می‌کند. کلیدهای زیر که در این خروجی بازگردانده می‌شوند، می‌توانند بسیار مفید باشند:

کلید objects تعداد کل داکیومنت موجود در دیتابیس را نشان می‌دهد. برای ارزیابی اندازه دیتابیس و بررسی رشد آن، با مشاهده مداوم می‌توانید از آن استفاده کنید.

avgObjectSize اندازه متوسط ​​این داکیومنت را نشان می‌دهد. همچنین، اطلاعاتی درباره این موضوع به‌دست می‌آورید که دیتابیس روی چه داکیومنتی (بزرگ و پیچیده یا کوچک) کار می‌کند. این مقدار ضریب مقیاس را مشخص کرده باشید یا نباشید، همیشه بر‌حسب بایت نشان داده می‌شود.

کلیدهای collections و indexes نشان می‌دهد که درحال‌حاضر چند مجموعه و فهرست در دیتابیس تعریف شده است. کلید totalSize نیز حاکی از میزان فضای ذخیره‌سازی‌ای است که دیتابیس از دیسک اشغال می‌کند.

مزایا و معایب mongodb
تابع State چه نقشی در پایگاه داده مانگو دی‌بی دارد؟

بررسی اطلاعات سرور با serverStatus

اطلاعات بازگشتی با متد ()stats می‌تواند نشان دهد که درحال‌حاضر چه مقدار داده در دیتابیس شما ذخیره شده است؛ اما اطلاعاتی درباره عملکرد یا مشکلات موجود ارائه نمی‌دهد. برای این اطلاعات، اغلب از متدِ ()serverStatus استفاده می‌شود:

db.serverStatus()

خروجی این روش طولانی است و اطلاعات زیادی درباره استفاده از سرور ارائه می‌دهد:

Output
{
        "host" : "ubuntu-mongo-rs",
        "version" : "4.4.6",
        "process" : "mongod",
        "pid" : NumberLong(658997),
        "uptime" : 976,
        . . .
        "ok" : 1
}

تمام اطلاعات ارائه‌شده در این روش مفید و کاربردی هستند. در این آموزش، به‌طور خاص بر سه بخش تمرکز شده است.

بررسی اتصالات و کانکشن‌ها

ابتدا بخش connections این خروجی را پیدا کنید:

Output
        . . .
        "connections" : {
                "current" : 4,
                "available" : 51196,
                "totalCreated" : 4,
                "active" : 2,
                "exhaustIsMaster" : 1,
                "exhaustHello" : 0,
                "awaitingTopologyChanges" : 1
        },
        . . .

هر سرور دیتابیس می‌تواند تعداد زیادی اتصال را در یک زمان پشتیبانی کند. کلید current تعداد کلاینت‌هایی را نشان می‌دهد که درحال‌حاضر به پایگاه‌داده متصل هستند. این در حالی است که available تعداد اتصالات استفاده‌نشده باقی‌مانده‌ای را نشان می‌دهد که در دیتابیس در‌دسترس است. مقدار totalCreated تعداد اتصالات استفاده‌شده از راه‌اندازی سرور را نگه می‌دارد.

اکثر برنامه‌ها برای استفاده مجدد از اتصالات موجود طراحی شده‌اند و اتصالات چندگانه را اغلب پشتیبانی نمی‌کنند؛ بنابراین، اگر تعداد زیادی اتصالات پیش‌بینی‌نشده داشته باشید، احتمالاً نشان‌دهنده پیکربندی نادرست در ارائه نحوه دسترسی مشتریان به سرور است.

درصورتی‌که براساس workload تعداد زیادی از اتصالات را پیش‌بینی کرده‌اید، برای توزیع ترافیک در چندین نمونه MongoDB، اضافه‌کردن یک یا چند Shard به یک کلاستر مفید خواهد بود.

بررسی قفل‌های سراسری در سرور

بخش globalLock را در output پیدا کنید. این بخش به قفل‌های سراسری در کل سرور دیتابیس مربوط می‌شود و اطلاعاتی را در این رابطه نمایش می‌دهد:

Output
        . . .
        "globalLock" : {
                "totalTime" : NumberLong(975312000),
                "currentQueue" : {
                        "total" : 0,
                        "readers" : 0,
                        "writers" : 0
                },
                "activeClients" : {
                        "total" : 0,
                        "readers" : 0,
                        "writers" : 0
                }
        },

MongoDB از قفل‌ها برای اطمینان از سازگاری داده‌ها هنگام انجام چندین عملیات هم‌زمان استفاده و تضمین می‌کند که هیچ دو کوئری‌ای دیتای یکسان را هم‌زمان تغییر نمی‌دهند. در سرورهایی که به‌طور مداوم استفاده می‌شوند، این احتمال وجود دارد که قفل‌کردن به Bottleneckهایی منجر شود؛ به‌طوری‌که یک یا چند کوئری درانتظار باز‌شدن قفل‌ها قبل از اجرا باشند.

مقدار currentQueue.total تعداد کوئری‌هایی را نشان می‌‌دهد که منتظر باز‌شدن قفل‌‌ها برای اجرا هستند. اگر این مقدار زیاد باشد؛ یعنی عملکرد دیتابیس تحت‌تأثیر قرار گرفته است و تکمیل کوئری‌ها زمان بیشتری می‌برد.

این مشکل اغلب ناشی از تعداد زیادی از کوئری‌هاست که مدت‌زمان طولانی قفل‌ها را نگه می‌دارند‌. همچنین، ممکن است نشان‌دهنده استفاده ناکارآمد از ایندکس‌ها یا کوئری‌هایی با طراحی ضعیف یا دلایل دیگر باشد.

دیتابیس برای Read یا Write؟

برای پاسخ به این پرسش، بخش opcounters را پیدا کنید:

Output
        "opcounters" : {
                "insert" : NumberLong(10000007),
                "query" : NumberLong(6),
                "update" : NumberLong(6),
                "delete" : NumberLong(0),
                "getmore" : NumberLong(0),
                "command" : NumberLong(1298)
        },

این بخش از خروجی ()serverStatus اطلاعاتی درباره اینکه سرور دیتابیس بیشتر برای read یا write استفاده می‌شود و تعادل در usage سرور ارائه می‌دهد. در این مثال، پس از درج داکیومنت آزمایشی counter‌ برای عملیات insert بسیار بیشتر از عملیات query است. در سناریویی واقعی، این مقادیر احتمالاً متفاوت خواهند بود.

این آمار می‌تواند ایده‌ای کلی برای نحوه استفاده از سرور ارائه می‌دهد و این مسئله نیز معین می‌شود که آیا مشکلات عملکردی، مانند queueهای طولانی قفل به‌صورت آنی دردسترسی هستند یا نه. با‌این‌حال، آن‌ها اطلاعات Real-time (درلحظه و آنی) درباره نحوه استفاده از سرور را ارائه نمی‌دهند. برای این کار، دستورهای mongostat و mongotop می‌توانند اطلاعات مفیدی ارائه دهند.

گام سوم: استفاده از mongostat و mongotop برای مشاهده آمار Real-time

دستورهای استفاده‌شده برای دسترسی به آمار سرور MongoDB می‌توانند درباره نحوه استفاده از سرور و اطلاعات موجود در دیتابیس اطلاعاتی نمایش دهند؛ اما نمی‌توانند اطلاعاتی درباره این مسئله ارائه دهند که درحال‌حاضر، کدام مجموعه‌ها به‌طور فعال استفاده می‌شوند یا چه نوع کوئری‌هایی در حال اجرا هستند.

MongoDB دو ابزار مفید سیستمی برای نظارت بلادرنگ ارائه می‌دهد که فعالیت دیتابیس را تجزیه‌و‌تحلیل و اطلاعات ارائه‌شده را به‌طور مداوم به‌روز می‌کند: یکی mongostat و دیگری mongotop. ابزار mongostat نمایی کلی از وضعیت فعلی MongoDB ارائه می‌کند؛ اما mongotop مدت‌زمان مصرفی نمونه در عملیات Read و Write را بررسی می‌کند. این ابزارها به‌جای شِل MongoDB از Command Line اجرا می‌شوند.

مشاهده آمار بلادرنگ با mongostat

برای استفاده از mongostat، اتصال شِل MongoDB فعلی خود را حفظ و پنجره ترمینال دیگری را برای دسترسی به شل سرور خود باز کنید. در شلِ سرور دوم، دستور mongostat را اجرا کنید:

mongostat -u AdminSammy --authenticationDatabase admin

همان‌طورکه قبلاً گفتیم، mongostat به دسترسی‌های پیشرفته نیاز دارد. اگر احراز هویت را در نمونه MongoDB فعال و کاربری را با نقش‌های مناسب تعریف کرده‌اید، باید نام کاربری و دیتابیس احراز هویت و درصورت درخواست، رمزعبور آن را وارد کنید (همان‌طورکه در این مثال نشان داده شده است).

در پیکربندی پیش‌فرض، mongostat شمارنده‌های کوئری‌های اجرا‌شده فعلی را در بازه‌های زمانی یک‌ثانیه‌ای چاپ می‌کند:

. . .
    *0    *0     *0     *0       0     1|0  0.0% 38.7%       0 1.54G 210M 0|0 1|0   112b   42.5k    4 Nov 28 15:50:33.294
    *0    *0     *0     *0       0     0|0  0.0% 38.7%       0 1.54G 210M 0|0 1|0   111b   42.2k    4 Nov 28 15:50:34.295
   755    *0     *0     *0       0     1|0  0.1% 38.8%       0 1.54G 210M 0|0 1|0   154k   79.4k    4 Nov 28 15:50:35.294
  2853    *0     *0     *0       0     0|0  0.4% 39.1%       0 1.54G 211M 0|0 1|0   585k    182k    4 Nov 28 15:50:36.295
  2791    *0     *0     *0       0     1|0  0.7% 39.4%       0 1.54G 212M 0|0 1|0   572k    179k    4 Nov 28 15:50:37.293
  2849    *0     *0     *0       0     0|0  1.0% 39.7%       0 1.54G 213M 0|0 1|0   584k    182k    4 Nov 28 15:50:38.296
   745    *0     *0     *0       0     2|0  1.1% 39.8%       0 1.54G 213M 0|0 1|0   153k   79.2k    4 Nov 28 15:50:39.294
    *0    *0     *0     *0       0     0|0  1.1% 39.8%       0 1.54G 213M 0|0 1|0   111b   42.2k    4 Nov 28 15:50:40.295
    *0    *0     *0     *0       0     2|0  1.1% 39.8%       0 1.54G 213M 0|0 1|0   167b   42.7k    4 Nov 28 15:50:41.293
. . .

مقدار صفر در خروجی mongostat برای یک نوع کوئری مشخص، نشان‌دهنده این است که دیتابیس هیچ عملیاتی از آن نوع را اجرا نمی‌کند. خروجی این مثال صفر را برای هر نوع کوئری نشان می‌دهد؛ یعنی درحال‌حاضر هیچ درخواستی به‌طور فعال در حال اجرا نیست.

افزون‌براین، همچنان باید اولین پنجره ترمینال خود را باز و به شِل MongoDB خود متصل کنید. داکیومنت‌های آزمایش بیشتری را در مجموعه accounts وارد و بررسی کنید که آیا mongostat متوجه این فعالیت می‌شود یا خیر:

for (let i = 1; i <= 10000; ++i) {
    db.accounts.insertOne({
        "number": "2000-" + i,
        "currency": "USD",
        "balance": Math.random() * 100000
    })
}

این مثال، یک حلقه for مشابه فرایندی است که در مرحله اول اجرا کردید، با این تفاوت که این بار حلقه تنها ۱۰۰۰۰ ورودی را وارد می‌کند. پیشوند شماره‌حساب‌ها ۲۰۰۰ و currency همیشه USD است.

در‌حالی‌که داکیومنت‌های جدید در حال درج هستند، خروجی دستور mongostat را بررسی کنید:

2021-11-28T15:54:42.290+0000    connected to: mongodb://localhost/

                    ns    total    read    write    2021-11-28T15:54:43Z
    admin.system.roles      0ms     0ms      0ms
  admin.system.version      0ms     0ms      0ms
config.system.sessions      0ms     0ms      0ms
   config.transactions      0ms     0ms      0ms
  local.system.replset      0ms     0ms      0ms
         test.accounts      0ms     0ms      0ms

. . .

حین اجرای کوئری، خطوط جدید نمایش داده‌شده به‌واسطه mongostat مقادیری غیر از صفر را نمایش می‌دهند. در ستون insert، مقادیر در چندین ثانیه بیشتر بودند. این ستون نشان‌دهنده تعداد کوئری‌هایی است که دیتای جدید را وارد دیتابیس می‌کند. mongostat داده‌ها را در فواصل یک ثانیه نشان می‌دهد؛ پس می‌توانید نه‌تنها نسبت insertها رادرمقایسه‌با سایر انواع عملیات دیتابیس، بلکه سرعت insert دیتای جدید به‌وسیله دیتابیس را نیز مشاهده و بررسی کنید. در این مثال، سرور تقریباً به ۳۰۰۰ اینسرت در ثانیه رسیده است.

دیتابیس mongodb چیست
چگونه آمار بلادرنگ را در دیتابیس MongoDB مشاهده کنیم؟

نظارت بر فعالیت سرور به‌صورت گروه‌بندی‌شده با mongoto

از mongostat برای نظارت بر workload فعلیِ سرور دیتابیس به‌صورت گروه‌بندی‌شده و بر‌مبنای نوع کوئری می‌توان استفاده کرد. دومین ابزار برای نظارت بر مونگودی‌بی mongotop است. این ابزار فعالیت سرور دیتابیس را به‌صورت گروه‌بندی‌شده براساس collectionها نشان می‌دهد.

با فشاردادن CTRL + C، اجرای mongostat در پنجره ترمینال دوم را متوقف و سپس، دستور mongotop را در همان ترمینال اجرا کنید. اگر احراز هویت (آتنتیکیشن) را فعال کرده‌اید، باید به‌عنوان کاربری با دسترسی مناسب مجدد احراز هویت کنید:

mongotop -u AdminSammy --authenticationDatabase admin

mongotop فهرستی از تمام collectionهای موجود در دیتابیس را به‌همراه زمان صرف‌شده برای Read و Write و نیز زمان مجموع Time Window را در خروجی نمایش می‌دهد. مشابه mongostat، خروجی هر ثانیه رفرش می‌شود:

Output
2021-11-28T15:54:42.290+0000    connected to: mongodb://localhost/

                    ns    total    read    write    2021-11-28T15:54:43Z
    admin.system.roles      0ms     0ms      0ms
  admin.system.version      0ms     0ms      0ms
config.system.sessions      0ms     0ms      0ms
   config.transactions      0ms     0ms      0ms
  local.system.replset      0ms     0ms      0ms
         test.accounts      0ms     0ms      0ms

. . .

سعی کنید Document بیشتری را در دیتابیس insert کنید تا ببینید آیا گزارش فعالیت در mongotop ثبت می‌شود یا خیر. در شِل MongoDB، حلقه for زیر را اجرا کنید. پس از انجام این کار، پنجره ترمینال را با اجرای دستور mongotop مشاهده کنید:

for (let i = 1; i <= 10000; ++i) {
    db.accounts.insertOne({
        "number": "3000-" + i,
        "currency": "USD",
        "balance": Math.random() * 100000
    })
}

حال گزارش فعالیت‌ در آمارِ mongotop را می‌توانید مشاهده کنید:

Output
. . .
                    ns    total    read    write    2021-11-28T15:57:27Z
         test.accounts    127ms     0ms    127ms
  admin.$cmd.aggregate      0ms     0ms      0ms
    admin.system.roles      0ms     0ms      0ms
  admin.system.version      0ms     0ms      0ms
config.system.sessions      0ms     0ms      0ms
   config.transactions      0ms     0ms      0ms
  local.system.replset      0ms     0ms      0ms

                    ns    total    read    write    2021-11-28T15:57:28Z
         test.accounts    130ms     0ms    130ms
  admin.$cmd.aggregate      0ms     0ms      0ms
    admin.system.roles      0ms     0ms      0ms
  admin.system.version      0ms     0ms      0ms
config.system.sessions      0ms     0ms      0ms
   config.transactions      0ms     0ms      0ms
  local.system.replset      0ms     0ms      0ms
. . .

در این بخش، mongotop نشان می‌دهد که تمام فعالیت‌های دیتابیس در کالکشنی از اکانت‌های دیتابیس test اتفاق افتاده است. تمام عملیاتی که در Time Window انجام می‌شدند، عملیات Write بوده است. همه این موارد باید با عملیات حلقه for هماهنگ باشد که شما اجرا کرده‌اید.

همانند mongostat، می‌توانید با کلیدهای میان‌بر CTRL + C اجرای mongotop را متوقف کنید. هنگامی که در زمان پیک لود قرار دارید، می‌توانید از mongotop برای نظارت بر نحوه انجام فعالیت دیتابیس در collectionهای مختلف استفاده کنید. این کار باعث می‌شود که اسکیما برای مقیاس‌‌بندی راحت‌تر انجام شود. علاوه‌براین، با این اطلاعات متوجه خواهید شد که استفاده از collection بیشتر Read است یا Write.

گام چهارم: استفاده از پروفایلر دیتابیس MongoDB برای شناسایی کوئری‌های کُند

Bottleneckهای عملکرد دیتابیس می‌توانند منابع مختلفی داشته باشند. اگرچه مقیاس‌بندی دیتابیس (افقی یا عمودی) اغلب راه‌حلی برای رفع Bottleneck است، ممکن است دلیل این اتفاق، درواقع محدودیت‌های دیتابیس نباشد و شاید مشکلات به Schema یا دیزاینِ کوئری مربوط باشد.

از دلایل اجرای کُند و طولانی‌مدت کوئری‌ها، استفاده نامناسب از ایندکس‌هاست یا خطاهایی که در خود کوئری ایجاد می‌شوند. بنابراین، کوئری‌های طولانی‌مدت اغلب در طول توسعه برنامه بررسی می‌شوند.
با اجرای Manual کوئری‌های test و بررسی اینکه کدام‌یک از آن‌ها ضعیف هستند، محل ایجاد خطا را می‌توان پیدا کرد؛ اگرچه این کار بسیار خسته‌کننده خواهد بود. خوشبختانه ابزار پروفایلر دیتابیس MongoDB می‌تواند این کار را به‌صورت خودکار انجام دهد.

MongoDB Profiler چیست؟

پروفایلر دیتابیس MongoDB می‌تواند کوئری‌ها و آمار مربوط به اجرای آن‌ها در هنگام مطابقت با شرایطی خاص را ثبت کند (اصطلاحاً log بیندازد). مهم‌ترین شرط زمان اجرای کوئری‌ است. اگر اجرای کوئری‌ بیشتر از زمان مشخصی طول بکشد، پروفایلر به‌طور خودکار آن کوئری‌ را به‌عنوان مشکل‌دار (Problematic) علامت‌گذاری می‌کند. با استفاده از این ویژگی، خواهید توانست کوئری‌هایی با عملکرد ضعیف را تشخیص دهید و سپس، به‌راحتی مشکلات شناسایی‌شده را رفع کنید.

قبل از استفاده از پروفایلر، کوئری زیر را اجرا کنید. این کوئری یکی از اکانت‌های insertشده را بازیابی می‌کند؛ اگرچه فرایند طی‌شده به این سادگی‌ها نیست:

db.accounts.find({"number": "1000-20"})

این دستور دقیقاً Account درخواستی شما را بازیابی می‌کند:

Output
{ "_id" : ObjectId("61a38fd5eedf737ac8e54e96"), "number" : "1000-20", "currency" : "EUR", "balance" : 24101.14770458518 }

شاید کوئری‌ سریعاً اجرا نشود و MongoDB با چند لحظه تأخیر اکانت را پیدا کند. در یک برنامه، ممکن است انواع مختلفی از کوئری‌ها وجود داشته باشند که عملکرد ضعیفی دارند و درعمل، متوجه عملکرد ضعیف آن‌ها نشوید.

MongoDB را می‌توانید طوری پیکربندی کنید که در تشخیص کوئری‌های طولانی، به شما کمک کند. برای انجام این کار، ابتدا با اجرای دستور زیر پروفایلر را فعال کنید:

db.setProfilingLevel(1, { slowms: 100 })

متدِ ()setProfilingLevel دو آرگومان می‌گیرد. ابتدا سطح پروفایل است که می‌تواند ۰ یا ۱ یا ۲ باشد:

  1.  پروفایلر را غیرفعال می‌کند.
  2. پروفایلر را فقط در کوئری‌های واجدشرایط فعال می‌کند.
  3. پروفایلر را برای همه کوئری‌ها فعال می‌کند.

در این مثال، پروفایلر کوئری‌هایی که در بیشتر از ۱۰۰ میلی‌ثانیه اجرا می‌شوند، به همان شیوه‌ای عمیقاً بررسی می‌کند که آرگومان دوم { slowms: 100 } تعریف کرده است.

توجه کنید استفاده از این ویژگی میزان عملکرد را کاهش می‌دهد؛ زیرا MongoDB باید علاوه‌بر اجرای کوئری‌ها، آن‌ها را تجزیه‌وتحلیل کند. بنابراین، گه‌گاهی باید از آن هنگام وقوع bottleneck‌ در مانیتورینگ عملکرد بهره برد.

این روش پیغام موفقیت‌آمیز زیر را برمی‌گرداند:

Output
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }

از‌این‌پس، نمایه‌سازی دیتابیس فعال خواهد شد. MongoDB به‌طور فعال بر اجرای هر کوئری نظارت می‌کند تا درخواستی را پیدا کند که تکمیل آن بیش از ۱۰۰ میلی‌ثانیه طول می‌کشد.

این فرایند را با اجرای چند کوئری‌ مختلف امتحان کنید. ابتدا از دستور count برای یافتن تعداد داکیومنت موجود در کالکشن اکانت‌ها استفاده کنید:

db.accounts.count()

این دستور به‌سرعت تعداد داکیومنت موجود در مجموعه را برمی‌گرداند:

Output
1020000

سپس، سه حساب بانکی اول موجود در مجموعه را جست‌وجو کنید:

db.accounts.find().limit(3)

بازهم دیتابیس نتایج را به‌سرعت برمی‌گرداند:

Output
{ "_id" : ObjectId("61ef40640f2ba52efc56ee17"), "number" : "1000-1", "currency" : "EUR", "balance" : 25393.132960293842 }
{ "_id" : ObjectId("61ef40640f2ba52efc56ee18"), "number" : "1000-2", "currency" : "EUR", "balance" : 63629.42056192393 }
{ "_id" : ObjectId("61ef40640f2ba52efc56ee19"), "number" : "1000-3", "currency" : "EUR", "balance" : 75602.12331602155 }

درنهایت، بار‌دیگر عبارت جست‌وجوی حساب بانکی خاص را اجرا کنید:

db.accounts.find({"number": "1000-20"})

این کوئری نتیجه را برمی‌گرداند؛ اما مانند دفعه قبل، یک یا دو لحظه بیشتر از عملیات قبلی طول می‌کشد:

Output
{ "_id" : ObjectId("61a38fd5eedf737ac8e54e96"), "number" : "1000-20", "currency" : "EUR", "balance" : 24101.14770458518 }

حتی اگر کوئری‌ کُندتر از این می‌بود، پروفایلر از خود Output تولید نمی‌کرد؛ اما جزئیات مربوط به عملیات کُند در مجموعه‌ای ویژه در دیتابیس به نام system.profile ثبت می‌شود. این مجموعه Capped Collection است که حجم آن هرگز از ۱ مگابایت تجاوز نمی‌کند. این بدان‌معناست که همیشه حاوی فهرستی از آخرین کوئری‌های کُند خواهد بود و باقی را نگه نخواهد داشت.

برای بازیابی اطلاعات مربوط به کوئری‌های شناسایی‌شده نمایه‌ساز، باید مجموعه system.profile را به روش زیر جست‌وجو کنید:

db.system.profile.find().sort({ "ts" : -1 }).pretty()

این کوئری طبق معمول از متد ()find استفاده می‌کند. همچنین، شامل یک قسمت مرتب‌سازی است که حاوی { “ts” : -1 } به‌عنوان آرگومان است. این کار ابتدا مجموعه نتایج را با آخرین کوئری‌ها مرتب می‌کند. درادامه، متد()pretty در پایان خروجی را با فرمت خواناتری نمایش می‌دهد.

هر query کُند به‌عنوان یک داکیومنت نشان داده می‌شود و system.profile مانند هر collection معمولی است. این یعنی می‌توانید نتایج را فیلتر و مرتب کنید و حتی از آن‌ها در aggregation pipelineها برای محدود‌کردن یا تجزیه‌و‌تحلیل بیشتر فهرست جست‌وجوهای شناسایی‌شده پروفایلر استفاده کنید.

توجه کنید که نتیجه فقط شامل یک داکیومنت است. دو کوئری دیگر به‌اندازه کافی سریع اجرا شده‌اند که پروفایلر را فعال نکنند:

Output
{
        "op" : "query",
        "ns" : "test.accounts",
        "command" : {
                "find" : "accounts",
                "filter" : {
                        "number" : "1000-20"
                },
                . . .
        },
        "nreturned" : 1,
        "keysExamined" : 0,
        "docsExamined" : 1030000,
        . . .
        "millis" : 434,
        "planSummary" : "COLLSCAN",
        . . .
}

جزئیات ارائه‌شده در Output

این خروجی تعدادی جزئیات درباره اجرای کُند کوئری ارائه می‌دهد:

  • در فیلد millis، زمان دقیقی را خواهید یافت که برای تکمیل کوئری‌ طول کشیده است.
  • فیلد docsExamined تعداد داکیومنت اسکن‌شده برای برگرداندن مجموعه نتایج را ارائه می‌دهد.
  • کلید op نشان‌دهنده نوع عملیات است. در این‌جا، این یک کوئری‌ است؛ زیرا عملیاتی را نشان می‌دهد که در آن از ()find برای بازیابی داده‌های دیتابیس استفاده کرده‌اید.
  • nreturned نشان‌دهنده تعداد داکیومنتی است که کوئری برگردانده است. در این مثال، تنها یک doc از میان بیش از یک‌میلیون داکیومنت اسکن‌شده بازگردانده شده است.
  • کلید ns نشان می‌دهد که کدام دیتابیس و کالکشن در این عملیات نقش ایفا کرده‌اند. همان‌طور‌که خروجی نشان می‌دهد، این عملیات مجموعه accounts را در دیتابیسِ تست کوئری‌ کرد.
  • PlanSummary متدی را نشان می‌دهد که MongoDB برای اجرای کوئری‌ استفاده کرده است. COLLSCAN به اسکن کامل مجموعه مربوط است؛ یعنی تمام داکیومنت‌های موجود در کالکشن را تک‌تک مرور کرد تا حساب بانکی منطبق را پیدا کند.
  • کلید command اطلاعات بیشتری درباره خودِ کوئری‌ ارائه می‌دهد. در این حالت، کلید فرعی filter حاوی کل داکیومنتِ فیلتر است. با استفاده از اطلاعاتی که از فیلدهای op و command به‌دست می‌آید، می‌توانید کوئری مدنظر را بازسازی کنید.

درمجموع، این اطلاعات بر نیاز به نمایه‌ای تأکید می‌کند که می‌تواند به MongoDB در اجرای سریع‌تر این کوئری‌ کمک کند. در این مثال خاص، ایجاد ایندکسی برای پشتیبانی از کوئری‌ها که داده‌ها را براساس فیلد شماره فیلتر می‌کند، عملکردشان را افزایش می‌دهد. در سناریوهای واقعی، برای کوئری‌های کُند ممکن است راهکارها متفاوت باشد. همچنین، به کوئری‌ای‌ بستگی دارد که سبب بروز مشکل شده است.

برای اتمام سشنِ Profiling، می‌توانید با تنظیم سطح پروفایل روی صفر، پروفایلر را غیرفعال کنید:

db.setProfilingLevel(0)

نتیجه همراه با پیام، موفقیت‌آمیز‌بودن فرایند را نشان خواهد داد:

Output
{ "was" : 1, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }

اکنون دیتابیس به حالت عادی بازمی‌گردد، بدون اینکه Profiling در پشت‌صحنه اتفاق بیفتد.

ساخت دیتابیس در mongodb
ازطریق پروفایلر چگونه کوئری‌های کند را شناسایی کنیم؟

جمع‌بندی

با دنبال‌کردن این آموزش، نحوه یافتن استاتیک‌های سرور MongoDB و استفاده از ابزارهای تشخیصی مانند mongotop و mongostat و نیز مکانیسم پروفایلر دیتابیس MongoDB را یاد خواهید گرفت. از این موارد می‌توانید برای درک بهتر workload دیتابیس خود و تعیین‌کردن فعال‌ترین مجموعه‌ها و اینکه آیا سرور عمدتاً Write را انجام می‌دهد یا Read، استفاده کنید. همچنین، کوئری‌های کُند تأثیرگذار بر عملکرد MongoDB را می‌توانید شناسایی کنید تا آن‌ها را با موارد کارآمدتر جایگزین کنید.

این موارد فقط مجموعه‌ای از ابزارها و تکنیک‌هایی هستند که می‌توانید از آن‌ها برای نظارت بر سلامت و عملکرد نصب MongoDB خود استفاده و براساس آن عمل کنید. هریک از این ابزارها را می‌توانید پیکربندی و سفارشی کنید تا اطلاعات هدفمندتری درباره عملکرد سرور به شما ارائه دهند. برای این کار، می‌توانید داکیومنت‌های رسمی MongoDB را نیز بررسی کنید.

سؤالات متداول

1. چگونه بر کوئری‌ها در MongoDB نظارت کنیم؟

از ()db. setProfilingLevel برای ثبت کوئری‌های کُند یا همه آن‌ها و سپس، از ElasticSearch + Kibana + Logstash برای تجزیه‌وتحلیل و نظارت بر کوئری‌های mongoDB استفاده کنید.

2. عملکرد MongoDB چیست؟

MongoDB بهترین دیتابیسِ داکیومنت‌های NoSQL برای توسعه‌دهندگان پیشرفته‌ای است که روی برنامه‌های کاربردی با کارایی فراوان کار می‌کنند. MongoDB با اسناد JSON مانند خود برای مقیاس‌بندی افقی و تعادل لود مهم است و به توسعه‌دهندگان تعادل عالی از سفارشی‌سازی و مقیاس‌پذیری ارائه می‌دهد.

3. DB در دستور MongoDB چیست؟

MongoDB از DATABASE_NAME برای ایجاد پایگاه‌ داده استفاده می‌کند. این دستور درصورت وجودنداشتن، دیتابیس جدید ایجاد می‌کند؛ وگرنه پایگاه داده موجود را برمی‌گرداند.

4. آیا MongoDB از SQL کُندتر است؟

MongoDB در پردازش کوئری‌ها سریع‌تر است؛ اما این سرعت زیاد با افزایش Load و System Requirement امکان‌پذیر است. درمجموع، باید بگوییم که بدون دانستن هدف استفاده، نمی‌توان دیتابیس‌های SQL یا پایگاه‌های داده NoSQL مانند MongoDB را به‌عنوان بهتر یا بدتر از دیگری طبقه بندی کرد.

5. آیا MongoDB می‌تواند جایگزین MySQL شود؟

MySQL و MS SQL Oracle و Server تقریباً مترادف با RDBMS هستند؛ اما MongoDB دیتابیسی مبتنی‌بر داکیومنتِ Cross-platform و NoSQL است. گاهی اوقات، جایگزینی MySQL با MongoDB ممکن است تصمیم عاقلانه‌ای باشد؛ زیرا مونگودی‌بی دیتابیس باهوشی است که امکان تغییرات سریع فریم‌ورک‌های شناخته‌شده را هنگام تکامل اپلیکیشن‌ها فراهم می‌کند.