ویژگی تصویر

تابع use_result() در PHP

  /  PHP   /  تابع use_result() در PHP
بنر تبلیغاتی الف
آموزش PHP

در اکوسیستم MySQLi (رابط MySQL در PHP) تابع use_result() راهی برای بازیابی نتایج به صورت unbuffered یا جریانی (streaming) فراهم می‌کند. به عبارت ساده‌تر، با use_result() سرور ردیف‌ها را به‌تدریج ارسال می‌کند و کل مجموعه نتایج را در حافظهٔ کلاینت ذخیره نمی‌کند. این حالت برای پردازش نتایج بسیار بزرگ یا کاهش مصرف حافظه مفید است.

کجا و چرا از use_result() استفاده کنیم؟

  • وقتی نتیجهٔ کوئری خیلی بزرگ است (میلیون‌ها ردیف) و نمی‌خواهیم تمام آن را در حافظهٔ PHP نگه داریم.
  • برای پردازش تدریجی داده‌ها (مثلاً خواندن و نوشتن به فایل، پردازش خط‌به‌خط یا ارسال به یک API خارجی).
  • کاهش مصرف حافظهٔ سرور و جلوگیری از شکست PHP به‌خاطر Out Of Memory.

تفاوت کلیدی با store_result()

باید تفاوت بین buffered و unbuffered را بدانید:

ویژگیuse_result() (Unbuffered)store_result() / پیش‌فرض (Buffered)
حافظهٔ کلاینتکم — داده‌ها به‌تدریج خوانده می‌شوندزیاد — تمام نتیجه در حافظه ذخیره می‌شود
قابلیت اجرای کوئری دیگر روی همان اتصالتا زمان خواندن یا آزادسازی نتایج، امکان اجرای کوئری جدید وجود نداردبله — چون نتیجه کامل در کلاینت است
استفادهٔ معمولنتایج بزرگ، پردازش خط‌به‌خطنتایج کوچک تا متوسط، نیاز به دسترسی تصادفی

نحوهٔ استفاده — مثال‌های عملی

نمونه‌ی شیء‌گرا (object-oriented):

$mysqli = new mysqli('localhost', 'user', 'pass', 'db');

if ($mysqli->connect_errno) {
    die("Connect failed: " . $mysqli->connect_error);
}

$sql = "SELECT id, name, data FROM large_table";
if ($result = $mysqli->use_result()) {
    while ($row = $result->fetch_assoc()) {
        // پردازش هر ردیف به‌صورت تک‌تک
        echo $row['id'] . " - " . $row['name'] . "n";
    }
    $result->free(); // آزادسازی نتیجه
} else {
    echo "Query failed: " . $mysqli->error;
}

$mysqli->close();

توضیح: در این مثال کد با use_result() یک نتیجهٔ غیرپیمایشی (unbuffered) می‌گیرد و سپس با fetch_assoc() ردیف‌ها را یکی‌یکی می‌خواند. پس از اتمام باید نتیجه با free() آزاد شود. تا زمانی که همهٔ ردیف‌ها خوانده نشده یا free() صدا زده نشده، نمی‌توان کوئری دیگری روی همان اتصال اجرا کرد.

نمونهٔ معادل به‌صورت تابعی (procedural):

$link = mysqli_connect('localhost', 'user', 'pass', 'db');

$sql = "SELECT * FROM huge_table";
$res = mysqli_query($link, $sql, MYSQLI_USE_RESULT);

while ($row = mysqli_fetch_assoc($res)) {
    // پردازش ردیف
    var_dump($row);
}

mysqli_free_result($res);
mysqli_close($link);

توضیح: در نسخهٔ تابعی می‌توان با سومین پارام‌تر mysqli_query، flag = MYSQLI_USE_RESULT را مشخص کرد تا نتیجه unbuffered حاصل شود. روند پردازش مشابه است و باید نتیجه آزاد شود.

نکات مهم و محدودیت‌ها

  • وقتی از use_result() استفاده می‌کنید، تا زمانی که همهٔ ردیف‌ها خوانده نشده یا mysqli_free_result فراخوانی نشده باشد، اجازهٔ اجرای کوئری جدید روی همان اتصال وجود ندارد. بنابراین یا همهٔ داده‌ها را بخوانید یا فوراً free() را صدا بزنید تا اتصال آزاد شود.
  • برای کوئری‌های چندگانه (multi-query) یا زمانی که همزمان نیاز به اجرای کوئری‌های دیگری دارید، buffered (store_result) مناسب‌تر است.
  • get_result() برای prepared statements تابع مفیدی است اما تنها در صورت استفاده از درایور mysqlnd در دسترس است. اگر mysqlnd ندارید باید از bind_result/fetch یا store_result استفاده کنید.
  • برای دسترسی به تعداد ردیف‌ها (num_rows) در حالت unbuffered باید یا تمام ردیف‌ها را خوانده باشید یا از روش‌های دیگری استفاده کنید؛ در حالت buffered این مقدار سریع در دسترس است.

مثال پیشرفته: نوشتن نتایج بزرگ به فایل

$mysqli = new mysqli('localhost', 'user', 'pass', 'db');
$file = fopen('/tmp/output.csv', 'w');
$res = $mysqli->query("SELECT id, name, value FROM huge_table", MYSQLI_USE_RESULT);

while ($row = $res->fetch_assoc()) {
    fputcsv($file, $row);
}

$res->free();
fclose($file);
$mysqli->close();

توضیح: در این نمونه داده‌ها مستقیماً به فایل CSV نوشته می‌شوند و هرگز تمام نتیجه در حافظهٔ PHP بارگذاری نمی‌شود؛ مناسب برای صادرات داده‌های بسیار بزرگ.

بهینه‌سازی و بهترین شیوه‌ها

  • اگر فقط نیاز به تعداد محدودی ردیف دارید، در کوئری LIMIT قرار دهید—نیازی به use_result در آن حالت نیست.
  • برای کارهای دسته‌ای، بهتر است نتیجه را به بلوک‌هایی تقسیم کنید (مثلاً با WHERE + LIMIT و مقدار offset یا شناسهٔ آخر خوانده‌شده) و هر بار بخش کوچکی را پردازش کنید تا قفل‌های بلندمدت روی اتصال ایجاد نشود.
  • همیشه نتیجهٔ unbuffered را پس از پایان یا در صورت خطا آزاد کنید تا منابع آزاد شوند.
  • در صورت نیاز به پردازش موازی یا اجرای همزمان کوئری‌ها از اتصالات جداگانه (pool) استفاده کنید.

جمع‌بندی

تابع use_result() ابزاری قدرتمند برای پردازش نتایج بزرگ به‌صورت جریان است و هنگامی که حافظهٔ محدود است یا می‌خواهید پردازش خط‌به‌خط انجام دهید بسیار مناسب است. با این حال باید محدودیت‌های مرتبط با عدم توانایی اجرای کوئری جدید روی همان اتصال و نیاز به آزادسازی صریح نتایج را در نظر بگیرید. برای prepared statements و سناریوهای دیگر نیز ترکیب get_result، store_result و bind_result/fetch را بسته به نیاز خود انتخاب کنید.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: