Outgrowing Postgres: رسیدگی به همزمانی کاربر

با افزایش برنامه شما ، با یک چالش جدید روبرو خواهید شد: مدیریت افزایش همزمانی کاربر.
Postgres به دلیل قدرت و قابلیت اطمینان شناخته شده است ، اما برای رسیدگی به هزاران اتصالات همزمان طراحی نشده است. در حالی که مقیاس گذاری عمودی به حجم داده کمک می کند ، مدیریت اتصال به یک رویکرد متفاوت نیاز دارد.
مقیاس گذاری postgres برای تجزیه و تحلیل؟ شما باید Tinybird را امتحان کنید. شما می توانید جداول رویداد Postgres خود را وارد کنید ، مستقیماً با HTTP یا CDC از طریق Kafka به Tinybird بروید و از SQL برای تعریف نقاط پایانی API در زمان واقعی با تأخیر پاسخ میلی ثانیه حتی برای میلیاردها ردیف استفاده کنید. می توانید در اینجا به صورت رایگان ثبت نام کنید.
درک مدل اتصال Postgres و محدودیت های آن
قبل از اینکه به راه حل ها نگاه کنم ، می خواهم بررسی کنم که چگونه Postgres ارتباطات را کنترل می کند.
Postgres از یک مدل مبتنی بر فرآیند استفاده می کند – هر اتصال مشتری یک فرآیند جدید سرور را تخم ریزی می کند. این ارتباطات را به خوبی جدا می کند اما محدودیت هایی دارد:
- استفاده از منابع: هر اتصال جدید به حافظه و پردازنده نیاز دارد. اتصالات بیشتر به معنای استفاده بیشتر از منابع است.
- حد اتصال: حداکثر پیش فرض به طور معمول 100 اتصال است. شما می توانید این مسئله را افزایش دهید ، اما گذشته از یک نقطه خاص ، اتصالات بیشتر به عملکرد آسیب می رساند.
- سوئیچینگ متن: بسیاری از اتصالات فعال ، سیستم عامل را وادار می کند تا زمان بیشتری را برای جابجایی بین فرآیندها صرف کند و همه چیز را کاهش دهد.
می توانید تنظیمات فعلی خود را با:
SHOW max_connections;
SHOW superuser_reserved_connections;
قبل از افزایش max_connections
، استفاده از حافظه واقعی هر اتصال را با دقت ارزیابی کنید. هر اتصال Postgres دارای مقداری حافظه از سربار است ، اما بسته به پیکربندی و حجم کار شما می تواند به میزان قابل توجهی متفاوت باشد.
با تنظیمات پیش فرض ، یک اتصال جدید به طور معمول از حدود 1-2 مگابایت حافظه برای فرآیند باطن استفاده می کند. از ابزارهایی مانند استفاده کنید top
برای دیدن حافظه واقعی مورد استفاده در هر PID با پس زمینه. مهم است که به طور منظم این تعداد را کنترل کنید زیرا می تواند به سرعت افزایش یابد زیرا هر اتصال کار می کند.
3 راه برای رسیدگی به کاربران بیشتر در Postgres
3 روش اساسی وجود دارد که من می دانم برای افزایش همزمانی کاربر با Postgres:
- کاهش مدت زمان اتصال
- افزایش ظرفیت اتصال
- کاهش تقاضای ارتباط
این اهرم های شما هستند ، اما روش های زیادی برای کشیدن آنها وجود دارد. من قصد دارم به هر یک عمیق بروم.
مدت زمان اتصال را کاهش دهید
این اولین مجموعه از رویکردها با هدف سریع تر کردن نمایش داده ها ، بنابراین آنها برای زمان کمتری اتصالات را حفظ می کنند.
بهینه سازی پرس و جو ، پارتیشن بندی و نمایه سازی مناسب
بهینه سازی نمایش داده شد ، اجرای تقسیم بندی جدول و اطمینان از نمایه سازی مناسب استراتژی های حیاتی برای کاهش زمان اجرای پرس و جو است ، در نتیجه مدت زمان هر اتصال را به حداقل می رساند. من این تکنیک ها را در مقاله قبلی خود با عنوان “رسیدگی به حجم داده های در حال رشد در Postgres” در عمق بیشتری پوشش دادم.
ذخیره در پایگاه داده
قبل از مراجعه به ذخیره سازی در سطح برنامه ، می توانید برخی از گزینه های ذخیره سازی را در خود Postgres در نظر بگیرید.
نماهای مادی شدهنماهای مادی شده راهی برای نتایج پرس و جو حافظه پنهان در Postgres فراهم می کند. آنها با اجرای پرس و جو و ذخیره نتیجه تنظیم شده روی دیسک کار می کنند. این قبل از محاسبات اجازه می دهد تا نمایش داده شدگان بعدی که به دیدگاه مادی شده مراجعه می کنند ، داده های ذخیره شده را مستقیماً بازیابی کنند ، بدون اینکه دوباره پرس و جو زیر را اجرا کنند. با ذخیره نتایج ، دیدگاههای مادی اغلب زمان پرس و جو را برای بارهای کاری شدید خوانده شده و پرس و جوهای پیچیده مربوط به پیوندها یا تجمع کاهش می دهد. Postgres کار گران قیمت را به صورت مقدماتی انجام می دهد و نتایج را ذخیره می کند ، و بعداً به دنبال جستجوی سریع تر می شود. من نحوه ایجاد آنها را در مقاله “رسیدگی به حجم داده های در حال رشد در پست های” من پوشش می دهم.
تنظیم تنظیماتدر shared_buffers
پارامتر تعیین می کند که چه مقدار حافظه Postgres به ذخیره بلوک های دیسک اختصاص می یابد. درک این موضوع مهم است shared_buffers
بلوک های دیسک خام را ذخیره کنید ، نتایج پرس و جو نیست. داده های ذخیره شده هنوز باید توسط مجری پرس و جو پردازش شوند. تنظیم مناسب این پارامتر با کاهش خواندن دیسک فیزیکی ، سرعت پرس و جو را بهبود می بخشد.
برخلاف توصیه های مردمی ، تنظیم shared_buffers
تا 25 ٪ از کل رم اغلب بیش از حد است ، به خصوص در سیستم هایی که حافظه زیادی دارند. در بیشتر موارد ، تنظیمات بین 128 مگابایت و 4 گیگابایت کافی است. مستندات Postgres 128MB را برای سیستم هایی با 1 گیگابایت یا رم کمتر و حداکثر 4 گیگابایت برای سیستم هایی با 64 گیگابایت یا رم بیشتر توصیه می کند.
تنظیم shared_buffers
، می توانید خود را تغییر دهید postgresql.conf
پرونده:
shared_buffers = 2GB # Adjust based on your total RAM
به یاد داشته باشید پس از تغییر این تنظیم ، سرور Postgres خود را مجدداً راه اندازی کنید.
نظارت بر نسبت های HIT CACHE می تواند به شما در تعیین اینکه آیا خود را تعیین کنید shared_buffers
تنظیم مؤثر است:
SELECT
sum(heap_blks_read) as heap_read,
sum(heap_blks_hit) as heap_hit,
sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio
FROM
pg_statio_user_tables;
نسبت بالاتر از 0.99 (99 ٪) نشان می دهد که حافظه پنهان شما به خوبی کار می کند. با این حال ، حتی اگر نسبت پایین تر باشد ، در حال افزایش است shared_buffers
ممکن است بهترین یا تنها گزینه پیکربندی نباشد. قبل از افزایش shared_buffers
، در نظر بگیرید:
-
effective_cache_size
: این پارامتر به Postgres می گوید که چه مقدار حافظه برای ذخیره دیسک ، از جمله حافظه پنهان سیستم عامل در دسترس است. این را برآوردی از میزان حافظه در حافظه پنهان بافر سیستم عامل و Postgres ، به طور معمول 50-75 ٪ از کل RAM برای سرورهای DB اختصاصی تنظیم کنید. -
work_mem
: این تعیین می کند که قبل از ریختن روی دیسک ، چه مقدار حافظه برای عملیات مرتب سازی و هش اختصاص می یابد. در حال افزایشwork_mem
می تواند به طور قابل توجهی نمایش داده شدگان با انواع بزرگ حافظه یا هش را سرعت بخشد. هنگام تنظیم این مورد ، مراقب باشید زیرا این تنظیم در هر پرس و جو است. - OS CACHES: اجازه دادن به پرونده های داده Cache Postgres OS اغلب می تواند مؤثرتر از افزایش باشد
shared_buffers
بشر حافظه پنهان سیستم عامل پویا است و با حجم کار سازگار است و به داده های مکرر دسترسی پیدا می کند. با نگه داشتن حافظه بیشتر برای حافظه پنهان سیستم عامل به جای تخصیص آن بهshared_buffers
، شما اغلب می توانید به عملکرد بهتری دست یابید. به عبارت دیگر ، ترک RAM کافی برای سیستم عامل برای مدیریت حافظه نهان پرونده خود به طور مؤثر بسیار توصیه می شود.
به یاد داشته باشید ، هر حجم کار بی نظیر است. همیشه تغییرات پیکربندی را تست کنید و عملکرد سیستم را کنترل کنید تا بهترین تنظیمات را برای مورد استفاده خاص خود پیدا کنید. فقط کورکورانه از قوانین انگشت شست پیروی نکنید!
در حالی که این رویکرد مانند یک گلوله نقره ای به نظر می رسد ، می توانید خیلی راحت پیکربندی را اشتباه بگیرید. تنظیم مناسب نیاز به درک داده ها ، نمایش داده ها و الگوهای دسترسی شما دارد. با تنظیمات مختلف آزمایش کنید ، معیارهای کلیدی مانند نسبت هیت حافظه پنهان و عملکرد پرس و جو را کنترل کنید و تا زمانی که پیکربندی بهینه را برای پایگاه داده Postgres خود پیدا نکنید ، تکرار کنید.
ظرفیت اتصال را افزایش دهید
مجموعه دوم رویکردها بر افزایش میزان هرگونه اتصال می تواند داشته باشد.
استخر اتصال با pgbouncer
جمع آوری اتصال معمولاً اولین و مؤثرترین رفع ظرفیت اتصال است. PGBouncer به دو محدودیت مقیاس پذیری کلیدی در Postgres می پردازد: حافظه هر اتصال و هزینه های تعویض زمینه. PGBouncer به جای حفظ فرآیندهای جداگانه و تخصیص حافظه ، استخر کوچکی از اتصالات واقعی Postgres را مدیریت می کند که با اختصاص درخواست به اتصالات موجود از استخر ، تعداد بیشتری از اتصالات مشتری را ارائه می دهد. این امر باعث از بین رفتن اتصالات مکرر تنظیم و بسته شدن می شود.
2،000 اتصال مستقیم Postgres بسته به زمان کار و تنظیمات پیکربندی ، معمولاً به حدود 4-8 گیگابایت حافظه احتیاج دارند. PGBouncer می تواند با تعداد بسیار کمتری از اتصالات مخلوط شده ، همان بار را تحمل کند. به عنوان مثال ، 100 اتصالات مخلوط فقط ممکن است در حدود 200-400 مگابایت حافظه در خود PGBouncer استفاده کنند. باکترهای واقعی Postgres هنوز هم برای هر یک از این 100 اتصالات از حافظه استفاده می کنند ، اما ردپای حافظه کل حداقل در مقایسه با 2000 اتصالات مستقیم یک ترتیب از بزرگی است.
مثل همیشه ، ناهار رایگان وجود ندارد. PGBouncer محدودیت های مهمی برای در نظر گرفتن دارد:
- معماری تک رشته: کلیه فرآیندهای ترافیک از طریق یک موضوع واحد ، که به طور معمول منجر به افزایش تأخیر بین 2،000-5000 اتصال مشتری در سخت افزار مدرن می شود. چندین نمونه pgbouncer می تواند به شما کمک کند اما پیچیدگی عملیاتی را اضافه می کند.
- مدیریت صف: هنگامی که تمام اتصالات تلفیقی اشغال شد ، درخواست های جدید یک صف انتظار را وارد می کنند. معاملات طولانی مدت می تواند باعث رشد این صف شود و به طور بالقوه باعث ایجاد زمان مشتری می شود.
-
محدودیت های ویژگی: حالت جمع آوری معامله ، در حالی که کارآمد است ، اما از ویژگی های خاص Postgres مانند بیانیه های آماده و پشتیبانی نمی کند
LISTEN
/NOTIFY
بشر جمع بندی جلسه سازگاری گسترده تری اما کاهش کارایی ارائه می دهد.
در اینجا یک تنظیم اساسی PGBouncer آورده شده است:
- نصب PGBouncer:
sudo apt-get install pgbouncer
- پیکربندی pgbouncer
;; /etc/pgbouncer/pgbouncer.ini
[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb
[pgbouncer]
listen_port = 6432
listen_addr = 127.0.0.1
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
- شروع pgbouncer:
sudo service pgbouncer start
اکنون ، به جای اتصال مستقیم به Postgres ، برنامه شما به PGBouncer متصل می شود (در این مثال ، در پورت 6432).
PGBouncer می تواند به طور موثری نیازهای مقیاس گذاری اتصال را تا 10،000+ مشتری انجام دهد. شما باید فراتر از چندین هزار اتصال ، عمق و تأخیر را از نزدیک کنترل کنید. هنگامی که این معیارها نشانگر اشباع است ، می خواهید برخی از پیشنهادات بیشتر در این مقاله را در نظر بگیرید.
ماکت ها را بخوانید
با ارائه ظرفیت اضافی برای عملیات خوانده شده از طریق مقیاس افقی ، نمایش داده های Ofpload Ofpload را از سرور اصلی خود بخوانید. هر ماکت یک نسخه به روز از داده های شما را از طریق تکثیر جریان حفظ می کند.
اینها برخی از مواردی است که باید در نظر بگیرید:
افزایش ظرفیت
عملکرد ماکت بر اساس پیچیدگی بار کار ، پیکربندی سخت افزار ، الگوهای پرس و جو و زیرساخت های شبکه و ذخیره سازی متفاوت است.
- یک قاعده خوب این است که ظرفیت خواندن معمولی از 50-75 ٪ از عملکرد سرور اولیه متغیر است.
- توصیه می شود قبل از نیاز به طراحی مجدد معماری ، حداکثر 2-3 ماکت داشته باشید.
- مقیاس بندی مؤثر بیشتر به بهینه سازی پرس و جو و طراحی زیرساخت ها نسبت به تعداد ماکت خام بستگی دارد.
تنگناهای مشترک
- تأخیر در تکرار: در مورد اولیه می نویسد که باید در هر ماکت دوباره پخش شود. در زیر بارهای سنگین ، ماکت ها می توانند عقب بیفتند. تاخیر ممکن است بسیار متغیر باشد و بستگی به حجم معامله ، پیچیدگی معامله ، تأخیر شبکه و عملکرد I/O ذخیره سازی دارد ، اما موارد زیر رایج است:- 5-10 ثانیه تاخیر زیر بارهای عادی .- LAG می تواند به دقیقه یا ساعت برسد در حین نوشتن اوج یا عملیات دسته ای بزرگ.
- تقویت نوشتن: هر ماکت عملیات نوشتن اولیه را تکرار می کند. تقویت نوشتن به اندازه معاملات نوشتن ، روش تکثیر (جریان در مقابل منطقی) ، پهنای باند شبکه و قابلیت های ذخیره بستگی دارد. اینها برخی از موضوعاتی است که ممکن است در آن انجام دهید:- 3 ماکت = 4x بار نوشتن در سیستم شما .- می تواند I/O را تحت تأثیر قرار دهد ، به خصوص در قله ها .- اغلب به ذخیره سریعتر (SSD) در ماکت ها نیاز دارد.
- مدیریت اتصال: هر ماکت به استخر اتصال خود نیاز دارد .- این پیچیدگی مدیریت اتصال را ضرب می کند .- برای رسیدگی به عدم موفقیت ماکت به منطق برنامه نیاز دارد.
در زیر یک پیکربندی نمونه برای تنظیم ماکت های شما آورده شده است:
- تنظیم تکثیر جریان در
postgresql.conf
در ابتدای کار:
wal_level = replica
max_wal_senders = 10
wal_keep_segments = 64
- ماکت را برای اتصال به قسمت اولیه تنظیم کنید
postgresql.conf
:
# Required settings
standby_mode="on"
primary_conninfo = 'host=primary_hostname port=5432 user=replication_user password=your_password'
restore_command = 'cp /path/to/archive/%f %p'
# Optional but recommended settings
recovery_min_apply_delay = 0
recovery_target_timeline="latest"
hot_standby = on
hot_standby_feedback = on
- در برنامه خود ، منطق را برای توزیع نمایش داده های خوانده شده در ماکت های خود پیاده سازی کنید.
نظارت و مدیریت
همانطور که در تمام رویکردهای پیشنهادی ، نظارت برای درک تأثیر بهینه سازی شما ضروری است. موارد زیر را بسیار دقیق کنترل کنید تا درک کنید که آیا تکثیر مطابق آنچه در نظر گرفته شده است کار می کند:
- استفاده کردن
pg_stat_replication
برای ردیابی:* تأخیر تکرار - ارسال/دریافت وال
-
تأخیر مجدد
-
هشدار را برای:* تاخیر تکرار بیش از آستانه ها اجرا کنید
-
مسائل مربوط به اتصال ماکت
-
نرخ تولید وال
Read Replicas یک کار عالی برای توزیع بار در چندین سرور انجام می دهد ، و حداکثر 80-90 ٪ از ترافیک خوانده شده در هر ماکت را در استقرارهای معمولی انجام می دهد ، اما ، آنها یک راه حل ساده “اضافه کردن سرورهای بیشتر” نیستند.
با افزایش حجم نوشتن ، باید تاخیر تکثیر و ظرفیت I/O را از نزدیک نظارت کنید. هنگامی که این معیارها تخریب مداوم را نشان می دهند-تاخیر بیش از ثانیه یا I/O به طور مرتب در ظرفیت-ممکن است زمان آن رسیده باشد که برخی از رویکردهای سطح برنامه را در این مقاله در نظر بگیرید.
تقاضای اتصال را کاهش دهید
مجموعه سوم رویکردها بر نیاز به اتصالات کمتر به طور کلی متمرکز است. اینها بسیار تهاجمی تر هستند زیرا نیاز به تغییراتی در منطق کاربرد شما و/یا معماری سیستم شما و در نتیجه یک تلاش مهندسی بزرگتر دارند.
پردازش ناهمزمان
برای عملیاتی که به نتایج فوری احتیاج ندارند ، استفاده از پردازش ناهمزمان را در نظر بگیرید. با بارگذاری کارهای وقت گیر به کارگران پس زمینه ، می توانید سریعتر اتصالات پایگاه داده را آزاد کنید.
این رویکرد به ویژه در هنگام برخورد با کارهایی مانند پردازش مجموعه داده های بزرگ ، مدیریت تعامل API خارجی یا انجام تجمع پیچیده پایگاه داده بسیار ارزشمند است. با بارگذاری این عملیات به کارگران پس زمینه ، برنامه ها می توانند عملکرد را حفظ کنند ، تنگناهای سیستم را کاهش دهند و حتی با نیازهای محاسباتی قابل توجهی ، تجربه کاربری نرم و صاف تری را نیز فراهم کنند.
علاوه بر این ، این رویکرد مزایای دیگری را نیز فراهم می کند: کارگران پس زمینه می توانند به صورت افقی ، مستقل از کاربرد اصلی شما ، مقیاس بندی شوند و در عین حال باعث افزایش مقاومت در برابر خرابی های موقت سیستم نیز می شوند.
پردازش ناهمزمان را می توان با استفاده از فن آوری های صف مانند RabbitMQ یا Apache Kafka اجرا کرد. برای برنامه های پایتون ، یک کرفس محبوب است ، یک صف کار توزیع شده است که به خوبی با چارچوب های مختلف وب ادغام می شود.
در اینجا یک مثال ساده وجود دارد:
from celery import Celery
app = Celery('tasks', broker="redis://localhost:6379")
@app.task
def process_data(data):
# Time-consuming data processing task
result = perform_complex_calculation(data)
store_result_in_database(result)
# In your main application
def handle_request(data):
process_data.delay(data) # This will be processed asynchronously
return {"status": "processing"}
اگر در حال توسعه گره هستید ، Bullmq به عنوان یک راه حل به دنبال محبوبیت در حال افزایش است. برای برنامه های ریل ، می توانید از ActiveJob با پشتیبان مانند Sidekiq برای پردازش کار پس زمینه کارآمد استفاده کنید.
اشتراک اتصال
در برنامه های چند رشته ای ، به جای اینکه هر موضوع اتصال خود را باز کند ، اشتراک اتصال را در میان موضوعات اجرا کنید. در حالی که هر دو مدیریت جلسه محلی و محلی و PGBouncer با هدف بهینه سازی اتصال به پایگاه داده ، آنها به چالش های مختلف در مدیریت اتصال می پردازند.
جلسات محلی محلی یک رویکرد سبک و کاربردی برای کاهش اتصال سربار در یک نمونه کاربردی واحد ارائه می دهد. این روش با استفاده مجدد از اتصالات در میان موضوعات ، تعداد اتصالات همزمان پایگاه داده را به حداقل می رساند و ایجاد اتصال به سربار را کاهش می دهد.
در بیشتر محیط ها ، این رویکردها مکمل هستند. مدیریت اتصال به محلی و محلی می تواند در کنار PGBouncer برای بهینه سازی بیشتر راندمان اتصال با اتصال اتصال PGBouncer بین برنامه و بانک اطلاعاتی استفاده شود و مدیریت نخ محلی که باعث کاهش اتصال داخلی می شود.
در اینجا یک نمونه اجرا با استفاده از نسخه های نمونه وجود دارد node-postgres
، مجموعه ای محبوب از ابزارهای کار با Postgres در گره:
import { Pool, QueryResult, PoolClient } from 'pg';
import { type User } from '@/models/user';
const TIMEOUT_IDLE = 30000;
const TIMEOUT_CONNECTION = 2000;
class DatabaseManager {
private pool: Pool;
constructor() {
this.pool = new Pool({
user: "your_username",
host: "localhost",
database: "your_database",
password: "your_password",
port: 5432,
max: 20, // Maximum number of connections in the pool
idleTimeoutMillis: TIMEOUT_IDLE, // Connection timeout
connectionTimeoutMillis: TIMEOUT_CONNECTION, // How long a client is allowed to remain idle
});
}
async sharedConnectionQuery(
query: string,
params: any[] = []
): Promise {
const client: PoolClient = await this.pool.connect();
try {
const { rows }: QueryResult = await client.query(query, params);
return rows;
} catch (err) {
console.error("Database query error", err);
throw err;
} finally {
client.release(); // Always release the client back to the pool
}
}
async processData(userId: number): Promise {
const query = "SELECT * FROM users WHERE id = $1";
const results = await this.sharedConnectionQuery(query, [userId]);
return results?.[0] ?? null;
}
async processBatchUsers(): Promise<(User | null)[]> {
const userIds = Array.from({ length: 1000 }, (_, i) => i + 1);
try {
const results = await Promise.all(
userIds.map((userId) => this.processData(userId))
);
console.log("Batch processing complete");
return results;
} catch (error) {
console.error("Batch processing failed", error);
throw error;
}
}
// Cleanup method to shut down the connection pool
async shutdown(): Promise {
await this.pool.end();
}
}
// Usage example
async function main() {
const dbManager = new DatabaseManager();
try {
const processedUsers = await dbManager.processBatchUsers();
// Further processing of results
} catch (error) {
console.error("Processing failed", error);
} finally {
await dbManager.shutdown();
}
}
main();
این رویکرد تضمین می کند که هر موضوع از همان اتصال پایگاه داده استفاده می کند و تعداد کلی اتصالات مورد نیاز را کاهش می دهد.
ذخیره سازی در سطح برنامه
گزینه دیگری که باید در نظر بگیرید ، ذخیره سازی سطح برنامه ، استفاده از منطق در برنامه شما برای ارائه درخواست های تکراری در یک بازه زمانی کوچک از حافظه است.
حافظه پنهان سمت سرور
یک لایه ذخیره سازی با استفاده از بانکهای اطلاعاتی در حافظه محبوب مانند Redis یا Memcached می تواند با داشتن امکان رسیدگی به بار درخواست همزمان بسیار بزرگتر ، در پرداختن به مشکلات اضافه بار اتصال Postgres حرکت کند. اضافه کردن حافظه نهان به شما امکان می دهد به جای آن ، خواندن های مکرر از حافظه را انجام دهید ، و فشار خود را از بین ببرید.
- انتقال عملیات سنگین به حافظه نهان ، میزان استفاده از استخر اتصال از هزاران تماس پایگاه داده همزمان را به یک سطح قابل کنترل کاهش می دهد و زمان پاسخ از میلی ثانیه به میکرو ثانیه کاهش می یابد.
- برای وب سایت ها و برنامه هایی با بسیاری از کاربران که از همان داده ها درخواست می کنند ، یک حافظه نهان به درستی تنظیم شده می تواند بخش عمده ای از ترافیک خوانده شده را در حالی که به عنوان مثال Postgres شما اجازه می دهد تا روی نوشتن ها و پرس و جوهای غیرمجاز متمرکز شوند.
- ابتدا می توانید با ذخیره کردن پرس و جوهای بالاترین حجم خود ، کوچک را شروع کنید. این امر باعث می شود اعتبار سنجی رویکرد قبل از گسترش بیشتر ، ساده تر شود.
- علاوه بر این ، شما می توانید با استفاده از رویکردهای حافظه پنهان در حافظه ، قبل از تعویض آنها برای یک راه حل ذخیره از راه دور ، حتی ساده تر را شروع کنید.
در زیر یک نمونه در GO با استفاده از Redis وجود دارد:
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/go-redis/redis/v8"
"gorm.io/gorm")
type UserService struct {
db * gorm.DB cache * redis.Client
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func(s * UserService) GetUserWithCaching(ctx context.Context, userID int)( * User, error) {
// 1. Check Redis cache first
cacheKey: = fmt.Sprintf("user:%d", userID)
cachedUser,
err: = s.cache.Get(ctx, cacheKey).Result() if err == nil {
var user User
if parseErr: = json.Unmarshal([] byte(cachedUser), & user);
parseErr == nil {
return &user, nil
}
}
// 2. If not in cache, query database
var user User result: = s.db.First( & user, userID) if result.Error != nil {
return nil, result.Error
}
// 3. Store in cache for future requests
userJSON,
_: = json.Marshal(user) s.cache.Set(ctx, cacheKey, userJSON, 1 * time.Hour) return &user, nil
}
func NewUserService(db * gorm.DB, cache * redis.Client) * UserService {
return &UserService {
db: db,
cache: cache,
}
}
func main() {
// Redis and Database configuration would be here
// Actual connection setup omitted for brevity
}
ذخیره در برنامه های وب
اگر در حال تهیه یک برنامه وب هستید ، بسیاری از چارچوب های محبوب اجرای درخواست و ذخیره روتر را آسان می کند و تعداد درخواستهایی را که به پس زمینه شما ضربه می زند و متعاقباً ، نمونه (های) Postgres شما را کاهش می دهد. برای توسعه دهندگان NextJS ، حافظه نهان داده ، حافظه نهان مسیر کامل و حافظه نهان روتر گزینه های خوبی برای ذخیره در چندین لایه از برنامه شما هستند و بسیاری از جزئیات اجرای اصلی را از بین می برند و به شما امکان می دهند تا روی منطق کاربرد خود تمرکز کنید.
در حالی که ذخیره سازی می تواند بار پایگاه داده را به میزان قابل توجهی کاهش دهد ، شما باید از اشکالات احتمالی آگاه باشید:
- استحکام داده ها: داده های ذخیره شده اگر به درستی بی اعتبار یا تازه نشوند ممکن است منسوخ شوند.
- قوام حافظه نهان: حفظ قوام بین حافظه نهان و بانک اطلاعاتی می تواند به ویژه در سیستم های توزیع شده چالش برانگیز باشد.
- پیچیدگی اضافه شده: اجرای و مدیریت یک لایه ذخیره ، پیچیدگی را به معماری برنامه شما اضافه می کند.
نمایش داده شد
نمایش داده های دسته ای می تواند تعداد اتصالات پایگاه داده را که از برنامه شما استفاده می کند ، کاهش دهد. این رویکرد خواستار طراحی مجدد الگوی پرس و جو برنامه شما برای انجام عملیات بیشتر در یک تماس پایگاه داده واحد است. این ترکیب ملاحظات طراحی API را با تکنیک های پردازش دسته ای ترکیب می کند تا تعداد کلی اتصالات مورد نیاز را کاهش دهد.
در اینجا یک مثال با استفاده از asyncio
وت aiopg
برای عملیات پایگاه داده ناهمزمان در یک اسکریپت تجزیه و تحلیل داده در پایتون:
import asyncio
import aiopg
async def batch_select_operations():
dsn = 'dbname=test user=postgres password=secret host=localhost'
pool = await aiopg.create_pool(dsn)
async with pool.acquire() as conn:
async with conn.cursor() as cur:
queries = [
"SELECT AVG(temperature) FROM weather_data WHERE city = 'New York'",
"SELECT COUNT(*) FROM transactions WHERE amount > 1000",
"SELECT SUM(population) FROM cities WHERE country = 'France'"
]
results = []
for query in queries:
await cur.execute(query)
results.append(await cur.fetchone())
return results
# Usage
loop = asyncio.get_event_loop()
results = loop.run_until_complete(batch_select_operations())
print(f"Average temperature in New York: {results[0][0]}")
print(f"Number of large transactions: {results[1][0]}")
print(f"Total population of French cities: {results[2][0]}")
با جمع کردن پرس و جو ، باید مراقب باشید که بیش از حد از آن استفاده نکنید. اگر دسته های خود را خیلی بزرگ کنید ، هر اتصال برای پردازش تمام نمایش داده های صف بالا باز خواهد ماند. هدف این است که یک تعادل پیدا کنید – دسته ای به اندازه کافی برای حفظ تعداد اتصالات قابل کنترل ، اما نه آنقدر که اتصالات فردی خیلی طولانی شروع به کار می کنند. برای یافتن نقطه شیرین برای حجم کار خاص شما ممکن است برخی از آزمایشات و نظارت ها انجام شود. موارد زیر را در هنگام اجرای نمایش داده های دسته ای در نظر بگیرید:
- اندازه دسته: اندازه دسته ای را انتخاب کنید که اتصالات را کاهش می دهد بدون اینکه عملیات فردی را خیلی طولانی کند.
- پیچیدگی: در هنگام دسته بندی پرس و جوهای پیچیده ، محتاط باشید ، زیرا ممکن است منجر به معاملات طولانی تر شود.
- الزامات سازگاری: اطمینان حاصل کنید که دسته بندی در جایی که به روزرسانی های فوری ، به روزرسانی های فوری ضروری باشد ، سازش نمی کند.
وقتی مقیاس اتصال کافی نیست
بنابراین این 2،614 کلمه (شامل کد) در مقیاس اتصال است. 😅
با وجود همه این بهینه سازی ها ، هنوز هم نکته ای به وجود خواهد آمد که Postgres تلاش می کند تا نیازهای همزمانی شما را حفظ کند.
اینها برخی از نشانه هایی است که شما در حال رسیدن به محدودیت های آنچه Postgres می تواند داشته باشد:
- زمان اتصال: اگر شاهد زمان های مکرر اتصال هستید ، ممکن است نشان دهد که پایگاه داده شما نمی تواند بار اتصال را کنترل کند.
- استفاده از CPU بالا: اگر پردازنده سرور پایگاه داده شما به طور مداوم در استفاده یا نزدیک به 100 ٪ استفاده است ، این نشانه این است که تلاش می کند تا از حجم کار خودداری کند.
- افزایش تأخیر پرس و جو: اگر زمان پاسخ به پرس و جو به طور مداوم در حال افزایش است ، حتی برای نمایش داده های ساده ، ممکن است نشان دهد که پایگاه داده شما بیش از حد بارگیری شده است.
- فشار حافظه: اگر سرور پایگاه داده شما به طور مکرر در حال تعویض یا تمام کردن حافظه است ، این نشانه ای است که نمی تواند بار فعلی را تحمل کند.
- تأخیر در تکرار: در یک تنظیم با ماکت های Read ، اگر ماکت ها به طور مداوم در پشت اولیه قرار بگیرند ، نشان می دهد که بار نوشتن برای یک بار اصلی بسیار زیاد است.
وقتی شروع به دیدن این علائم کردید ، وقت آن است که رویکردهای دیگر را در نظر بگیرید:
- مقیاس بندی عمودی: به سخت افزار قدرتمندتر ارتقا دهید. این می تواند تسکین موقت را فراهم کند اما محدودیت ها (و هزینه های آن) است.
- توری: داده های خود را در چندین نمونه postgres توزیع کنید. این می تواند ظرفیت شما را تا حد زیادی افزایش دهد اما پیچیدگی قابل توجهی را به کاربرد شما اضافه می کند. من در مقاله قبلی خود در مورد “رسیدگی به حجم داده های در حال رشد در Postgres” Sharding را پوشانده ام.
- راه حل های بدون سرور بدون سرور: اگر شما شرکت B2B با بسیاری از مشتریان با اندازه های مختلف هستید ، یک راه حل چند مستاجر مجازی مانند نیل می تواند مناسب شما باشد.
- راه حل های DSQL سازگار با Postgres: پایگاه داده های SQL توزیع شده که یا Postgres یا Postgres سازگار هستند مانند Citus ، Aurora DSQL و CockroachDB ممکن است یک روش مناسب برای رسیدگی به مقیاس شما باشد بدون اینکه شما را مجبور به تغییر بخش اعظم کد خود کند. هر یک از آنها نقاط قوت و ضعف خود را دارند و مناسب برای شما بستگی به موارد و نیازهای استفاده شما دارد.
- عیاشی: برای انواع خاصی از داده ها و الگوهای دسترسی ، یک پایگاه داده NOSQL مانند MongoDB ، Scylladb یا DynamoDB ممکن است برای سناریوهای با کنفرانس بالا مناسب تر باشد ، تا زمانی که داده های شما حس بیشتری پیدا کند.
- بانکهای اطلاعاتی تخصصی: برای بار کاری تحلیلی ، استفاده از یک پایگاه داده تحلیلی اختصاصی را برای بهینه سازی شده برای گردش خون بالا ، پرس و جوهای کم تأخیر در مجموعه داده های بزرگ مانند Pinot ، DuckDB یا یا هجوم tinybird
خط پایین
مقیاس بندی postgres برای رسیدگی به کاربران همزمان دیگر به معنای بازسازی کل پشته شما نیست. با جمع آوری اتصال شروع کنید – این میوه کمترین آویزان است و اغلب تسکین فوری را فراهم می کند. از آنجا ، نمایش داده های خود را بهینه کرده و استراتژی های ذخیره سازی هوشمند را پیاده سازی کنید. فقط در این صورت باید به راه حل های پیچیده تری مانند Read Replicas ، تنظیم پیکربندی و Sharding نگاه کنید.
در اینجا یک لیست چک سریع برای مقابله با مسائل همزمانی وجود دارد:
- PGBouncer را برای جمع آوری اتصال تنظیم کنید.
- پرس و جوهای آهسته را بهینه کنید و اتصالات را بالا ببرید.
- ذخیره سازی را در جایی که منطقی باشد اضافه کنید.
- وظایف پس زمینه را به پردازش ASYNC منتقل کنید.
- در نظر بگیرید که ماکت های خواندن را برای بار کاری سنگین خواندن در نظر بگیرید.
فراموش نکنید که عملکرد خود را هنگام رفتن معیار کنید. ابزارهایی مانند PGBench می توانند به اندازه گیری تأثیر تغییرات شما کمک کنند. ممکن است تعجب کنید که این بهینه سازی ها تا چه حد می توانند شما را به خود جلب کنند – من دیده ام که سیستم ها فقط با اجرای مناسب اتصال و بهینه سازی پرس و جو ، 10 برابر کاربران بیشتری را کنترل می کنند.
و اگر به محدودیت ها ضربه بزنید؟ اشکالی ندارد این احتمالاً بدان معنی است که برنامه شما با موفقیت در حال رشد است. فقط اطمینان حاصل کنید که علائم هشدار دهنده را به اندازه کافی زود تشخیص داده اید تا حرکت بعدی خود را برنامه ریزی کنید ، خواه این داده های شما را به خود جلب کند یا برخی از بارهای کاری را به پایگاه داده های تخصصی منتقل کنید.
در این مجموعه ، من در مورد نحوه اجرای بارهای OLAP در Postgres به طور اجرا کننده بحث خواهم کرد ، زیرا گاهی اوقات چالش واقعی فقط کار با کاربران بیشتر نیست ، بلکه رسیدگی به آنچه کاربران می خواهند با داده های شما انجام دهند.
آیا باید تجزیه و تحلیل های خود را از Postgres خارج کنید؟
Tinybird زیرساخت داده برای تیم های نرم افزاری است. شما می توانید جداول Postgres خود را با SQL پرس و جو کنید و نمایش داده شدگان را به عنوان نقاط پایانی API پویا و مقیاس پذیر برای گزارشگری در محصول ، داشبورد در زمان واقعی و موارد دیگر منتشر کنید.
شما می توانید Tinybird را به صورت رایگان و بدون محدودیت زمانی امتحان کنید. اینجا ثبت نام کنید