[Nodejs] mysql vs mysql2 , Connection Pool

이준규·2022년 4월 24일
6

백엔드

목록 보기
1/15

mysql 과 mysql2의 차이

여러가지 성능 개선을 위한 차이점이 존재함.

나는 promise 지원 여부에 대한 차이와
준비된 명령문(prepared statements) 지원의 차이
를 확실히 기억하고 넘어가려한다.

import * as mysql from "mysql2";
...

async findById(id: string) {
    return new Promise((resolve, reject) => {
      connection.query(
        `SELECT * FROM student WHERE id = "${id}";`,
        function(err, results) {
          if (err) reject(err);
          resolve(results);
        }
      );
    });
  }

지적받은 내 service 함수.
쿼리 결과를 받는 것을 보장한 뒤 return 하기 위해 promise로 커넥션을 관리했다.

mysql2가 아닌 mysql 을 사용해서 쿼리를 실행했다면 적절한 코드 였을 것이라고 생각함.

하지만, promise 를 지원하는 mysql2를 사용했으니 굳이 저럴 필요가 없었음

  • mysql 사용시엔 promise-mysql 모듈을 따로 설치하는 방법도 있음

mysql2 를 사용한 코드로 바꿔보면,

import * as mysql from "mysql2";
...

async findById(id: string) {
    return await connection.query(`SELECT * FROM student WHERE id = "${id}";`);
}

또는

import * as mysql from "mysql2";
...

async findById(id: string) {
  	try {
      const result = await connection.execute('SELECT * FROM student WHERE id = ?;', [id]);
      return result;
    } catch (err) {
      return err;
    }
}
  • await 을 사용하기도 했지만
    connection.query -> connection.execute 로 바꿔썻다.

  • execute를 사용한 이유는 mysql2 에서는 prepared statements를 지원하기 때문이다.

  • MySQL은 statement를 준비하고 쿼리하는 execute helper를 제공한다. 또한 prepare / unprepare 메서드를 통해 직접 statement를 준비하거나 준비하지 않을 수 있다.

  • execute는 내부적으로 prepare와 query를 호출한다.

만약 같은 statement를 실행(execute)한다면, LRU 캐시로부터 불려올 것이다.
그로 인해 쿼리 준비 시간을 아낄 수 있고 성능도 좋아진다.


Connection Pool

  • Connection을 사용하는 방법:

    createConnection >
    connection.connect > connection.query > connection.end

  • 이런 경우 사용자 요청이 많아져서 많은 connection이 발생 했을 때 과부하가 올 수 있다.

  • Connection Pool ?
    일정 수의 커넥션을 pool에 보관하여 사용자의 요청이 발생하면 연결을 해주고 연결 종료 시 Pool에 다시 반환하여 보관하는 것.

이해하기에 좋은 그림이 있어서 퍼옴

mysql2에서는 pool을 사용하는 두 가지 방법이 있다.

import * as mysql from "mysql2";
import 'dotenv/config';

const pool = mysql.createPool({
  host: process.env.HOST,
  password: process.env.PASSWORD,
  user: process.env.USERNAME,
  database: process.env.DATABASE
});

pool.query("SELECT field FROM atable", function(err, rows, fields) {

}) 

pool.getConnection(function(err, conn) {
  conn.query();
  pool.releaseConnection(conn);
});
  • 두 가지 같은 동작이지만 위의 예시의 경우
    자동적으로 getConnenction> query > release 를 수행해준다.

Pool에 promise 를 사용하기.

mysql2 공식 문서에 따른 사용법으로 위의 예시에 promise와 pool을 도입하여 리팩토링 해볼게요

import * as mysql from "mysql2";
import 'dotenv/config';

const pool = mysql.createPool({
  host: process.env.HOST,
  password: process.env.PASSWORD,
  user: process.env.USERNAME,
  database: process.env.DATABASE
});

const promisePool = pool.promise();

async findById(id: string) {
  	try {
      const result = await promisePool.query('SELECT * FROM student WHERE id = ?;', [id]);
      return result;
    } catch (err) {
      return err;
    }
}

Pool Options

  • pool에 담을 커넥션 리미트를 옵션으로 추가할 수 있다.
    connectionLimit
const pool = mysql.createPool({
  host: process.env.HOST,
  password: process.env.PASSWORD,
  user: process.env.USERNAME,
  database: process.env.DATABASE
  connectionLimit: 30
});
  • 30개로 설정했지만, 이 때 30개 이상의 커넥션 요청이 오면 어떻게 될까?

queue에 담겨서 앞의 커넥션이 release 되는 것을 기다리게 된다고 하는데.

기다리지 않고 에러를 발생 시킬 수 있는 옵션을 추가 할 수도 있따.

waitForConnections: false,

참고)

https://www.npmjs.com/package/mysql2
https://rat2.tistory.com/8
https://cotak.tistory.com/105

profile
백엔드

2개의 댓글

comment-user-thumbnail
2024년 5월 2일

아무생각없이 pool 만들어놓고
mydb_pool.getConnection()
이걸로 다시 연결 만들서 사용했더니 8번만에 먹통이 되는군요

이글 보니 고쳤습니다. ㅎㅎ

1개의 답글