بانکهای اطلاعاتی کلید/مقدار

یک مثال عملی با ردیس و پی اچ پی

ساختار داده‌اي و پیاده سازی کد

چندین سال کار با پایگاه‌های داده رابطه‌اي سنتی همه ما را به تفکر جدولی و سطری و ستونی عادت داده است. اما اکنون که با یک پایگاه‌داده NoSQL بدون جدول‌ها و سطر و ستون طرف هستیم، چگونه باید داده‌ها را ساماندهی کنیم؟ در اصل، در این نوع پایگاه‌های‌داده کلید-مقداری، مهم‌ترین قسمت طراحی داده‌اي، تشخیص درست کلیدها برای اشاره به اشیاء و نوع مقادیر ذخیره شونده در آن‌ها است. در این برنامه، به دلیل ماهیت کاربر محور خود، باید کار خود را از کاربران آغاز کنیم. این برنامه باید نام کاربری، شناسه کاربری، کلمه عبور، دنبال‌کنندگان کاربر (followers) و افرادی را که کاربر، آن‌ها را پیگیری مي‌کند، ذخیره کرده و در مواقع مناسب بازیابی‌كند. برای مشخص‌سازی یک کاربر، یک شناسه منحصر به فرد به وی اختصاص خواهیم داد که هنگام ثبت نام، با افزایش خودکار به کاربران جدید تخصیص یابد. کد فهرست ۳، برای ثبت نام کاربران جدید استفاده خواهد شد:

INCR global:nextUserId => 1000
SET uid:1000:username erfan
SET uid:1000:password shabakeh

فهرست ۳- کد لازم برای ثبت‌نام کاربران جدید

دستورات بالا، به صورت فهرست ۴ در کد PHP استفاده‌خواهند شد (موجود در فایل register.php در کد منبع برنامه):


# فرم توسط کاربر پر شده و محتوای نام کاربری و کلمه عبور دریافت مي‌شود

