Connection Pool — nima uchun kerak, sozlash
Muammodan boshlaylik
Node.js API. Har bir so'rovda:
app.get('/mahsulotlar', async (req, res) => {
const conn = await mysql.createConnection(config); // ulanish ochish
const [rows] = await conn.query('SELECT id, nom FROM mahsulotlar LIMIT 20');
await conn.end(); // yopish
res.json(rows);
});
Sekundiga 500 so'rov kelganda:
- Har bir ulanish ochish — TCP handshake, autentifikatsiya, TLS: 5-20 ms. So'rovning o'zi esa 1 ms
- Bir lahzada yuzlab ulanish ochilib-yopiladi
- MySQL chegarasi:
ERROR 1040 (HY000): Too many connections
Sayt to'xtadi — bazada hech qanday og'ir so'rov yo'q, faqat ulanishlar ko'p.
Connection pool — ulanishlarni har safar yaratmasdan, qayta ishlatish.
Pool qanday ishlaydi
┌──────────────┐ ┌─────────────────────────┐ ┌───────────┐
│ So'rov 1 │──┐ │ POOL │ │ │
│ So'rov 2 │──┼───→ │ [ulanish 1] ← band │ ←────→ │ MySQL │
│ So'rov 3 │──┤ │ [ulanish 2] ← band │ │ │
│ ... │ │ │ [ulanish 3] ← bo'sh │ │ │
│ So'rov 500 │──┘ │ ... (jami 10 ta) │ └───────────┘
└──────────────┘ │ navbat: so'rov 11-500 │
└─────────────────────────┘
- Ilova ishga tushganda pool bir nechta ulanishni oldindan ochadi
- So'rov keldi — pooldan bo'sh ulanishni oladi
- Ish tugadi — ulanishni yopmaydi, pool ga qaytaradi
- Hammasi band bo'lsa — so'rov navbatda kutadi
Hayotiy o'xshatma: taksi bekati. Har bir yo'lovchi uchun yangi mashina sotib olish (va yo'l oxirida tashlab ketish) o'rniga — bekatda 10 ta taksi navbatda turadi. Yo'lovchi oladi, borib keladi, mashina bekatga qaytadi.
Node.js — mysql2
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: 'dokon',
connectionLimit: 10, // maksimal ulanishlar
maxIdle: 10, // bo'sh turgan maksimal ulanishlar
idleTimeout: 60000, // bo'sh ulanish 60 soniyadan keyin yopiladi
queueLimit: 0, // navbat chegarasi (0 — cheksiz)
enableKeepAlive: true,
});
app.get('/mahsulotlar', async (req, res) => {
const [rows] = await pool.query('SELECT id, nom FROM mahsulotlar LIMIT 20');
res.json(rows);
});
pool.query() — o'zi ulanish oladi, so'rovni bajaradi va qaytaradi.
Tranzaksiyalar — bitta ulanishda
Tranzaksiya bitta ulanish ichida bo'lishi shart. pool.query() har safar boshqa ulanish olishi mumkin:
// ❌ BEGIN bir ulanishda, INSERT boshqasida, COMMIT uchinchisida
await pool.query('START TRANSACTION');
await pool.query('INSERT ...');
await pool.query('COMMIT');
// ✅
const conn = await pool.getConnection();
try {
await conn.beginTransaction();
await conn.query('INSERT ...');
await conn.query('UPDATE ...');
await conn.commit();
} catch (e) {
await conn.rollback();
throw e;
} finally {
conn.release(); // ⚠️ albatta pool ga qaytaring!
}
[!WARNING]
conn.release()ni unutish — ulanish oqishi (connection leak). Har bir xatoli so'rov bitta ulanishni abadiy band qiladi. 10 ta xatodan keyin pool bo'shaydi va barcha so'rovlar navbatda abadiy kutadi — sayt "qotadi", bazada esa hech qanday yuklama yo'q.finallybloki — majburiy.
PHP — boshqacha model
Klassik PHP (PHP-FPM) da har bir HTTP so'rov — alohida jarayon, va u tugaganda hamma narsa yo'qoladi. Ilova ichida pool yo'q.
Variantlar:
- Persistent connections —
PDO::ATTR_PERSISTENT => true: har bir PHP-FPM worker o'z ulanishini so'rovlar orasida saqlaydi. 50 ta worker — 50 ta doimiy ulanish - Tashqi pooler — ProxySQL: PHP unga ulanadi, u esa MySQL ga kamroq doimiy ulanishlar saqlaydi
- Uzoq yashovchi PHP — Swoole, RoadRunner, FrankenPHP, Laravel Octane: Node.js kabi doimiy jarayon, ichida haqiqiy pool
Kichik va o'rta loyihalarda oddiy PHP-FPM yetarli — har bir so'rovda ulanish ochish PHP + MySQL bitta serverda bo'lsa juda tez.
Pool hajmini tanlash
Intuitiv fikr: "katta pool — tezroq". Noto'g'ri.
MySQL serverining CPU yadrolari va disklari cheklangan. 8 yadroli serverga 500 ta ulanish bir vaqtda so'rov yuborsa — ular navbat uchun kurashadi, kontekst almashinuvi va qulflar ko'payadi, umumiy tezlik pasayadi.
Boshlang'ich formula (PostgreSQL jamiyatidan, MySQL uchun ham yaxshi yo'nalish):
pool hajmi ≈ (CPU yadrolari × 2) + disklar soni
8 yadroli baza serveri uchun — taxminan 15-20 ta faol ulanish. Bu barcha ilova serverlari uchun jami.
4 ta ilova serveri × connectionLimit 10 = 40 ta ulanish ✅
20 ta ilova serveri × connectionLimit 50 = 1000 ta ulanish ❌
Ilova serverlari ko'payganda — har birining pool hajmini kamaytiring yoki tashqi pooler (ProxySQL) qo'ying.
Keyin — o'lchang va moslashtiring: navbat uzunligi, so'rov kutish vaqti, baza CPU yuklamasi.
MySQL tomonida
SHOW VARIABLES LIKE 'max_connections'; -- 151 (standart)
SHOW STATUS LIKE 'Threads_connected'; -- hozir ochiq ulanishlar
SHOW STATUS LIKE 'Max_used_connections'; -- eng ko'p bir vaqtda
SHOW STATUS LIKE 'Aborted_connects'; -- muvaffaqiyatsiz ulanishlar
max_connections ni oshirish (my.cnf):
[mysqld]
max_connections = 500
wait_timeout = 600 # bo'sh ulanish 10 daqiqadan keyin uziladi
Har bir ulanish MySQL da xotira oladi (bir necha MB). 5000 ulanish — gigabaytlar. max_connections ni oshirishdan oldin — nima uchun shuncha ulanish kerakligini tushuning (ko'pincha — leak yoki noto'g'ri pool hajmi).
wait_timeout tuzog'i
MySQL bo'sh turgan ulanishni wait_timeout dan keyin o'zi uzadi. Pool esa bundan bexabar va o'lik ulanishni beradi:
Error: Connection lost: The server closed the connection.
Yechim: pool ning idleTimeout ini MySQL ning wait_timeout idan kichik qilish, yoki enableKeepAlive.
ProxySQL — tashqi pooler
Katta tizimlarda ilova va MySQL orasiga ProxySQL qo'yiladi:
[ilova 1] ─┐
[ilova 2] ─┼─→ [ProxySQL] ─→ [source]
[ilova N] ─┘ │ └──→ [replica 1, 2]
- Yuzlab ilova ulanishlarini o'nlab haqiqiy MySQL ulanishlariga to'playdi (multiplexing)
SELECTlarni replica larga, yozishlarni source ga avtomatik yo'naltiradi (53-dars)- Failover da yo'naltirishni o'zgartiradi — ilova bilmaydi ham
- So'rovlarni keshlash, cheklash, qayta yozish
Nima uchun shunday?
Nima uchun MySQL ulanishi bunchalik "qimmat" — oddiy so'rov-ku?
Har bir ulanish uchun MySQL alohida thread yaratadi, uning uchun xotira buferlari ajratadi, foydalanuvchini tekshiradi (parol xeshlash — ataylab sekin), TLS bo'lsa — shifrlash kalitlarini almashadi. Bularning hammasi so'rovning o'zidan ko'p vaqt oladi. Pool — bu "ishga tayyorlanish" xarajatini bir marta to'lab, keyin minglab so'rovga bo'lib yuborish.
Xulosa
Eslab qoling:
- Har so'rovda ulanish ochish — sekin va
Too many connectionsga olib keladi - Pool — ulanishlarni qayta ishlatish; band bo'lsa — navbat
- Node.js:
mysql2.createPool({ connectionLimit }),pool.query() - Tranzaksiya —
getConnection()+finally { conn.release() }; release ni unutish — leak - PHP-FPM: persistent connections yoki ProxySQL; Octane/Swoole — haqiqiy pool
- Katta pool ≠ tez; boshlanish: ~(yadrolar × 2), barcha ilovalar uchun jami
max_connections,wait_timeoutva poolidleTimeoutni moslang- Katta tizimlarda — ProxySQL
Keyingi dars
55-dars: Node.js — mysql2 moduli, CRUD, parametrli so'rovlar