تابع reap_async_query() در PHP
در برنامهنویسی وب، بهویژه هنگام کار با پایگاهدادههای بزرگ یا کوئریهای زمانبر، اجرای همزمان (concurrent) یا ناهمزمان (asynchronous) کوئریها میتواند عملکرد را بهطرز چشمگیری بهبود دهد. یکی از ابزارهای موجود در اکوسیستم PHP برای این منظور، تابع reap_async_query() در MySQLi است. در این مقاله به زبان ساده و فنی این تابع، نحوه استفاده، مثالهای عملی، محدودیتها و بهترین روشها را توضیح میدهیم.
چیست و چرا کاربرد دارد؟
reap_async_query() (یا متد mysqli::reap_async_query در سبک شیگرا) برای دریافت نتیجهی یک کوئری ناهمزمان اجرا شده با MYSQLI_ASYNC استفاده میشود. ترکیب ارسال ناهمزمان کوئری و سپس «reap» کردن نتایج به شما اجازه میدهد چند کوئری را همزمان ارسال کنید و سپس وقتی هرکدام آماده شد، نتیجهاش را بگیرید — بدون مسدود شدن جریان اصلی اجرای اسکریپت.
پیشنیازها و نکات فنی
- نیاز به درایور mysqlnd دارند. با libmysqlclient ممکن است درست کار نکند.
- باید از تابع mysqli_query یا روشهای مرتبط با پرچم MYSQLI_ASYNC برای ارسال کوئری استفاده کنید.
- هر کوئری ناهمزمان باید روی یک اتصال (connection) اجرا شود؛ برای ارسال همزمان به چند سرور یا چند اتصال، باید چند شیٔ mysqli ایجاد کنید.
- برخی قابلیتها مانند prepared statements ناهمزمان بهصورت مستقیم پشتیبانی نمیشوند (بسته به نسخهها و پیادهسازی).
مثال عملی: ارسال چند کوئری همزمان و دریافت نتایج
// ایجاد اتصالات
$links = [];
$hosts = ['localhost','localhost','localhost']; // میتواند سرورهای متفاوت باشد
foreach ($hosts as $i => $host) {
$links[$i] = new mysqli($host, 'user', 'pass', 'dbname');
if ($links[$i]->connect_errno) {
die("Connect failed: " . $links[$i]->connect_error);
}
}
// ارسال کوئریها بهصورت ناهمزمان
$sqls = [
"SELECT COUNT(*) AS c FROM large_table WHERE condition1",
"SELECT id, name FROM another_table WHERE something",
"CALL long_running_procedure()"
];
foreach ($links as $i => $link) {
$link->query($sqls[$i], MYSQLI_ASYNC);
}
// لیست خواندیها برای mysqli_poll
$read = $links;
$errors = $reject = [];
$timeout_secs = 5;
// منتظر شدن تا یکی از نتایج آماده شود
while (count($read)) {
$ready = mysqli_poll($read, $errors, $reject, $timeout_secs);
if ($ready > 0) {
foreach ($read as $k => $link) {
$result = $link->reap_async_query();
if ($result === false) {
// خطا در اجرای کوئری
error_log("Async query error on link $k: " . $link->error);
} else {
// پردازش نتیجه
while ($row = $result->fetch_assoc()) {
var_dump($row);
}
$result->free();
}
// حذف لینکای که پردازش شده تا از حلقه خارج شود
unset($read[$k]);
}
} else {
// timeout یا هیچ لینکی آماده نیست — میتوان کار دیگری انجام داد
// مثلاً لاگ، بروزرسانی وضعیت، یا کاهش timeout
}
}
// بستن اتصالات
foreach ($links as $link) {
$link->close();
}
این کد چند اتصال میسازد، کوئریها را با پرچم MYSQLI_ASYNC ارسال میکند، سپس با mysqli_poll منتظر میماند تا یکی از آنها آماده شود. بعد با reap_async_query نتیجه را میگیرد و پردازش میکند.
مثال ساده: یک کوئری ناهمزمان روی یک اتصال
$link = new mysqli('localhost','user','pass','dbname');
$link->query("SELECT SLEEP(3), NOW()", MYSQLI_ASYNC);
// انجام کارهای دیگر در بینِ اجرای کوئری
// ...
// سپس گرفتن نتیجه
$result = $link->reap_async_query();
if ($result) {
while ($row = $result->fetch_row()) {
print_r($row);
}
$result->free();
} else {
echo "Error: " . $link->error;
}
$link->close();
در این سناریو مقادیر غیر مسدودکننده اجرا میشوند: میتوانید کارهای دیگری مثل پردازش فایل یا درخواستهای خارجی را انجام داده و سپس نتیجه را بخوانید.
محدودیتها و تلههای رایج
- برای هر کوئری ناهمزمان باید اتصال جداگانه نداشته باشید؛ اگر میخواهید چند کوئری واقعی همزمان روی یک کانکشن اجرا کنید، این کار ممکن نیست؛ باید چند اتصال باز کنید.
- پشتیبانی تابع وابسته به mysqlnd است؛ در هاستهایی که libmysqlclient استفاده میشود، عملکرد ممکن است متفاوت باشد.
- در صورت استفاده نادرست ممکن است منابع سرور (اتصالات باز، حافظه) بهسرعت مصرف شود. مدیریت و بستن نتایج و اتصالات ضروری است.
- مدیریت تراکنشها (transactions) در کوئریهای ناهمزمان پیچیدهتر است؛ اگر در تراکنش هستید، نیاز به دقت دارید.
بهترین روشها و نکات بهینهسازی
- حداقل تعداد اتصال لازم را باز کنید؛ در تولید، از یک пул اتصال یا تکنیکهای مشابه بهره ببرید.
- برای گزارشها یا کوئریهای زمانبر، از کوئریهای ناهمزمان همراه با صفبندی (job queue) استفاده کنید تا منابع سرور کنترل شود.
- همیشه نتایج را free کنید تا حافظه آزاد شود.
- از mysqli_poll با timeout مناسب استفاده کنید تا اسکریپتها در حالت انتظار نامحدود گیر نکنند.
- اگر نیاز به همزمانی پیشرفته دارید، مطالعه بستههایی مثل Swoole یا ReactPHP که سطح بالاتری از همزمانی را فراهم میکنند، مفید است.
مقایسه کوتاه: synchronous vs asynchronous
| ویژگی | همگام (Synchronous) | ناهمزمان (Asynchronous) |
|---|---|---|
| زمان انتظار | مسدودکننده | غیرمسدود، امکان انجام کارهای دیگر |
| پیچیدگی پیادهسازی | پایین | متوسط تا بالا |
| استفاده از منابع | معمولی | ممکن است بیشتر (اتصالات/حافظه) |
نتیجهگیری
تابع reap_async_query() ابزار قدرتمندی در PHP برای مدیریت کوئریهای ناهمزمان در MySQLi است. با درک محدودیتها، مدیریت صحیح اتصالات و استفاده از mysqli_poll، میتوانید زمان پاسخدهی برنامههای خود را کاهش داده و بهرهوری را افزایش دهید. اگر به همزمانی پیچیدهتری نیاز دارید، ترکیب این روش با تکنولوژیهای دیگر (مانند Swoole یا job queues) میتواند راه حلهای مقیاسپذیرتری ارائه دهد.
آیا این مطلب برای شما مفید بود ؟



