目录
⛅1.4 同步(synchronous)连接池与异步(asynchronous)连接池
☀️0. 前言
上次给大家介绍了线程池的实现,这次来介绍下 Linux 下数据库连接池的实现,以大家最为熟悉的 MySQL 为例,因为连接池总体实现还是较为复杂的,本次以一个开源框架的数据库连接池部分为例进行讲解。
🌤️1. 数据库连接池概述
⛅1.1 服务器与数据库交互
首先,服务器与数据库的交互是请求响应模式,通常所使用的是 TCP 长连接,而 TCP 连接需要三次握手和四次挥手,并且每次连接都需要验证账号密码等,所以连接资源属于耗时资源,可以用连接池来复用连接。
⛅1.2 MySQL 数据库网络模型
这里来简单介绍以下 MySQL 数据库的网络模型:
主线程使用 IO 多路复用中的 select 来监听 listenfd,如果 listenfd 上触发了可读事件,就说明有客户端来连接,就为他分配一个连接线程,同一个连接上,如果收到多个请求,该连接线程是串行执行的。如果对 IO 多路复用部分不熟悉的同学,可以看看我网络专栏的部分文章,这里你或许会疑惑为什么 MySQL 用的是 select,而不是性能更高的 epoll?主要原因是 select 支持跨平台,epoll 只是 linux 下的,并且 MySQL 规定最高只能监听128个文件描述符,在这种小数量下,select 其实和epoll 性能是差不多的。
所以当我们创建多条连接时,每条连接 MySQL 都会分配一个线程,可以充分释放 MySQL 执行SQL 语句的性能,那是不是创建的连接越多越好呢?显然不是的,看过线程池的同学应该明白,一般数量控制在 CPU 核心数比较合适,创建的太多反而会降低性能。
⛅1.3 MySQL 连接驱动安装
如果要用 C/C++ 实现数据库连接池,首先要安装 libmysqlclient-dev 驱动,这个官方提供的驱动使用了阻塞 IO,具体这个阻塞的 IO 要怎么理解呢?以最常见的 query() 函数为例:首先将 sql 通过MySQL 协议打包,然后服务器调用 send() 或 write(),然后调用 rend() 或 recv() 会阻塞线程等待 MySQL 的返回,最后确定是完整回应包后进行协议解析并将结果返回,协议打包和解析都是MySQL 驱动帮我们完成的,所以可以很明显看到这是个耗时操作,一般主线程在处理业务逻辑需要和数据库交互的时候,这种阻塞的耗时操作都需要优化。
⛅1.4 同步(synchronous)连接池与异步(asynchronous)连接池
同步和异步的概念不好理解,经常要和阻塞和非阻塞搞混,这里以后面要实现的两个接口为例:
QueryResult Query(char const* sql, T* c