MySQL连接池

1 什么是数据库连接池?

数据库连接池(Connection pooling):
在启动时建立足够的数据库连接,组成一个连接池,后续程序可以对池中的连接进行申请、使用、释放。

好处:

  • 资源复用
    • 不使用连接池,需每次都与数据库建立连接
        1. 三次握手建立连接
        1. MySQL认证
        1. SQL执行
        1. 四次挥手断开连接
  • 提高响应速度
    • 一次连接建立和销毁,可复用同一条连接多次执行 SQL 语句

2 MySQL连接驱动

安装libmysqlclient-dev库,提供了mysql.h、动态库和静态库。

该库中提供了一系列接口,具体参考:MySQL :: MySQL 5.7 C API Developer Guide :: 5 C API Basic Interface

流程如下:
初始化连接驱动mysql_library_init,使用mysql连接驱动与MySQL进行交互(执行SQL语句),释放连接资源mysql_library_end。

该驱动使用了阻塞的IO

在这里插入图片描述

左边为服务器,右边为MySQL。

假如服务器调用了一个Query的接口,最终会调用mysql_query

int mysql_query(MYSQL *mysql, const char *stmt_str);

流程如下:

  1. 将sql进行协议打包,变成二进制数据
  2. 调用send/write函数,将数据放入到缓冲区
  3. 调用read函数阻塞线程,等待MySQL返回,确定是一个完整的回应包,会进行协议解析,将结果返回

这整个过程是一个耗时操作,因此不能放在业务线程中

3 同步连接池 vs 异步连接池

同步连接:
直接拿到数据库返回的结果

template <class T>
QueryResult DatabaseWorkerPool<T>::Query(char const* sql, T* connection /*= nullptr*/)
{
    if (!connection)
        connection = GetFreeConnection();

    ResultSet* result = connection->Query(sql); //调用了mysql_query
    connection->Unlock();
    if (!result || !result->GetRowCount() || !result->NextRow())
    {
        delete result;
        return QueryResult(nullptr);
    }

    return QueryResult(result);
}

异步连接:
并不是立马获取到返回结果,而是在其他线程中获取

template <class T>
QueryCallback DatabaseWorkerPool<T>::AsyncQuery(char const* sql)
{
    BasicStatementTask* task = new BasicStatementTask(sql, true);
    // Store future result before enqueueing - task might get already processed and deleted before returning from this method
    QueryResultFuture result = task->GetFuture();
    Enqueue(task);
    return QueryCallback(std::move(result));
}

可以看到,AsyncQuery创建了一个task,并将task入队列。

同步连接池的使用场景: 服务器初始化的时候,开启多个线程初始化,加快初始化流程

异步连接池的使用场景: 业务逻辑需要使用数据库的时候,采用异步连接池
由于核心线程只有一个或少数几个,不能阻塞在那里,影响其他请求的处理,因此核心线程只处理数据库返回的结果,将阻塞等待结果返回交给其他线程处理。

同步连接池: 当前最多允许几个线程或协程井发使用连接

在这里插入图片描述

加锁是为了让每个连接只有一个线程处理

异步连接池: 当前最多允许几个连接同时执行SQL语句

在这里插入图片描述

服务端启动的时候为什么不使用异步连接池?
因为服务器需要数据初始化完成后,才提供服务。

4 代码分析

代码:
链接: https://pan.baidu.com/s/1z-mf8CNsjcYqXSAfpiwmLA?pwd=dzsi 提取码: dzsi

连接池对应着MySQL中一个ip:port:database,如果需要使用多个库,则需要多个连接池

DatabaseWorkerPool.h

void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads);

synchThreads:同步连接池线程数量
asyncThreads:异步连接池线程数量

接口封装

通过接口区分使用的是同步连接池还是异步连接池

同步接口:

  • DirectExecute 不需要结果
  • Query 需要结果

