用数据说话 - 数据库连接池真的香吗?

3,362 阅读2分钟

什么是连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

为什么要使用连接池

一次数据库的步骤

  1. TCP建立连接的三次握手
  2. MySQL认证的三次握手
  3. 真正的SQL执行
  4. MySQL的关闭
  5. TCP的四次握手关闭

利用连接池后

连接池优点

  • 较少了网络开销
  • 系统的性能会有一个实质的提升
  • 没了麻烦的TIME_WAIT状态

连接池实战

预备1:模拟并发处理

由于只有并发处理才能体现连接池的价值。所以要试验连接池前我们先搞一个并发函数。

要求如下:

  1. 可以设置并发量
  2. 可以设置总执行次数
  3. 可以打印每次执行的耗时和总耗时
const sleep = delay => new Promise(resolve => setTimeout(resolve, delay))

const asyncFun = async (fun, curMax = 4, sum = 200) => {
    let num = 0
    let curNum = 0
    console.time('总耗时');
    console.log('beginTime:' + new Date().toLocaleString())
    const result = []
    while (num !== sum) {
        if (curNum <= curMax) {
            result.push(new Promise(async resolve => {
                console.log(`Process Run 并发数:${curNum} 完成:${num}/${sum} `)
                res = await fun()
                curNum--
                resolve(res)
            }))
            num++
            curNum++
        } else {
            await sleep(10)
        }
    }
    console.log('endTime:' + new Date().toLocaleString())
    console.timeEnd('总耗时');
}
module.exports = { asyncFun }

// 测试
const test = async () => {
    const delay = (Math.random() * 1000).toFixed()
    await sleep(delay)
}
setTimeout(() => asyncFun(test, 4, 20))

预备2:mysql查看连接数

mysqladmin -uroot -pexample processlist

未使用连接池

(async () => {
    // get the client
    const mysql = require('mysql2/promise');
    // 连接配置
    const cfg = {
        host: "localhost",
        user: "root",
        password: "example", // 修改为你的密码
        database: "shop", // 请确保数据库存在
        connectionLimit : 5,
    }


    // 非连接池
    const query = async () => {
        const connection = await mysql.createConnection(cfg)
        const [rows, fields] = await connection.execute(`SELECT * FROM users`)
        // console.log('select:', rows)
        connection.destroy()
    }

    const { asyncFun } = require('./async')
    await asyncFun(query, 20, 1000)

})()

执行耗时

数据库的连接数

使用连接池

下面我们改用连接池方式

    
    // 设置连接池
    const pool = await mysql.createPool(cfg)
    // 连接池
    const query = async () => {
        const connection = await pool.getConnection()
        const [rows, fields] = await connection.execute(`SELECT * FROM users`)
        // console.log('select:', rows)
        connection.release()
    }

执行耗时

数据库的连接数

数据对比与总结

非连接池 连接池
总耗时 7.043 1.459s
连接数 20(等于并发数) 5(连接池连接数)

在处理中只使用了五个连接有效的节省了系统资源,并且提升了性能总耗时只有原来的大约五分之一。

数据库中间件中的连接池设置

(async () => {
    const Sequelize = require("sequelize");

    // 建立连接
    const sequelize = new Sequelize("kaikeba", "root", "example", {
        host: "localhost",
        dialect: "mysql",
        // operatorsAliases: false,
        pool: {
            max: 10,
            min: 0,
            idle: 30000
        }
    });

    // 定义模型
    const Fruit = sequelize.define("Fruit", {
        name: { type: Sequelize.STRING(20), allowNull: false },
        price: { type: Sequelize.FLOAT, allowNull: false },
        stock: { type: Sequelize.INTEGER, defaultValue: 0 }
    });

    // 同步数据库,force: true则会删除已存在表
    let ret = await Fruit.sync({ force: true })
    console.log('sync', ret)
    ret = await Fruit.create({
        name: "香蕉",
        price: 3.5
    })
    // console.log('create', ret)

    const find = async () => {
        Fruit.findAll()
        // console.log('findAll', JSON.stringify(ret, '', '\t'))
    }

    // await find()
    const { asyncFun } = require('./async')
    asyncFun(find,20,100)

})()