رویدادها در MySQL


بسم الله الرحمن الرحیم

رویدادهای MySQL‌ در نسخه 5.1.6 به MySQL اضافه شدن و راه‌ دیگری رو برای کارهای زمانبندی شده و یا Cron Job پیشنهاد می دن. از رویداد ها می شه برای پشتیبان گیری، حذف رکوردهای قدیمی و به درد نخور، جمع آوری اطلاعات برای گزارش‌دهی و … استفاده کرد. برخلاف trigger های استاندارد که در شرایط خاص ای که تعیین می شن، اجرا می شن، یک رویداد چیزیه که با‌گذشت زمان اجرا می شن و گاهی اوقات هم بهشون به عنوان trigger های زمانی (Temporal Trigger) اشاره می شه. رویداد ها رو می شه طوری زمان‌بندی کرد که یا یکبار اجرا بشن و یا طی زمان های مشخصی چندین بار در همان مواقع اجرا کنید.

در این مقاله مواردی که برای شروع کار با رویدادها نیاز دارید، بهتون توضیح داده خواهد شد. مثل راه‌اندازی زمانبند رویداد (event scheduler)، افزودن رویداد ها که یکبار و یا چندین بار اجرا بشن، مشاهده رویدادهای موجود و نیز ویرایش رویدادها.
همینطور مثالی رو درباره نحوه استفاده از رویدادهای MySQL برای ایجاد بلاگ که دارای پست های زمانبندی شده هست رو هم به صورت عملی بررسی خواهیم کرد.


راه‌اندازی زمانبند رویداد (Event Scheduler)
زمانبند رویداد MySQL‌ پروسه ایه که پشت صحنه اجرا می شه و مدام دنبال رویدادها برای اجرا هست. قبل از اینکه بتونید رویدادی رو زمانبندی و یا ایجاد کنید، نیاز دارید که زمانبند رویداد رو فعال کنید که برای این کار می تونیم از دستور زیر استفاده کنیم:
mysql> SET GLOBAL event_scheduler = ON;
همینطور، برای غیر‌فعال کردن همه رویدادها می تونیم از دستور زیر استفاده کنیم:
mysql> SET GLOBAL event_scheduler = OFF;

پس از فعال‌سازی زمانبند رویداد MySQL‌، وضعیتشو در لیست پروسه‌های MySQL‌ می تونیم با استفاده از دستور SHOW PROCESSLIST ببینیم:

mysql> SHOW PROCESSLIST\G
...
Id : 37
User : event_scheduler
Host : localhost
db : NULL
Command : Daemon
Time : 5
State : Waiting on empty queue
Info : NULL

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

  • اسامی رویدادها محدود به 64 کارارکتر هستن.
  • از نسخه MySQL 5.1.8 ، اسامی رویدادها حساسیت به حروف بزرگ و کوچیک ندارند. همینطور هر رویدادی باید اسم یکتایی داشته باشه بدون در نظر گرفتن بزرگی یا کوچکی بودن اسم رویداد.
  • رویدادها توسط رویدادهای دیگه نمی تونن ایجاد، ویرایش و یا حذف بشن.
  • هنگام تعیین رویداد زمانی، نمی تونید به تابع های ذخیره شده و توابع تعریف شده توسط کاربر رفرنس ای داشته باشید.


ایجاد رویدادها

دستور زیر رویدادی به است myevent رو برای ما ایجاد می کنه:

DELIMITER |

CREATE EVENT myevent
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
DO
BEGIN
UPDATE mytable SET mycol = mycol + 1;
END |

DELIMITER ;

این رویداد فقط برای یکبار و یک ساعت پس از زمانی که ایجاد شده اجرا خواهد شد. کوئری یا کوئری های مورد نظر بین دستورات BEGIN و END قرار می گیرن و در زمان مشخص شده اجرا خواهند شد.

نکته‌ای که در این تکه کد هست اینه که بدون استفاده از delimiter های ابتدایی و پایانی و همینطور بدون استفاده از کلمات BEGIN و END هم می‌شد رویداد رو ایجاد کرد که در اینصورت کدمون به شکل زیرتغییر می کرد.
اما دلیلی که باعث شده این موارد رو به کدمون اضافه کنیم اینه که در صورتی که بخواهیم چندین کوئری رو اجرا کنیم در اینصورت باید از BEGIN و END استفاده کرده و کوئری هامون رو در بینشون قرار بدیم و در حقیقت در صورتی که بخواهیم فقط یک کوئری اجرا بشه قرار دادن این موارد که در کد فوق هم قرار داده شده بی‌معنی هست.
اما مشکلی ای که وجود داره اینه که ما در کوئری مون نیاز داریم که سمی‌کولن (;) رو در انتها برای نشون دادن اتمام کوئری، نیاز هست که قرار بدیم و در برنامه‌های کلایت که برای کار با mysql هست این موضوع اینگونه برداشت خواهد شد که پایان کدمون هست و دستورات اجرا می شن و در حالی که اینطور نیست و ممکنه چند کوئری داشته باشیم که باعث می شه همون اولی اجرا بشه و برای حل این مشکل از delimiter که در کد اولمون هم ازش استفاده شده، می تونیم استفاده کنیم.