$username = gt(username"); $password = gt("password"); $r = redisLink();

# در صورتی که نام کاربری وجود نداشته باشد، ادامه کار را انجام مي‌دهد

if ($r->get("username:$username:id")) goback("Sorry the selected username is already in use.");

# کاربر وجود ندارد، پس وی را در پایگاه داده ذخیره مي‌كند

$userid = $r->incr("global:nextUserId"); $r->set("username:$username:id",$userid); $r->set("uid:$userid:username",$username); $r->set("uid:$userid:password",$password);

# تولید کلید رمز برای تشخیص هویت کاربران

$authsecret = getrand(); $r->set("uid:$userid:auth",$authsecret); $r->set("auth:$authsecret",$userid); #کاربر ثبت نام شده را به مجموعه کاربران اضافه مي‌كنند $r->sadd("global:users",$userid);

 

فهرست ۴- کد php برای ثبت‌نام کاربران
توجه كنيد، بخش اول کد بالا که برای تخصیص یک شناسه‌کاربری منحصر به فرد به یک کاربر استفاده مي‌شود، یک الگوی طراحی برای پایگاه‌های داده key-value است و بهتر است همیشه آن را به یاد داشته باشید. توجه داشته باشيد که داده‌ها در چنین پایگاه‌های‌داده‌اي، با استفاده از کلیدها بازیابی مي‌شوند و نمی‌توان از آن‌ها خواست تا برای مقادیری‌خاص، کلیدهای مربوط را بیابند. با زبان پایگاه‌های داده رابطه‌ای، در این پایگاه‌های داده جدید، ما همواره با استفاده از کلیدهای اولیه مي‌توانیم به داده‌ها دسترسی داشته باشیم. برای مدل‌سازی دنبال کنندگان یک کاربر در این شبکه اجتماعی کوچک، مي‌توانیم از یک مجموعه حاوی شناسه‌هاي کاربری کاربران استفاده كنيم. به همین منظور کدهای زیر را در نظر بگیرید:

uid:1000:followers => مجموعه‌اي از شناسه کاربری دنبال‌کننده‌گان
uid:1000:following => مجموعه‌اي از شناسه کاربرانی که توسط کاربر دنبال می شوند

کد PHP مورد نیاز برای اجرای این عملیات که در فایل follow.php ذخیره می‌شود در فهرست ۵ آورده شده است.


 

# شناسه کاربری کاربر مورد نظر را بازیابی مي‌كند $f = intval(gt("f")); $uid = intval(gt("uid")); # کنترل این‌که شناسه کاربری فرد مورد نظر با فرد #حاضر متفاوت باشد if ($uid != $User[‘id’]) { if ($f) { # اضافه کردن فرد مورد نظر به‌عنوان دنبال کننده # و اضافه کردن دنبال‌کننده جدید به لیست دنبال‌کننده‌گان قبلی $r->sadd("uid:".$uid.":followers",$User [‘id’]); $r->sadd("uid:".$User[‘id’].":following",$uid); } else { #حذف‌‌کردن فرد مورد نظر به‌عنوان دنبال‌کننده #و حذف ‌‌کردن دنبال کننده جدید از لیست دنبال‌کنندگان قبلی $r->srem("uid:".$uid.":followers",$User[‘id’]); r->srem("uid:".$User[‘id’].":following",$uid); } }

فهرست ۵- کد لازم برای پیاده‌سازی گروه های دنبال‌کننده کاربر وکسانی که کاربر آن‌ها را دنبال می‌کند.

یکی دیگر از مهم‌ترین بخش‌هاي این برنامه، ذخیره و بازیابی پست‌هاي کاربران است. این پست‌ها باید بر‌اساس زمان و از آخرین پست به نخستين پست مرتب شوند، پس بهترین نوع داده‌اي برای ذخیره‌سازی آن‌ها نوع داده‌اي لیست است. برای اضافه‌کردن پست‌هاي جدید باید از دستور LPUSH استفاده کرد و با استفاده از دستور LRANGE، مي‌توان صفحه بندی مناسب را برای پست‌ها نیز انجام داد. کد فهرست ۶ برای ذخیره به روزرسانی‌هاي کاربر در کد PHP موجود در فایل post.php استفاده مي‌شود:

 

# ایجاد ارتباط با پایگاه داده

$r = redisLink();

# تولید شناسه پست با استفاده از عملیات پایگاه داده

$postid = $r->incr("global:nextPostId");

$status = str_replace("\n"," ",gt("status")); # تولید پست مناسب از روی داده‌هاي موجود

$post = $User[‘id’]."|".time()."|".$status;

تخصیص مقدار پست تولید شده در خط بالا به کلید پست با# شناسه منحصر به فرد تولید شده#

$r->set("post:$postid",$post);

# بازیابی پیرو‌هاي کاربر حاضر از مجموعه ذخیره شده برای وی

$followers = $r->smembers("uid:".$User[‹id›].":followers");

if ($followers === false) $followers = Array();

$followers[] = $User[‹id›];

/* اضافه کردن پست حاضر به پست‌هاي خود کاربر */ #پست حاضر را به لیست پست‌هاي همه کاربران پیرو کاربر فعال اضافه مي‌کند#

foreach($followers as $fid) { $r->push("uid:$fid:posts",$postid,false); }

# پست حاضر را به روند زمانی در‌نظر گرفته شده در برنامه اضافه مي‌کند # و آن را به هزار پست آخر محدود مي‌كند

$r->push("global:timeline",$postid,false); $r->ltrim("global:timeline",0,1000);

فهرست ۶- کدهای php مورد نیاز برای کنترل پست‌های کاربر

یکی دیگر از بخش‌هاي مهم این برنامه اعتبار‌سنجی کاربران است. به دلیل پراکنده بودن سرورها و توزیع یافته بودن این برنامه روي ماشین‌هاي مختلف، استفاده از متغیرهای نشست PHP راه‌حل مناسبی نيست. برای این کار، از قدرت پایگاه داده استفاده خواهیم‌كرد. تمام چیزی که برای اعتبار‌سنجی کاربران نیاز است، یک متغیر رشته‌اي random است که باید در کوکی‌هاي کاربرانی که هویت آن‌ها تأیید شده در سمت کلاینت و در یک کلید (شناسه کاربر) در پایگاه‌داده ذخیره‌شود. دستورات مربوط در پایگاه داده Redis به این صورت خواهد بود:

SET uid:1000:auth fea5e81ac8ca77622bed1c2132a021f9
SET auth:fea5e81ac8ca77622bed1c2132a021f9 1000

کد PHP برای انجام امور فوق که در فایل login.php پیاده‌سازی شده در فهرست ۷ آورده شده است.

 

# دریافت مقادیر از فرم وب پیج

if (!gt("username") || !gt("password")) goback("You need to enter both username and password to login.");

# مقادیر درست دریافت شده‌اند

$username = gt("username"); $password = gt("password"); #اتصال به پایگاه‌داده $r = redisLink();

# کنترل وجود کاربر و کلمه عبور وی در پایگاه داده

$userid = $r->get("username:$username:id"); if (!$userid) goback("Wrong username or password"); $realpassword = $r->get("uid:$userid:password"); if ($realpassword != $password) goback("Wrong useranme or password");

نام کاربری و کلمه عبور درست است. ذخیره در کوکی و# فرستادن کاربر به صفحه اصلی # $authsecret = $r->get("uid:$userid:auth"); setcookie("auth",$authsecret,time()+3600*24*365); header("Location: index.php");

 

فهرست ۷- کدهای موردنیاز برای پیاده‌سازی Login

حال نوبت به صفحه اصلی و بازیابی پست‌هاي کاربران و درج آن‌ها در این صفحه به همراه تمهیدات صفحه‌بندی است. کد PHP مورد نیاز که در فایل home.php ذخیره می‌شود، در فهرست ۸ آورده شده است.

 

تابعی برای بازیابی پست‌ها بر اساس شناسه آن‌ها و درج# در وب پیج#

function showPost($id) { $r = redisLink(); $postdata = $r->get("post:$id"); if (!$postdata) return false;

$aux = explode("|",$postdata); $id = $aux[0]; $time = $aux[1]; $username = $r->get("uid:$id:username"); $post = join(array_splice($aux,2,count($aux)-2),"|"); $elapsed = strElapsed($time); $userlink = "".utf8entities($username).""; echo(‘
’.$userlink.’ ‘.utf8entities($post)." "); echo(‘posted ‘.$elapsed.’ ago via web
’); return true; } # تابعی برای بازیابی پست‌هاي یک کاربر و شناسه‌هاي آن‌ها function showUserPosts($userid,$start,$count) { $r = redisLink(); $key = ($userid == -1) ? "global:timeline" : "uid:$userid:posts"; $posts = $r->lrange($key,$start,$start+$count); $c = 0; foreach($posts as $p) { if (showPost($p)) $c++; if ($c == $count) break; } return count($posts) == $count+1; }  

فهرست ۸- کدهای لازم برای ساخت صفحه اصلی یاHome Page

ارزیابی عملکرد و جمع‌بندی

حال که به طور کلی کد منبع این برنامه ساده را بررسی کردیم و به اصول و مفاهیم بنیادی برای کار با یک پایگاه‌ داده NoSQL از نوع Key-Value پی بردیم، نوبت بررسی امکان مقیاس دهی افقی به نرم‌افزار است. قبل از پرداختن به این مبحث، مي‌توان عملکرد این برنامه را روي یک سرور منفرد نیز ارزیابی كرد. برای این بخش، با استفاده از یک سرور‌کند و تحت فشار، وب سرور آپاچی توانست به میزان زمان نمایش صفحات وب در پنج میلی ثانیه برای صد کلاینت همزمان که ده هزار درخواست نوشتن را به وب سرور ارسال مي‌کردند، دست‌یابد. چنین سرعتی به آن معنی است که با استفاده از PHP و Redis ویک سخت‌افزار کند و قدیمی و همین‌طور یک توزیع معمولی لینوکس، مي‌توان به سادگی به هزاران‌کاربر در روز سرویس‌داد. بر‌این اساس، تصور نتایج خیره‌کننده عملکرد این مجموعه در شرایطی که روي سرورهای بسیار قدرتمند امروزی به‌کاربرده شود، چندان دشوار نیست.
حال، تصور كنيد که تعداد کاربران و حجم محتوای این برنامه به حدی بالا رفته که به سادگی و با یک سرور سخت‌افزاری به سرویس‌دهی مطلوب دست‌نخواهیم یافت. در این حالت باید با استفاده از یک تکنیک مناسب برای Hashing، یک کلید Hash تولید‌کرد و بر‌اساس آن، درخواست‌هاي مختلف را با سرورهای مختلف پاسخ داد. نقطه قوت پایگاه داده Redis در مقابل Memcached نیز امکان استفاده از تکنیک‌هاي مختلف برای Hashing است که استفاده از روش‌هاي گوناگون را در کاربردهای گوناگون امكان‌پذير مي‌سازد. با این حال، با اضافه‌کردن سرورهای مختلف به مجموعه ماشین‌هاي در حال کار در این برنامه، مشکلات متعددی بروز مي‌کنند که اتفاقاً، یکی از چالش‌هاي مهم پیش روی کاربران برای استفاده از پایگاه‌های داده NoSQL است. به بیان دقیق‌تر، طراحی داده‌اي بر‌اساس کار با یک سرور، با مدل داده‌اي برای کار با چندین سرور در این پایگاه‌هاي داده مي‌تواند بسیار متفاوت باشد و توسعه دهنده باید به خوبی از این امر آگاه باشد. مثالی برای این مفهوم، ذخیره پست‌هاي کاربران در پایگاه داده با استفاده از شناسه‌هاي منفرد و یکتای آن‌ها است. در صورتی که مدل تک سروری خود را روي مجموعه بزرگی از سرورها به‌کار ببریم، کد global:nextPostId باعث تولید شناسه‌هاي افزایشی زیادی روي یک سرور شده و طریقه اشتراک‌گذاری مقادیر سرورهای مختلف با یکدیگر، دچار آشوب بزرگی خواهد شد. یکی از راه‌هاي غلبه بر این مشکل مشخص، استفاده از یک سرور، تنها برای افزایش مقادیر شناسه پست‌ها‌ است.

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

یک راه دیگر، تغییر در مدل طراحی و استفاده از شناسه‌هاي غیر افزایشی و اتفاقی و بلند (به‌عنوان مثال به بلندای md-5 برای اطمینان از انحصاری بودن آن) است. در این نرم‌افزار ساده، برای توسعه مجموعه ماشین‌هاي اجرایی و افزایش مقیاس افقی با مشکلات بیشتری نیز مواجه خواهیم شد که بیان آن‌ها از حوصله این مطلب‌خارج است. با این حال، برای انجام امور مقیاس دهی افقی نرم‌افزارهای مبتنی بر Redis منابع مختلفی در سطح اینترنت وجود دارد که به سادگی و با یک جست‌وجوی ساده قابل یافتن هستند. با وجود این مشکلات، قابلیت‌هاي توزیع‌پذیری و همچنین مدل ساده داده‌اي و سرعت عملکرد این پایگاه داده، آن را به گزینه مناسب‌تری در مقابل انواع رابطه‌اي رقیب برای توسعه چنین برنامه‌هايي تبدیل کرده است. دنیای پایگاه‌های داده NoSQL، جذابیت‌هاي بیشتری نیز دارد که در آینده با آن‌ها آشنا خواهید شد!

۰

میانگین امتیاز

شما هم امتیاز بدهید‍!

امتیاز کاربران: ۴٫۷۵ ( ۱ رای)
برگهٔ قبلی 1 2

مجتبی بنائی

دانشجوی دکترای نرم‌افزار دانشگاه تهران (yun.ir/smbanaie)، مدرس دانشگاه و فعال در حوزه توسعه نرم‌افزار و مهندسی داده که تمرکز کاری خود را در چند سال اخیر بر روی مطالعه و تحقیق در حوزه کلان‌داده و زیرساخت‌های پردازش داده و تولید محتوای تخصصی و کاربردی به زبان فارسی و انتشار آنها در سایت مهندسی داده گذاشته است. مدیریت پروژه‌های نرم‌افزاری و طراحی سامانه‌های مقیاس‌پذیر اطلاعاتی از دیگر فعالیتهای صورت گرفته ایشان در چند سال گذشته است.

۲ دیدگاه

  1. سلام

    قبل از اینکه بخوایم با ردیس کار کنیم باید یه دیتابیس بسازیم درسته ؟

    الان اون دیتابیس رو چجوری ایجاد کنیم
    ——
    اگه میشه آموزش کار با ردیس در لوا رو هم بزارید

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

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

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

دکمه بازگشت به بالا