Mysqlnd و کوئری های buffer شده: داده های حجیم

25 بهمن 1397
درسنامه درس 15 از سری آموزش PDO
PDO-big-data

با قسمت آخر از دوره ی آموزشی کار با رابط PDO در خدمت شما هستیم. در این قسمت به سراغ مبحث mysqlnd و کوئری های بافر شده می رویم.

Mysqlnd و کوئری های buffer شده: داده های حجیم

اخیرا تمام extension های PHP که با پایگاه داده ی MySQL کار می کردند بر اساس یک کتابخانه ی سطح پایین (low-level) به نام mysqlnd بروز رسانی شده و جایگزین کلاینت libmysql شد. این بروزرسانی باعث بروز برخی تغییرات در رفتار PDO شد.

یکی از این تغییرات بحث buffered queries (به معنی کوئری های بافر شده) است. احتمالا تا به حال این نام به گوشتان نخورده است اما مطمئن باشید که بارها از این قابلیت استفاده کرده اید. متاسفانه خبر بد این است که برخلاف نسخه های قدیمی PHP که در آن از کوئری های بافر شده به شکل آزادانه استفاده می کردیم، نسخه های جدید که بر پایه ی mysqlnd driver هستند چنین اجازه ای به شما نمی دهند.

When using libmysqlclient as library PHP's memory limit won't count the memory used for result sets unless the data is fetched into PHP variables. With mysqlnd the memory accounted for will include the full result set. source: php.net

ترجمه:

هنگامی که از libmysqlclient به عنوان کتابخانه استفاده می کنید، محدودیت حافظه ی PHP، حافظه ی اشغال شده توسط result set ها را نادیده می گیرد مگر آنکه داده ها از طریق یک متغیر fetch شوند. چنانچه از mysqlnd استفاده کنید، حافظه شامل تمام result set خواهد بود.

تمام این مسئله در رابطه با یک result set است. اگر یادتان باشد در جلسات قبل اشاره کردیم که result set ها (به معنی مجموعه نتایج) تمام ردیف هایی هستند که توسط یک کوئری پیدا می شوند.

هنگامی که کوئری SELECT اجرا می شود، دو راه برای ارائه ی نتایج در اسکریپت شما وجود دارد:

  • روش بافر شده (buffered)
  • روش بافر نشده (unbuffered)

هنگامی از روش بافر شده استفاده می شود، تمام داده هایی که توسط کوئری برگشت داده می شوند، به صورت یک جا وارد حافظه (memory) اسکریپت می شوند اما در حالت بافر نشده یک پایگاه داده ردیف های پیدا شده را تک به تک تحویل اسکریپت می دهد.

بر همین اساس می توان گفت در حالت بافر شده، result set ها همیشه باری به دوش حافظه و سرور هستند، حتی اگر fetching شروع نشده باشد. به همین خاطر است که پیشنهاد می شود بیشتر از نیازتان درخواست نکنید و اگر به طور مثال به 20 نتیجه نیاز دارید، طوری کد بنویسید که تنها 20 نتیجه را دریافت کنید، نه اینکه 50 نتیجه را دریافت کرده و سپس 20 تا از آن ها را جدا کنید.

البته این مشکل، زمانی که کلاینت های قدیمی و برپایه ی libmysql استفاده می شدند، زیاد به چشم کاربران PHP نمی آمد چرا که حافظه ی اشغال شده توسط result set در دستورات ()memory_get_usage  و memory_limit محاسبه نمی شد، اما زمانی که mysqlnd معرفی شد همه چیز تغییر کرد؛ هر راهی را برای دریافت نتایج انتخاب کنید باز هم result set ای که توسط کوئری بافر شده برگردانده می شود در دستورات ()memory_get_usage  و memory_limit محاسبه خواهد شد.

به طور مثال ما هر دو حالت را روی یک سری داده پیاده سازی کردیم:

$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, FALSE);
$stmt = $pdo->query("SELECT * FROM Board");
$mem = memory_get_usage();
while($row = $stmt->fetch());
echo "Memory used: ".round((memory_get_usage() - $mem) / 1024 / 1024, 2)."M\n";

$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, TRUE);
$stmt = $pdo->query("SELECT * FROM Board");
$mem = memory_get_usage();
while($row = $stmt->fetch());
echo "Memory used: ".round((memory_get_usage() - $mem) / 1024 / 1024, 2)."M\n";

خروجی به شکل زیر خواهد بود:

Memory used: 0.02M
Memory used: 2.39M