异步接口:

  • Execute 不需要结果
  • AsyncQuery 需要结果
  • DelayQueryHolder 异步执行多个SQL语句

参数支持

简单字符串 “select * from table”

//! Enqueues a one-way SQL operation in string format that will be executed asynchronously.
//! This method should only be used for queries that are only executed once, e.g during startup.
void Execute(char const* sql);

预处理:

//! Enqueues a one-way SQL operation in prepared statement format that will be executed asynchronously.
//! Statement must be prepared with CONNECTION_ASYNC flag.
void Execute(PreparedStatement<T>* stmt);

预处理示例:
对于select * from table where id = 1可传递select * from table where id = ?,直到执行时才传进真正的值,避免每次查询都进行词法、语法分析

作用:节省执行成本外,减少网络传输的数据,避免sql注入

DatabaseWorkerPool

class DatabaseWorkerPool
{
private:
	std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue; //阻塞队列
	std::array<std::vector<std::unique_ptr<T>>, IDX_SIZE> _connections; //0:异步  1:同步
	std::unique_ptr<MySQLConnectionInfo> _connectionInfo; //数据库
	std::vector<uint8> _preparedStatementSize; //预处理
	uint8 _async_threads, _synch_threads; //同步连接池数量、异步连接池数量
};
enum InternalIndex
{
	IDX_ASYNC,
	IDX_SYNCH,
	IDX_SIZE
};

AsyncCallbackProcessor

在这里插入图片描述

添加callback函数

在这里插入图片描述

该接口拿到异步连接池返回的结果,通过调用InvokeIfReady函数

在这里插入图片描述

InvokeIfReady会调用Callback函数

入队

// class DatabaseWorkerPool中
std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue;
// class DatabaseWorkerPool中
template <class T>
QueryCallback DatabaseWorkerPool<T>::AsyncQuery(char const* sql)
{
    BasicStatementTask* task = new BasicStatementTask(sql, true);
    // Store future result before enqueueing - task might get already processed and deleted before returning from this method
    QueryResultFuture result = task->GetFuture();
    Enqueue(task);
    return QueryCallback(std::move(result));
}

在这里插入图片描述

每个BasicStatementTask有一个Promise,task->GetFuture是一个期望,核心线程获取了期望,后续线程获取到结果(兑现承诺)后,核心线程可通过get的方式获取到结果

任务执行

DatabaseWorker.h:

class TC_DATABASE_API DatabaseWorker
{
    public:
        DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection);
        ~DatabaseWorker();

    private:
        ProducerConsumerQueue<SQLOperation*>* _queue;
        MySQLConnection* _connection; //绑定的连接

        void WorkerThread();
        std::thread _workerThread;

        std::atomic<bool> _cancelationToken;

        DatabaseWorker(DatabaseWorker const& right) = delete;
        DatabaseWorker& operator=(DatabaseWorker const& right) = delete;
};

AdhocStatement.h

class TC_DATABASE_API BasicStatementTask : public SQLOperation
{
    public:
        BasicStatementTask(char const* sql, bool async = false);
        ~BasicStatementTask();

        bool Execute() override; //执行
        QueryResultFuture GetFuture() const { return m_result->get_future(); }

    private:
        char const* m_sql;      //- Raw query to be executed
        bool m_has_result;
        QueryResultPromise* m_result;
};
bool BasicStatementTask::Execute()
{
    if (m_has_result)
    {
        ResultSet* result = m_conn->Query(m_sql); //调用mysql_query
        if (!result || !result->GetRowCount() || !result->NextRow())
        {
            delete result;
            m_result->set_value(QueryResult(nullptr)); //兑现承诺
            return false;
        }

        m_result->set_value(QueryResult(result)); //兑现承诺
        return true;
    }

    return m_conn->Execute(m_sql);
}

取出结果:InvokeIfReady兑现承诺

在这里插入图片描述
参考链接:https://xxetb.xetslk.com/s/1QH6AQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值