که برای تعیین delimiter می تونیم از همین کلمه در ابتدای کدمون استفاده کنیم و سپس اسمی رو براش در نظر می‌گیریم که در مثال فوق کاراکتر پایپ ( | ) تعیین شده و پس از دستور END اسم مورد نظر رو تکرار کرده و در پایان کدمون رو هم با delimiter و سمی‌‌کولن ( ; )‌ می بندیم.

ولی همونطور که گفته شد، در صورتی که فقط یک کوئری برای اجرا دارید از کد مشابهی مثل زیر استفاده کنید بهتر خواهد بود:

CREATE EVENT myevent
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
DO UPDATE mytable SET mycol = mycol + 1;


برای مشاهده لیستی از رویدادهای موجود می تونیم از دستور SHOW EVENTS استفاده کنیم:

mysql> SHOW EVENTS\G
*************************** 1. row ***************************
Db : mysql
Name : myevent
Definer : root@localhost
Time zone : SYSTEM
Type : ONE TIME
Execute at : 2012-08-09 06:34:22
Interval value : NULL
Interval field : NULL
Starts : NULL
Ends : NULL
Status : ENABLED
Originator : 0
character_set_client : utf8
collation_connection : utf8_general_ci
Database Collation : latin1_swedish_ci
1 row in set (0.01 sec)

بعد از اینکه رویدادی منقضی می‌شه، به طور اتوماتیک رویداد مربوطه حذف می شه، مگر اینکه بطور صریح اعلام کرده باشید که حذف نشه و برای این منظور می شه از دستور ON COMPLITION استفاده کرد. به مثال زیر دقت کنید:

CREATE EVENT myevent
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
ON COMPLETION PRESERVE
DO UPDATE mytable SET mycol = mycol + 1;


در این مثال، حتی با وجودیکه رویداد منقضی می‌شه ولی رویداد حذف نخواهد شد و در دیتابیس نگهداری می شه که این اجازه رو به ما می ده که بعداً بتونیم ویرایشش کنیم که دوباره رویداد رو اجرا کنیم و یا مثلاً دوست داشته باشیم به عنوان مرجع نگهش داریم.

برای حذف دائمی رویداد، بطور دستی می شه از دستور DROP EVENT استفاده کرد:
DROP EVENT myevent;

برای تعیین اینکه رویدادی چندین بار بطور پشت‌سرهم اجرا بشه می تونیم از EVERY استفاده کنیم:

CREATE EVENT myevent
ON SCHEDULE EVERY 1 HOUR
DO UPDATE mytable SET mycol = mycol + 1;

و همینطور بجای اینکه رویدادی داشته باشیم که بجای اینکه یکبار و یا همیشه اجرا بشه، می تونیم رویدادی داشته باشیم که چندین بار ولی در طی یک مدت زمان مشخصی اجرا بشه و برای این کار می تونیم از دستورات START و END استفاده کنیم:

CREATE EVENT myevent
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 1 DAY
ENDS CURRENT_TIMESTAMP + INTERVAL 1 YEAR
DO UPDATE mytable SET mycol = mycol + 1;CREATE EVENT myevent
ON SCHEDULE EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 1 DAY
ENDS CURRENT_TIMESTAMP + INTERVAL 1 YEAR
DO UPDATE mytable SET mycol = mycol + 1;

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

همینطور برای تعیین زمان می شه از دستورات YEAR, MONTH, WEEK, DAY, HOUR, MINUTE , SECOND و … استفاده کرد.


آپدیت رویدادها

در صورتی که تمایل به تغییر رفتار رویدادی دارید، بجای حذف و ایجاد مجدد رویداد، می شه رفتار رویداد رو توسط دستور ALTER EVENT تغییر داد. به عنوان مثال، برای تغییر زمان رویداد قبلی که هر ماه اجرا بشه و همینطور زمانی در آینده در ساعت ۱ صبح اجرا بشه، می تونیم از دستور زیر واسه این کار استفاده کرد:

