<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های امیر صالحی</title>
        <link>https://virgool.io/feed/@iamamirsalehi</link>
        <description>علاقه مند به دنیای استارت آپ و توسعه فردی</description>
        <language>fa</language>
        <pubDate>2026-06-16 19:37:08</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/186060/avatar/aXxV06.jpg?height=120&amp;width=120</url>
            <title>امیر صالحی</title>
            <link>https://virgool.io/@iamamirsalehi</link>
        </image>

                    <item>
                <title>نقش Buffer Pool در بهینه‌سازی عملکرد پایگاه‌های داده</title>
                <link>https://virgool.io/@iamamirsalehi/%D9%86%D9%82%D8%B4-buffer-pool-%D8%AF%D8%B1-%D8%A8%D9%87%DB%8C%D9%86%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%B9%D9%85%D9%84%DA%A9%D8%B1%D8%AF-%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D9%87%D8%A7%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87-ciuydp4wflic</link>
                <description>در دنیای پایگاه‌های داده، عملکرد سریع و کارآمد یکی از مهم‌ترین دغدغه‌های مهندسان نرم‌افزار است. سیستم‌های مدیریت پایگاه داده (DBMS) همواره تلاش می‌کنند تا با استفاده بهینه از حافظه، عملیات خواندن و نوشتن را تسریع کنند. یکی از مهم‌ترین مکانیزم‌هایی که در این مسیر مورد استفاده قرار می‌گیرد، Buffer Pool است. در این مقاله، به بررسی دقیق Buffer Pool، نحوه عملکرد آن، مزایا، چالش‌ها و راهکارهای بهینه‌سازی خواهیم پرداخت.بافر پول (Buffer Pool) چیست؟بافتر پول (Buffer Pool) یک بخش از حافظه است که توسط پایگاه داده برای ذخیره موقت داده‌های خوانده شده از دیسک استفاده می‌شود. این مکانیزم باعث می‌شود که خواندن و نوشتن اطلاعات بر روی دیسک کاهش یابد و به جای آن داده‌ها مستقیماً از RAM پردازش شوند که سرعت اجرای کوئری‌ها را به طور قابل توجهی افزایش می‌دهد.در واقع، زمانی که یک کوئری درخواست داده‌ای را ارسال می‌کند، پایگاه داده ابتدا بررسی می‌کند که آیا این داده قبلاً در Buffer Pool قرار دارد یا خیر. اگر داده در RAM موجود باشد، آن را مستقیماً از آنجا باز می‌گرداند. در غیر این صورت، داده از دیسک خوانده شده و در Buffer Pool ذخیره می‌شود تا در درخواست‌های آینده از آن استفاده گردد.نحوه عملکرد Buffer Poolعملکرد Buffer Pool را می‌توان در چند مرحله کلی خلاصه کرد:۱. خواندن داده از دیسک و ذخیره در Buffer Poolزمانی که یک صفحه داده مورد درخواست قرار می‌گیرد، ابتدا بررسی می‌شود که آیا این صفحه در حافظه (Buffer Pool) موجود است یا خیر. اگر صفحه موجود باشد (Buffer Hit)، مستقیماً از حافظه خوانده می‌شود. در غیر این صورت (Buffer Miss)، داده از دیسک خوانده شده و در Buffer Pool ذخیره می‌شود.۲. مدیریت داده‌های ذخیره‌شدهبافر پول (Buffer Pool) اندازه محدودی دارد و نمی‌تواند تمامی داده‌های پایگاه داده را در خود نگه دارد. بنابراین، زمانی که فضای آن پر شود، باید سیاست‌هایی برای جایگزینی داده‌های قدیمی با داده‌های جدید اعمال شود. یکی از مهم‌ترین الگوریتم‌های مورد استفاده در این زمینه، LRU (Least Recently Used) است که قدیمی‌ترین داده‌هایی را که کمتر مورد استفاده قرار گرفته‌اند حذف می‌کند.۳. نوشتن داده‌ها به دیسکزمانی که تغییراتی در داده‌های ذخیره‌شده در Buffer Pool اعمال می‌شود، این تغییرات بلافاصله روی دیسک ذخیره نمی‌شوند، بلکه در حافظه باقی می‌مانند تا زمانی که نیاز به نوشتن آن‌ها باشد. این فرآیند که Write-Back نامیده می‌شود، باعث کاهش تعداد عملیات ورودی/خروجی (I/O) و افزایش کارایی سیستم می‌شود. البته، برای جلوگیری از از دست رفتن داده‌ها در صورت وقوع خرابی، از مکانیزم‌هایی مانند Checkpointing و WAL (Write-Ahead Logging) استفاده می‌شود.مزایای استفاده از Buffer Poolاستفاده از Buffer Pool در پایگاه‌های داده مزایای متعددی دارد، از جمله:کاهش زمان تأخیر در دسترسی به داده‌ها: به دلیل استفاده از حافظه به جای دیسک، زمان دسترسی به داده‌ها به طور قابل توجهی کاهش می‌یابد.کاهش تعداد عملیات I/O روی دیسک: با ذخیره موقت داده‌های پرکاربرد در حافظه، میزان خواندن و نوشتن بر روی دیسک کاهش می‌یابد که این امر طول عمر دیسک را افزایش می‌دهد.افزایش سرعت اجرای کوئری‌ها: در سیستم‌های پایگاه داده‌ای که از Buffer Pool بهینه استفاده می‌کنند، پردازش کوئری‌ها با سرعت بیشتری انجام می‌شود.مدیریت بهینه حافظه: با استفاده از الگوریتم‌های مدیریت حافظه مانند LRU، داده‌هایی که بیشترین استفاده را دارند در حافظه نگهداری می‌شوند و داده‌های کم‌کاربردتر جایگزین می‌شوند.چالش‌های Buffer Pool و راهکارهای بهینه‌سازی۱. تنظیم اندازه بهینه Buffer Poolیکی از چالش‌های مهم در استفاده از Buffer Pool تعیین اندازه مناسب آن است. اگر Buffer Pool بیش از حد کوچک باشد، میزان Buffer Miss افزایش یافته و عملیات I/O روی دیسک بیشتر خواهد شد. از سوی دیگر، اگر بیش از حد بزرگ باشد، ممکن است منابع حافظه‌ای سیستم به درستی مدیریت نشود. برای حل این مشکل، می‌توان از آنالیز الگوی دسترسی به داده‌ها و مانیتورینگ میزان Hit Ratio استفاده کرد.۲. مدیریت سیاست‌های جایگزینی دادهانتخاب الگوریتم مناسب برای جایگزینی داده‌ها از اهمیت بالایی برخوردار است. هرچند LRU یکی از متداول‌ترین روش‌هاست، اما در برخی شرایط ممکن است کارایی لازم را نداشته باشد. در این موارد، می‌توان از الگوریتم‌هایی مانند Adaptive Replacement Cache (ARC) که بهینه‌تر عمل می‌کند، استفاده کرد.۳. هماهنگی Buffer Pool با سایر بخش‌های پایگاه دادهبرای افزایش بهره‌وری Buffer Pool، باید ارتباط آن با سایر مکانیزم‌های پایگاه داده مانند Query Optimizer، Transaction Manager و Indexing System بهینه‌سازی شود. به عنوان مثال، برخی پایگاه‌های داده از Partitioned Buffer Pools استفاده می‌کنند تا عملکرد پردازش موازی را بهبود بخشند.نتیجه‌گیریبافر پول (Buffer Pool) یکی از مهم‌ترین مکانیزم‌های بهینه‌سازی در سیستم‌های مدیریت پایگاه داده است که به کاهش تأخیر و افزایش سرعت خواندن و نوشتن کمک می‌کند. با تنظیم صحیح اندازه، استفاده از الگوریتم‌های جایگزینی مناسب و مانیتورینگ عملکرد، می‌توان بهره‌وری آن را به حداکثر رساند. در نهایت، درک نحوه کار Buffer Pool و پیاده‌سازی صحیح آن می‌تواند تأثیر قابل توجهی بر عملکرد کلی سیستم پایگاه داده داشته باشد.</description>
                <category>امیر صالحی</category>
                <author>امیر صالحی</author>
                <pubDate>Fri, 14 Mar 2025 17:10:16 +0330</pubDate>
            </item>
                    <item>
                <title>در Business Logic پروژه از کلمات technical در نام گذاری دوری کنید</title>
                <link>https://virgool.io/@iamamirsalehi/%D8%A7%D8%B2-%DA%A9%D9%84%D9%85%D8%A7%D8%AA-technical-%D8%AF%D8%B1-%D9%86%D8%A7%D9%85-%DA%AF%D8%B0%D8%A7%D8%B1%DB%8C-%D8%AF%D9%88%D8%B1%DB%8C-%DA%A9%D9%86%DB%8C%D8%AF-jmfkva4t9gpk</link>
                <description>اصل مطلبدر جاهایی که ما در حال هندل کردن Business logic هستیم بهتر است که از نام های technical یا فنی استفاده نکنیم.حتما توی پروژه هاتون برای نام گذاری از کلمات technical خیلی استفاده میکنید از جمله خودم که قبلا این کارو میکردم، مثلا OrderDTO یا  SusbcriptionDTO یا create (که ربط پیدا میکنه به CRUD) و ...اسم DTO که مخفف Data Transfer Object هست خیلی نام technicalی هستش و اصلا درک نمیکنم چرا باید همچین اسمی رو گذاشت؟ مگه ما یک محصول رو develop نمیکنیم؟ بزارید با مثال بیشتر توضیح بدهم به شما.فرض کنید شما یک نیازمندی رو باید توی پروژه پیاده سازی بکنید و شما باید یک بخشی رو توسعه بدید که ادمین میتونه Subscription جدید در سیستم اضافه بکنه که این Subscription اطلاعاتی مثل عنوان، مدت زمان و قیمت هستش.احتمالا یک سرویسی دارید به اسم SubscriptionService که یک متدی داره به اسم create که به عنوان ورودی SubscriptionDTO رو میگیره و این SubscriptionService رو داخل Controller خودتون صدا میزنید تا بره یک Susbcription جدید برای ما ایجاد بکنهclass SubscriptionController
{
    public function __construct(private SubscriptionService $subscriptionService)
    {
    }
    
    public function create(Request $request)
    {
       $title = $request-&gt;get(&#039;title&#039;);
       $duration = $request-&gt;get(&#039;duration&#039;);
       $price = $request-&gt;get(&#039;price&#039;);
       try{
          $subscriptionDTO = new SubscirptionDTO($title, $duration, $price);
          $this-&gt;subscriptionService-&gt;create($subscriptionDTO);
       }catch(Exception $exception){
          // catch
       }  
       
       // succeed
    }
}یک نفر رو از تیم Product خودتون بیارید و بهش کد خودتون رو نشون بدید و ازش بخواهید که بگه به نظرش SubscriptionDTO چه معنی میده؟ احتمال زیاد میگه نمیدونم با اینکه خود اون فرد Productی از شما همچین نیازمندی رو خواسته. کد شما رو حتی کسی که فنی نیست هم باید متوجه بشه چون ما داریم یک محصول توسعه میدیم.در نیازمندی که بالاتر تعریف کردم، اشاره کردم که ادمین میتونه یک Subscription جدید اضافه بکنه، یک Subscription جدید. پس اینجا میتونیم با همین کلمات توافق کنیم و ازشون در کدهامون هم استفاده بکنیم:$newSubscription = new NewSubscription($title, $duration, $price);خیلی بهتر شد، بعد از شما هم هر کسی بیاد این کلاس رو استفاده بکنه میدونه که برای ساخت یک Subscription جدید هستش. (توی مقاله ی بعدی راجب همین title - duration و price صحبت میکنم که این ها هم جای بهبود دارن) $this-&gt;subscriptionService-&gt;create($subscriptionDTO);فعل create هم بسیار کلمه technical هستش که خیلی مربوط میشه به عملیات CRUD، اینجا هم میتونیم بگردیم به صحبت هایی که با فرد Productی کردیم و این نام رو میتونیم با Add جایگزین کنیم:class SubscriptionController
{
    public function __construct(private SubscriptionService $subscriptionService)
    {

    }

    public function create(Request $request)
    {
       $title = $request-&gt;get(&#039;title&#039;);
       $duration = $request-&gt;get(&#039;duration&#039;);
       $price = $request-&gt;get(&#039;price&#039;);
       try{
          $newSubscription = new NewSubscription($title, $duration, $price);
          $this-&gt;subscriptionService-&gt;add($newSubscription);
       }catch(Exception $exception){
          // catch
       }

       // succeed
    }
}تونستیم به خوانایی کدهامون کمک بکنیم، همین کارهای کوچیک باعث میشه ذهنتون در نام گذاری درگیر بشه و به کلماتی productی بیشتر فکر بکنه.</description>
                <category>امیر صالحی</category>
                <author>امیر صالحی</author>
                <pubDate>Wed, 27 Nov 2024 10:27:50 +0330</pubDate>
            </item>
                    <item>
                <title>چطور تونستیم Failure تسک هایی که سمت GPU ها میره رو به صفر برسونیم</title>
                <link>https://virgool.io/@iamamirsalehi/%DA%86%D8%B7%D9%88%D8%B1-%D8%AA%D9%88%D9%86%D8%B3%D8%AA%DB%8C%D9%85-failure-%D8%AA%D8%B3%DA%A9-%D9%87%D8%A7%DB%8C%DB%8C-%DA%A9%D9%87-%D8%B3%D9%85%D8%AA-gpu-%D9%87%D8%A7-%D9%85%DB%8C%D8%B1%D9%87-%D8%B1%D9%88-%D8%A8%D9%87-%D8%B5%D9%81%D8%B1-%D8%A8%D8%B1%D8%B3%D9%88%D9%86%DB%8C%D9%85-ldbdzxzgk3fn</link>
                <description>مقدمهما سرویس AI در حوضه املاک (real estate) میدیم که کلا با عکس سرو کار داریم، ینی اینکه فرض کن شما از پذیرایی خونتون یه عکسی میگیرید و میخواید پذیرایی رو بدون اثاث ببینی، شما میدی به ما و ما با استفاده از AI کل اثاث های شمارو پاک میکنیم، این یکی از سرویس ها بود و تعداد سرویس ها بیشتر از اینهاست.طبیعتا تعداد زیادی از این عکس ها میاد سمت ما و ما باید بتونیم به درستی این عکس ها رو بدیم به AI تا برای ما Generate بکنه.اصلا داستان چیه؟ (قبل بهبود)ما ۵ تا GPU لوکال داریم، ینی ۵ تا GPUی که دقیقا بغل گوشمونه که بهش به اصطلاح task میدیم تا کاراشو بکنه. ۲ تا GPU cloud داریم و از سرویس FaaS هم استفاده میکنیم که ۳۰ تا GPU دارهنکته ای که باید اضافه کنم اینه که FaaS به تازگی اضافه شده و ما کل کار رو با ۷ تا GPU جمع میکردیم.حالا ما برای توزیع تسک ها قبلا به این شکل بود که یه RabbitMQ بالا بود و برای هر GPU یه worker بالا اومده بود و تسک های مختلف به این شکل توزیع میشد که میرفت میدید ببینه کدوم صف از همه خلوت تره و هر کدوم که خلوت تر بود تسکو مینداخت توی اون صف، حالا مشکل این بود که به هر دلیلی اگه worker به مشکلی میخورد کل تسک های داخل اون صف failed میشد، این مشکلی که worker هم میخورد بیشتر به خاطر اختلالی که توی اینترنت کشور میوفتاد بود. به چارت بالا اگه دقت کنید، عکسی هستش که من قبلا از تغییرات گرفتم و اون رنگ های قرمز تعداد تسک های fail شدست که در ۳ روز گذشته اتفاق افتادهرنگ قرمز: تسک های fail شدهرنگ سبز پررنگ: تسک هایی که زیر ۳۰ ثانیه Generate شدنرنگ سبز کم رنگ: تسک هایی که بین ۳۰ تا ۶۰ ثانیه Generate شدنرنگ آبی: تسک هایی که بین ۶۰ تا ۹۰ ثانیه Generate شدنرنگ زرد: تسک هایی که بیشتر از ۹۰ ثانیه زمان برده تا Generate بشنچطوری بهبودش دادیم؟ما یه سرویس کاملا جدا آوردیم بالا به اسم Dispatcher که این سرویس وظیفش اینکه که بتونه تسک هارو به شکل بهینه توزیع کنه بین GPU ها، این سرویس جزيیات خیلی زیادی داره که بعدا تو یه مقاله ای دیگه توضیح میدم اما الان کلیات رو میگم.این سرویس رو ما با Golang نوشتیم و RabbitMQ رو کامل حذف کردیم و از پترن worker pool استفاده کردیم، ما اومدیم GPUها رو گروه بندی کردیم و بهشون تگ زدیم، برای مثال یه دسته GPU لوکال داریم، یک دسته GPU cloud داریم و یک دسته FaaS داریم.برای دسته هایی که امکان fail شدن تسک براشون خیلی زیاده رو یه backup گذاشتیم، برای مثال GPU های local امکان اینکه تسک fail بشه زیاده چون اختلال اینترنت باعث میشه GPUها از دسترس خارج بشن و تسک رو fail بکنه سر همین اگه fail کرد ما باید برای تسک بتونیم تصمیم بگیریم، اینجا وقتی میبینم یک تسک رفت سمت local و fail شد بر میداریم میزنیم به cloud چون GPU های stableی داره و این retry کردن رو ما حداکثر تا ۳ بار انجام میدیم.شاید از خودتون سوال بپرسید چرا همون اول به cloud نمیزنم؟چون برامون هزینه میتراشه، GPU های لوکال رو باید تو الویت ما باشن و چون خریدیمشون و کنار گوشمونن و اول به اونا میزنیم و بعد به cloud که همین باعث میشه تعداد تسک های fail ما خیلی کم بشه، اما نا گفته نمانند که GPUهای cloud هم تسک میگیرن و انجام میدن و همیشه منتظر نمیمونن که تسکی fail بشه تا بیاد سمتشونحالا کی به FaaS میزنیم؟سرویس FaaS با هر ریکوئستی که سمتش بفرستیم از موجودی ما پول کم میکنه و باید خیلی حواسمون باشه که هر تسکی سمتش نره و تسک هایی با الویت بالا رو سمتش بفرستیم.یکی از مواردی که این سرویس  Dispatcherی که نوشتیم داره انجام میده اینکه میتونه برای ما تسک ها رو الویت بندی بکنه، برای مثال کاربرهای آمریکا که بیشتر مشتریان ما هستند و چند تا از سرویس های ما براشون خیلی جذابه رو Segementهاشو مشخص کردیم و میزنیم به FaaS چون زیر ۳۰ ثانیه به ما جواب میده.چارت بالایی که میبینید آماری هستش که ما بعد از پیاده سازی سرویس جدیدمون به اسم Dispatcher برای ما رقم زده و تعداد تسک های fail ما رو تقریبا به صفر رسونده.توی پست های بعدی بیشتر راجب dispatcher صحبت میکنم که چطور کار میکنه.</description>
                <category>امیر صالحی</category>
                <author>امیر صالحی</author>
                <pubDate>Tue, 12 Nov 2024 17:03:45 +0330</pubDate>
            </item>
                    <item>
                <title>دیزاین safe قسمت اول - هرگز null برنگردون</title>
                <link>https://virgool.io/@iamamirsalehi/%D9%87%D8%B1%DA%AF%D8%B2-null-%D8%A8%D8%B1%D9%86%DA%AF%D8%B1%D8%AF%D9%88%D9%86-kpz2pjemhpgn</link>
                <description>مقدمه نداریم چون وقت نداریمتوی پروژه هاتون اگه یه سری بزنید حتما میبینید که یک سری از متد ها دو نوع return type دارن، مثال زیر رو نگاه کنید.public function get(string $imdbID): ?Movie
{
} اگه به کد بالا دقت کنید میبینید که هم داره Movie میگردونه و هم داره null بر میگردونه (علامت سوال ? نشون دهنده اینکه میتونه null برگردونه) مشکلش چیه؟زمانی که شما در return یک method دارید null برمیگردونید در حقیقت دارید هندل کردن اینکه اصلا resultی بوده یا نه رو میندازید گردن client، شاید از خودتون سوال بپرسید که client کیه؟ کلاینت میشه جایی که این کد داره استفاده میشه، برای مثال:class MovieController
{
    public function __construct(private MovieService $movieService)
    {
    }
    
    public function get(MovieRequest $request)
    {
       $imdbID = $request-&gt;get(&#039;imdb_id&#039;);
       $movie = $this-&gt;movieService-&gt;get($imdbID);
       if (is_null($movie)){
          // Not found
       }  
       
       return $movie
    }
}
همونطور که در کل بالا پیداست جایی که داره از سرویس MovieService استفاده میشه یک Controllerهه که توی متد get این controller داره getش صدا زده میشه، حالا اگه دقت کنید یه شرط گذاشته شده که اگه null بود چی کار کنه و اگه null نبود چی کار بکنه.این باعث میشه که دیزاینی که ما داریم برای کدمون انجام میدیم از safe بودنش بزنه و خطا کردن رو راحت میکنه.راه حل چیه؟چک کردن اینکه آیا Movie هست یا نه رو بر عهده سرویس بزارید و اگه نبود Exception بده، توی یه پست دیگه راجب Command method و Query method ها صحبت میکنم/**
 * @throws MovieApplicationException
 */
public function get(string $imdbID): Movie
{
    $movie = $this-&gt;movieRepository-&gt;findByIMDBID($imdbID);
    if (is_null($movie)) {
        throw MovieApplicationException::couldNotFindMovie();
    }

    return $movie;
}و سمت client هم به شکل زیر میشهpublic function get(MovieRequest $request): Response
{
    $imdbID = $request-&gt;get(&#039;imdb_id&#039;);
    try {
       $movie = $this-&gt;movieService-&gt;get($imdbID);
    } catch (BusinessException $exception) {
       return JsonResponse::unprocessableEntity($exception-&gt;getMessage());
    }

    return JsonResponse::ok(&#039;&#039;, [
       &#039;title&#039; =&gt; $movie-&gt;title,
       &#039;language&#039; =&gt; $movie-&gt;language,
       &#039;country&#039; =&gt; $movie-&gt;country,
       &#039;poster&#039; =&gt; $movie-&gt;poster,
       &#039;url&#039; =&gt; $movie-&gt;url,
       &#039;imdbRating&#039; =&gt; $movie-&gt;imdb_rating,
       &#039;imdbID&#039; =&gt; $movie-&gt;imdb_id,
       &#039;imdbVotes&#039; =&gt; $movie-&gt;imdb_votes,
    ]);
}برای safe کردن دیزاین یک کد کارهای بیشتری میشه انجام داد که به مرور توی نوشته های بعدی دربارش صحبت میکنمخلاصهبه جای null برگردوندن exception پرت کن</description>
                <category>امیر صالحی</category>
                <author>امیر صالحی</author>
                <pubDate>Sat, 09 Nov 2024 10:51:49 +0330</pubDate>
            </item>
            </channel>
</rss>