بنابراین در کوئری های بافر شده، حتی اگر ردیف ها را تک به تک و دانه دانه دریافت کنید، حافظه باز هم اشغال خواهد شد!!

بر این اساس حتما یادتان باشد که هنگام انتخاب داده های بسیار بزرگ و حجیم، مقدار PDO::MYSQL_ATTR_USE_BUFFERED_QUERY را روی FALSE قرار دهید. البته این کار معایبی نیز دارد که مهم ترین آن ها عبارت اند از:

  • در کوئری های بافر نشده، حرکت دادن pointer داخلی در result set ها ممکن نیست (که باز هم بی اهمیت است1).
  • در کوئری های بافر نشده، نمی توانید از ()rowCount استفاده کنید (که تقریبا هیچ اهمیتی ندارد2).
  • در این حالت، هنگامی که یک کوئری بافر نشده فعال باشد نمی تواند از همان connection (اتصال) برای اجرای دیگر کوئری ها استفاده کنید3.

1- اگر با pointer ها آشنایی ندارید به قسمت "دریافت داده ها از طریق ()fetch" در مقاله ی انواع دریافت داده ها در PDO – قسمت اول مراجعه کنید.

2- در قسمت های قبل توضیح دادیم که رابط PDO تابعی به نام ()PDOstatement::rowCount ارائه می دهد که تعداد ردیف های پیدا شده توسط کوئری را به شما برمیگرداند. این تابع بسیار به ندرت استفاده می شود و اگر بخواهم صادقانه تر بگویم احتمالا هیچ وقت در برنامه های عادی خود از آن استفاده نکنید. توسعه دهندگان مبتدی و حتی بعضا حرفه ای بیشتر اوقات برای شمردن چیزی از آن استفاده نمی کنند بلکه با آن چک می کنند تا ببینند داده ای برگشت داده شده است یا نه. این کار اشتباهی است! چرا که برای چنین مواقعی خود داده را دارید! یعنی از دستورات ()fetch و ()fetchAll استفاده کنید و دقیقا همین کار را برایتان انجام می دهند. برای مطالعه ی بیشتر به قسمت اول از مقاله ی ردیف ها (Rows) در پایگاه داده PDO مراجعه کنید.

3- با اینکه دو مورد اول مهم نبودند اما این مورد می تواند بسته به کار شما مهم باشد، بنابراین خوب به این مورد توجه کنید.

کلام پایانی

این قسمت، قسمت آخر از سری آموزشی رابط شیء گرای PDO بود و دوست دارم صمیمانه از شما که در این دوره همراه من بودید تشکر کنم. همانطور که قبلا به شما توضیح داده بودم، این دوره تنها به شکل تئوری طراحی نشده و در مباحث دوره از سوال های Stack Overflow و تجریبات برنامه نویسان قدیمی و حرفه ای در زمینه ی PDO استفاده شده است به همین دلیل شما تا اینجای کار دانش عملی بسیار خوبی (در حدود 90 درصد و بیشتر) از PDO دارید؛ امیدوارم بتوانید از دانش خود در زمینه های عملی و در بازار کار استفاده کنید.

دوست دارم قبل از پایان این جلسه به سوالات متداول شما پاسخ بدهم:


سوال: آیا این پایان PDO است و دیگر مقاله ای در این زمینه نخواهیم داشت؟

پاسخ: قطعا مقالات دیگری در زمینه ی PDO خواهم نوشت، مخصوصا مقالات عملی تر به همراه مثال و شاید مینی پروژه های دیگر. دلیل اینکه چنین مقالاتی را وارد سری آموزشی PDO نمی کنم این است که این مقالات به سیر آموزشی PDO مربوط نیستند و در واقع فوت های کوزه گری، نمونه کد های آماده، اشکالات رایج و ... خواهند بود که برای تازه کاران چندان مناسب نیست. مخاطب این مقالات افرادی هستند که با PDO آشنایی کامل داشته باشند.

سوال: چرا این دوره کوتاه بود؟ فکر می کردم PDO سخت تر از این حرف ها باشد!

پاسخ: متاسفانه باور اکثر برنامه نویسان همین است. برخلاف باور عمومی، مبحث PDO بسیار شیرین و کوتاه است چرا که پایه های آن، همان پایه های mysql و mysqli می باشد. در حقیقت شما چیز جدیدی یاد نمی گیرید بلکه دانش قبلی خود را بروزرسانی می کنید یا روش جدیدی برای استفاده از دانش قبلی خود پیدا می کنید و از همین جهت نیازی به دگرگون کردن ذهن خود ندارید. باور کنید یا نکنید، PDO به همین کوتاهی و سادگی است!

