C++ MySQL数据库连接池
新手学了C++多线程,看了些资料练手写了C++数据库连接池小项目,本身的源码地址
关键技术点
MySQL数据库编程、单例模式、queue队列容器、C++11多线程编程、线程互斥、线程同步通信和
unique_lock、基于CAS的原子整形、智能指针shared_ptr、lambda表达式、生产者-消费者线程模型
连接池项目
为了提高MySQL数据库(基于C/S计划)的访问瓶颈,除了在服务器端增加缓存服务器缓存常用的数据之外(比方redis),还可以增加连接池,来提高MySQL Server的访问效率,在高并发情况下,大量的TCP三次握手、MySQL Server连接认证、MySQL Server关闭连接回收资源和TCP四次挥手所耗费的性能时间也是很明显的,增加连接池就是为了减少这一部分的性能损耗。
在市场上比力流行的连接池包括阿里的druid,c3p0以及apache dbcp连接池,它们对于短时间内大量的数据库增删改查操作性能的提升是很明显的,但是它们有一个共同点就是,全部由Java实现的。
那么本项目就是为了在C/C++项目中,提供MySQL Server的访问效率,实现基于C++代码的数据库连接池模块。
连接池功能点介绍
连接池一样平常包含了数据库连接所用的ip地址、port端标语、用户名和密码以及别的的性能参数,比方初始连接量,最大连接量,最大空闲时间、连接超时时间等,该项目是基于C++语言实现的连接池,主要也是实现以上几个全部连接池都支持的通用基础功能。
- 初始连接量(initSize):表现连接池事先会和MySQL Server创建initSize个数的connection连接,当应用发起MySQL访问时,不用再创建和MySQL Server新的连接,直接从连接池中获取一个可用的连接就可以,使用完成后,并不去开释connection,而是把当前connection再归还到连接池当中。
- 最大连接量(maxSize):当并发访问MySQL Server的哀求增多时,初始连接量已经不够使用了,此时会根据新的哀求数量去创建更多的连接给应用去使用,但是新创建的连接数量上限是maxSize,不能无限定的创建连接,由于每一个连接都会占用一个socket资源,一样平常连接池和服务器程序是摆设在一台主机上的,如果连接池占用过多的socket资源,那么服务器就不能吸收太多的客户端哀求了。当这些连接使用完成后,再次归还到连接池当中来维护。
- 最大空闲时间(maxIdleTime):当访问MySQL的并发哀求多了以后,连接池内里的连接数量会动态增加,上限是maxSize个,当这些连接用完再次归还到连接池当中。如果在指定的maxIdleTime内里,这些新增加的连接都没有被再次使用过,那么新增加的这些连接资源就要被回收掉,只需要保持初始连接量initSize个连接就可以了。
- 连接超时时间(connectionTimeout):当MySQL的并发哀求量过大,连接池中的连接数量已经到达maxSize了,而此时没有空闲的连接可供使用,那么此时应用从连接池获取连接无法成功,它通过阻塞的方式获取连接的时间如果超connectionTimeout时间,那么获取连接失败,无法访问数据库。
实现的逻辑图片
数据表的结构
文章内容不会将MySQL的安装,基于你已经下载了mysql server 8.0 ,我们建立一个mysql数据库的数据表来演示后面如何用C++连接数据表,并且写SQL.
先辈入mysql,输入密码创建一个数据库名叫chat,同时创建数据表- CREATE DATABASE chat;
- use chat;
- CREATE TABLE user (
- id INT(11) NOT NULL AUTO_INCREMENT,
- name VARCHAR(50) NOT NULL,
- age INT(11) NOT NULL,
- sex ENUM('male', 'female') NOT NULL,
- PRIMARY KEY (id)
- );
复制代码 如果输出OK就代表创建user好了,我们来看看数据表- desc user;
- +-------+-----------------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +-------+-----------------------+------+-----+---------+----------------+
- | id | int | NO | PRI | NULL | auto_increment |
- | name | varchar(50) | NO | | NULL | |
- | age | int | NO | | NULL | |
- | sex | enum('male','female') | NO | | NULL | |
- +-------+-----------------------+------+-----+---------+----------------+
复制代码 查看一下内容,没有- mysql> select * from user;
- Empty set (0.19 sec)
复制代码 到这里我们MySQL表就创建好了,我们不用管他,我们进行编写CPP连接数据库代码
连接数据库,并且执行sql语句
打开VS2019,并且创建一个控制台项目,项目结构如图
main.cpp负责执行主函数代码,Connect负责编写封装数据库的连接和sql操作,mysqlPool负责编写数据库连接池。
但我们还不急着编写代码,先导入需要的外部库,在VS上需要进行相应的头文件和库文件的设置,如下:
- 1.右键项目 - C/C++ - 常规 - 附加包含目次,填写mysql.h头文件的路径
- 2.右键项目 - 链接器 - 常规 - 附加库目次,填写libmysql.lib的路径
- 3.右键项目 - 链接器 - 输入 - 附加依赖项,填写libmysql.lib库的名字
- 4.把libmysql.dll动态链接库(Linux下后缀名是.so库)放在工程目次下
如果你没有修改过MySQL路径,一样平常mysql.h在你的电脑路径如下
如果你没有修改过MySQL路径,一样平常libmysql.lib在你的电脑路径如下
libmysql.dll文件存放在你项目文件路径下面
1.封装Mysql.h的接口成connection类
接下来封装一下mysql的数据库连接代码,不懂的看看解释,也很简朴的调用Mysql.h的接口,我们在connection中额外加入创建时间函数和存活时间函数,不能让空闲的线程存活时间超过界说的最大空闲时间
connect.h的代码如下- #pragma once
- #include <mysql.h>
- #include <string>
- #include <ctime>
- using namespace std;
- /*
- 封装MySQL数据库的接口操作
- */
- class Connection
- {
- public:
- // 初始化数据库连接
- Connection();
- // 释放数据库连接资源
- ~Connection();
- // 连接数据库
- bool connect(string ip,
- unsigned short port,
- string user,
- string password,
- string dbname);
- // 更新操作 insert、delete、update
- bool update(string sql);
- // 查询操作 select
- MYSQL_RES* query(string sql);
- // 刷新一下连接的起始的空闲时间点
- void refreshAliveTime() { _alivetime = clock(); }
- // 返回存活的时间
- clock_t getAliveeTime()const { return clock() - _alivetime; }
- private:
- MYSQL* _conn; // 表示和MySQL Server的一条连接
- clock_t _alivetime; // 记录进入空闲状态后的起始存活时间
- };
复制代码 connect.cpp的代码如下-
- #include "public.h"
- #include "Connect.h"
- #include <iostream>
- using namespace std;
- Connection::Connection()
- {
- // 初始化数据库连接
- _conn = mysql_init(nullptr);
- }
- Connection::~Connection()
- {
- // 释放数据库连接资源
- if (_conn != nullptr)
- mysql_close(_conn);
- }
- bool Connection::connect(string ip, unsigned short port,
- string username, string password, string dbname)
- {
- // 连接数据库
- MYSQL* p = mysql_real_connect(_conn, ip.c_str(), username.c_str(),
- password.c_str(), dbname.c_str(), port, nullptr, 0);
- return p != nullptr;
- }
- bool Connection::update(string sql)
- {
- // 更新操作 insert、delete、update
- if (mysql_query(_conn, sql.c_str()))
- {
- LOG(+ "更新失败:" + sql);
- return false;
- }
- return true;
- }
- MYSQL_RES* Connection::query(string sql)
- {
- // 查询操作 select
- if (mysql_query(_conn, sql.c_str()))
- {
- LOG("查询失败:" + sql);
- return nullptr;
- }
- return mysql_use_result(_conn);
- }
复制代码 在public.h中编写的代码,资助我们输出日志和告诫
[code]#pragma once#include #define LOG(str) \ std::cout |