cover

建立一條 DB 連線要 50ms。你的 API 每秒處理 100 個請求,每個請求都重新建連線?那你每秒光是建連線就花 5 秒,數學上就已經不可能了。

這就是為什麼所有正經的後端框架都有 connection pool。

先講結論

Object Pool 預先建立一批物件放在池子裡。需要的時候「借出」、用完「歸還」。物件不會被銷毀,下次再借出去。省去了重複建立和銷毀的成本。

classDiagram
    class ConnectionPool {
        -size : Number
        -available : Connection[]
        -inUse : Connection[]
        +acquireConnection() Connection
        +releaseConnection(conn) void
    }
    class Connection {
        -id : Number
        -inUse : boolean
        +execute(query) Result
        +reset() void
    }
    ConnectionPool o-- Connection : 管理
    note for ConnectionPool "借出 → 使用 → 歸還\n避免重複建立"

實戰:資料庫連線池

Object Pool 模式:物件借用與歸還的共享池

class Connection {
    constructor(id) {
        this.id = id;
        this.inUse = false;
    }
 
    async connect() {
        // 模擬昂貴的連線建立
        await new Promise(resolve => setTimeout(resolve, 50));
        console.log(`連線 ${this.id} 已建立`);
    }
 
    async execute(query) {
        console.log(`連線 ${this.id} 執行: ${query}`);
        return { success: true, connectionId: this.id };
    }
 
    reset() { this.inUse = false; }
}
 
class ConnectionPool {
    constructor(size) {
        this.size = size;
        this.available = [];
        this.inUse = [];
    }
 
    async initialize() {
        for (let i = 0; i < this.size; i++) {
            const conn = new Connection(i + 1);
            await conn.connect();
            this.available.push(conn);
        }
    }
 
    acquire() {
        if (this.available.length === 0) {
            throw new Error('連線池已滿');
        }
        const conn = this.available.pop();
        conn.inUse = true;
        this.inUse.push(conn);
        return conn;
    }
 
    release(conn) {
        const index = this.inUse.indexOf(conn);
        if (index === -1) throw new Error('這條連線不在池裡');
        this.inUse.splice(index, 1);
        conn.reset();
        this.available.push(conn);
    }
}
 
// 使用
const pool = new ConnectionPool(3);
await pool.initialize(); // 預先建立 3 條連線
 
const conn = pool.acquire();       // 借一條
await conn.execute('SELECT * FROM users');
pool.release(conn);                 // 還回去
 
const conn2 = pool.acquire();       // 又借出來——同一條連線,不用重建

你已經在用了

  • PostgreSQL / MySQL 的 connection pool:pg、mysql2 都有內建
  • Thread pool:Node.js 的 Worker threads、Java 的 ThreadPoolExecutor
  • HTTP/2 connection pool:瀏覽器對同一個 domain 的連線複用

這些都是 Object Pool 的實際應用。

要注意什麼?

  • 記得歸還:用完不還,池子就空了。在有 GC 的語言裡尤其容易忘記,因為你以為 GC 會處理——但 pool 不知道你用完了
  • 池子大小:太小會排隊等待、太大浪費資源。需要根據實際負載調整
  • 物件狀態清理:歸還前要 reset,不然下一個使用者會拿到上一個人的殘留狀態

Object Pool 就像圖書館——書不是你的,你借出去、看完、還回來,其他人再借。問題是總有人借了不還


延伸閱讀