ALTER EVENT myevent
ON SCHEDULE EVERY 1 MONTH
STARTS '2011-12-01 01:00:00';

برای تغییر رویداد که کوئری های دیگه ای رو اجرا کنه:

delimiter |
ALTER EVENT myevent
DO
BEGIN
INSERT INTO mystats (total)
SELECT COUNT(*) FROM sessions;
TRUNCATE sessions;
END |
delimiter;

و برای تغییر نام رویداد می شه از دستور RENAME برای این کار استفاده کرد:

ALTER EVENT myevent
RENAME TO yourevent;



ارسال مطالب بلاگ بصورت زمانبدی شده

مثال عملی که می شه از رویداد ها درش استفاده کرد، اینه که فرض کنید بلاگی دارید و می‌خواهید که مطالب زمانبندی شده درش ارسال کنید و این مطالب در زمان مشخص شده در آینده تو بلاگ منتشر بشن. یه راهش اینکه که در جدول مربوطه تو دیتابیس دو ستون یکی برای زمان انتشار و یکی هم چک باکسی باشه که نمایش داده شدن یا نشدن مطلب رو تعیین کنه، که از اونور هم اسکریپتی رو هر ۱ دقیقه یک بار توسط Crontab/cronJob اجرا کنیم و در صورتی که زمان حال با زمان ای که تعیین شده برابر یا بزرگتر بود، در اینصورت تیک منتشر کردن مطلب رو در دیتابیس بزنیم، که متأسفانه راه موثری به نظر نمی رسه. راه دیگه که می شه به این هدف رسید، استفاده از رویدادهای MySQL‌ هست که در زمان تعیین شده اجرا و مطلب مربوطه رو منتشر می کنه.

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

<?php
// establish database connection and filter incoming data
// ...

// insert blog post with pending status, get id assigned to post
$query = "INSERT INTO blog_posts (id, title, post_text, status)
VALUES (NULL, :title, :postText, 'pending')";
$stm = $db->prepare($query);
$stm->execute(array(":title" => $title, ":postText" => $text));
$id = $db->lastInsertId();

// is this a future post?
if (isset($_POST["schedule"], $_POST["time"])) {
$scheduleDate = strtotime($_POST["time"]);

$query = "CREATE EVENT publish_:id
ON SCHEDULE AT FROM_UNIXTIME(:scheduleDate)
DO
BEGIN
UPDATE blog_posts SET status = 'published' WHERE id = :id;
END";
$stm = $db->prepare($query);
$stm->execute(array(":id" => $id, ":scheduleDate" => $scheduleDate));
}
// this is not a future post, publish now
else {
$query = "UPDATE blog_posts SET status = 'published' WHERE id = :id";
$stm = $db->prepare($query);
$stm->execute(array(":id" => $id));
}

وقتی مطلب تو دیتابیس ذخیره می شه، مطلب به عنوان وضعیت «در انتظار» خواهد بود. این به ما این امکان رو می ده که اگه پست قراره در آینده منتشر بشه، از طریق رویدادها بشه این کارو انجام داد، در غیراینصورت اگه قراره فوراً مطلب منتشر بشه که همون لحظه وضعیت مطلب رو تغییر می دیم و مطلب هم منتشر می شه.

و اگه قرار بود مطلب رو بعداً ویرایش کنید، می تونید رویداد رو توسط دستور DROP EVENT IF EXISTS حذف و با زمان مناسب دوباره رویدادی رو برای این منظور ایجاد کنید.


کلام آخر :-)
حال بایستی درک بهتری از اینکه رویدادها تو MySQL چیکار می کنن، داشته باشید. و همچنین نحوه ایجاد رویدادها و مدیریتشون رو. از اونجایی که رویدادها جایگزینی برای crontab/cronjob‌ و یا عملیاتی که زمانبندی شده می باشن، نیستن، چرا که رویدادها نمی تونن کدهای خارجی مثل کدهای php‌ رو اجرا کنن، اما جایگزین مناسبی برای عملیاتی که وابسته به زمان هستن که مربوط به دیتابیس MySQL می شن، می تونن باشن.

نکته پایانی هم اینکه تو سایت خود mysql (لینک های منبع) اطلاعات کاملتری راجع به رویداد ها هست که در صورتی که مایل هستید با رویداد ها کار کنید و یا اطلاعات بیشتری ازشون داشته باشید، توصیه می شه حتماً یه نگاهی بهشون بندازین.

:)

منابع: + + + +

پست‌های معروف از این وبلاگ

آپدیت نشدن View و ثابت ماندن مقدار فیلد Input در Angular

لیست شهر و استان‌های ایران