سوال: آیا با دانش فعلی خود می توانم وارد بازار کار شوم؟

پاسخ: بله! با کمی تمرین، کدنویسی شخصی و بررسی کدهای مختلف می توانید به راحتی وارد بازار کار شوید. خیالتان بابت امنیت استفاده از PDO نیز راحت باشد، از هر نظر چندین برابر از mysql و mysqli امن تر است.

سوال: آیا استفاده از PDO اجباری است؟

پاسخ: بگذارید اینطور بگویم، استفاده از mysql ممنوع! چرا که این رابط به طور کل منسوخ شده و حتی خود PHP نیز دیگر از آن پشتیبانی نمی کند. بین mysqli و pdo می توانید از هر کدام که خواستید استفاده کنید اما یادتان باشد که بین پراید و BMW نیز می توانید سوار هر کدام که خواستید بشوید! تنها مجوز استفاده از چیزی، آن را بهتر از بقیه ی چیز ها نمی کند، حکیمانه تصمیم بگیرید. بعید نیست که بعد از چند سال mysqli هم منسوخ شود!

امیدوارم از این سری آموزشی لذت برده باشید.

در پناه حق

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش PDO توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (4 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

star
05 دی 1398
سلام ممنون از مطالب خوبتون من یه پایگاه داده دارم با سه تا جدول که باید جداول رو به هم ربط بدم می خواستم ببینم من اگه بخوام با PDO این پروژه رو انجام بدم که امنیتش بیشتر باشه باید چکار کنم شما مطلبی در این مورد دارین؟ ممنون میشم منو راهنمایی کنید

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
06 دی 1398
سلام دوست عزیز ایجاد foreign key و ارتباط جدول ها با همدیگه بر عهده ی خود MySQL هست و از حیطه ی PHP یا رابط PDO خارج میشه. شما باید از طریق PHPMyAdmin یا به صورت دستی برای جدول هاتون foreign key و اینطور چیز ها رو تعریف کنید. من سعی می کنم یک پروژه ی جدید واقعی برای کار با PDO منتشر کنم تا به صورت عملی کار رو یاد بگیرید.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

صالح حمیدیان
01 اسفند 1397
دوره ی بسیار جامعی بود. از شما تشکر می کنم. نکته ی عالی این دوره این بود که جناب عالی اصطلاحات انگلیسی را نیز ذکر کرده بودید. بنده به شخصه متوجه شده ام که دانش انگلیسی ام در زمینه ی برنامه نویسی بالا رفته است.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
01 اسفند 1397
سلام خدمت شما دوست عزیز خوشحالم که تونستم کمکی کرده باشم به شما دوست عزیز. نظر لطف شماست.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

Ahmad Tavakoli
26 بهمن 1397
خواستم ازت تشکر کنم امیر اقا واقعا خوب بود. فقط یه سوال دارم شما گفتی pdo از mysqli بهتره. من میتونم با اکستنشن های مختلف mysqli رو قدرتمند تر از pdo بکنم. فکر می کنم این حرفت اشکال داره

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
27 بهمن 1397
سلام دوست عزیز اینکه میشه با کتابخانه های جداگانه و مختلف mysqli رو بهتر از PDO کرد حرف درستیه اما ما داریم PDO و mysqli رو در شرایط یکسان و بدون استفاده از هیچ کد خارجی می سنجیم. خیلی از برنامه نویس ها اعتقاد دارن که اصلا mysqli برای استفاده بدون اکستنشن و کتابخانه و این چیز ها ساخته نشده. بنابراین حرف شما درسته اما برای مقایسه ی دو چیز، ما حالت خالص اون ها رو در نظر می گیریم. مثلا وقتی می خوایم در مسابقات ورزشی بین دو ورزشکار داوری کنیم، اون ها رو در حالت عادی و بدون استفاده از داروی های استروئیدی و دوپینگ و غیره مقایسه می کنیم. نمیشه گفت فلان ورزشکار اگر دوپینگ کنه قوی تر میشه! بله معلومه که قوی تر میشه ولی مبنای مقایسه دیگه عادلانه نیست.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

مهدی قائمی
26 بهمن 1397
بسیار دوره ی عالی بود جناب زوارمی بنده به شخصه لذت فراوان بردم و بسیار از موارد برایم رفع شد. تشکر از شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

امیر زوارمی
27 بهمن 1397
خوشحالم که براتون مفید بوده ...

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.