Skip to content

Latest commit

 

History

History
127 lines (82 loc) · 7.4 KB

DB Connection Pool.md

File metadata and controls

127 lines (82 loc) · 7.4 KB

DB Connection Pool

API 서버를 구동하고 있었는데, 이따금씩 Nginx 502 Bad Gateway 오류가 났다.

1

서버는 Nginx- PM2 - Node.js - MySQL로 구성돼있었는데 도대체 문제 원인이 무엇인지, 한참 찾았다. 로컬 환경에서는 추가한 node_module을 Git에서는 ignore 해줬기 때문에 실 서버에서 npm install 을 하지 않아서 생기는 문제이기도 했고, Node.js 코드 자체에 문제가 있는 경우에도 502 Bad Gateway 가 뜨곤했다. 그런데 이번에는 npm install도 해주고 코드에도 이상이 없는데 처음에는 잘 작동되다가 갑자기 502 Bad Gateway 가 뜨는 것.

Nginx 에러 로그, PM2 에러 로그, winston을 이용한 에러로그 확인을 모두 했으나 뾰족하게 원인을 찾기 힘들었다. 이곳 저곳 물어보다가 결국 DB Connection Pool 을 Release 하지 않아서 발생한 문제라는 것을 알게 됐다.

구글링을 해보니 DB Connection Pool 문제로 인해 서버 오류가 터졌을 때 그 원인이 DB Connection Pool 이라는 것을 단번에 찾기 어렵다는 글이 많았다. (휴 발견해서 다행 ㅋ)

그래서 정리해보려 한다. DB Connection Pool 은 무엇이며 PoolRelease 해준다는 것은 어떤 의미인가? 만약 Pool은 여러 개 생성만 하고 Release를 하지 않으면 어떤 문제가 발생하는가? 알아보자.

DB Connection Pool 이란?

Single Connection

Single Connection을 사용할 경우 해당 Connection에 여러 Statement를 사용하게 된다. 여러 Statement 중 한 Statement에서 예외가 발생하면 Rollback을 수행해야 하는데 Single Connection의 경우는 해당 Connection을 통해 생성된 다른 모든 Statement의 작업도 Rollback되게 된다. (원치 않는 작업이 발생한다)

npm mysql2

// get the client
const mysql = require('mysql2');
 
// create the connection to database
const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  database: 'test'
});
 
// simple query
connection.query(
  'SELECT * FROM `table` WHERE `name` = "Page" AND `age` > 45',
  function(err, results, fields) {
    console.log(results); // results contains rows returned by server
    console.log(fields); // fields contains extra meta data about results, if available
  }
);
 
// with placeholder
connection.query(
  'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?',
  ['Page', 45],
  function(err, results) {
    console.log(results);
  }
);

참고> 위 예시는 날 쿼리를 그대로 사용했기 때문에 SQL Injection의 위험이 있을 수 있다. 그래서 Prepared Statement라는 것을 대신 사용한다. 이유는 이 사이트를 참조.

단순 Multiple Connection

2

위의 문제를 해결하려면 클라이언트의 각 요청에 대해 개별 Connection을 사용해야 한다. 하지만 다수의 클라이언트가 요청을 하는 경우, 매번 Connection객체를 각각 생성해야 하기 때문에 생성에 대한 소요 시간이 많아진다.

Connection Pool

3

Connection Pool단순 Multiple Connection 의 문제를 해결할 수 있다. Connection Pool은 미리 일정량의 Connection 객체를 생성하고 Pool이라는 공간에 저장한다. DB에 Connection해야하는 클라이언트 요청이 들어올 때, 해당 Pool에 가서 이미 생성되어 있고, 사용가능한 Connection을 찾아 DB에 접근한다.

npm mysql2 문서에는 다음과 같이 설명되어 있다.

Connection pools help reduce the time spent connecting to the MySQL server by reusing a previous connection, leaving them open instead of closing when you are done with them.

This improves the latency of queries as you avoid all of the overhead that comes with establishing a new connection.

Connection Release

Connection은 사용 이후 반드시 Pool에 다시 반납해줘야 한다. Pool에 있는 Connection을 재활용해야하기 때문이다. 가령, Pool에서 생성가능한 Connection의 개수가 최대 4개라고 할 때, Release하지 않은 상태에서 계속 Connection요청이 들어온다면, 5번째 요청 부터는 제대로 응답받지 못할 것이다. Pool에서 이미 4개의 Connection이 사용 중이고, 더 이상 사용가능한 Connection이 없어서 대기 상태에 빠지기 때문이다.

실제 실행 결과는 이 포스트Connection limit 부분을 참조하시라.

...



try {
    const connection = await pool.getConnection(async conn => conn);
    try {

        const Query;
        const Params;

        const [ResultSet] = await connection.query(
            Query,
            Params
        );

    } catch (err) {
        connection.release(); // 다음과 같이 사용 이후에는 release를 해줘야 한다.
        return res.json({ isSuccess: false, code: 500, message: "서버 오류" });
    }
} catch (err) {
    return res.json({ isSuccess: false, code: 501, message: "서버 오류" });

Connection연결 이후 잘 사용 되고 나서도 Release를 해줘야 하지만, Error가 발생했을 때도 반드시 Release를 해줘야 한다. ( 나는 이 모든 부분을 고려하지 못해서 결국 502 Bad Gateway 문제가 발생했었다;)

Pool에서 Connection의 최대 개수는 몇 개가 적당한가?

다음은 8개의 Connection을 최대로 활용할 수 있을 때 4개는 사용 중이고 4개는 대기 중인 상태의 Connection Pool의 상태이다.

4

image

위 4개 속성에 대한 조건은 논리적으로 따져서 결정하면 되지만, (가령, maxIdleminIdle 보다 작으면 안된다 등) 4개 속성을 모두 동일한 개수로 설정해도 무방하다. 중요한 건, maxActive의 값 자체이다.

Connection의 개수를 크게 하면 메모리 소모가 크고 적게 하면 Connection이 많이 발생할 때 대기 시간이 발생하기 때문에 Connection PoolConnection개수는 어플리케이션의 요구사항과 사용자의 수, 서버 메모리, 서버 부하 등의 여러 요소를 고려해서 결정해야 한다.


Reference