当前位置:   article > 正文

数据库连接池的实现方法(MySQL+C++)_c++ mysql连接池

c++ mysql连接池

本文主要介绍基于MySQL数据库、使用C++语言实现数据库连接池的方法。

1 准备依赖的文件

在本文中,我们利用MySQL提供的“mysql-connector-c++”以及boost编写数据库连接池。

1.1 安装mysql-connector-c++

“mysql-connector-c++”可以从MySQL官网上下载,如下图:

说明:

  • 上图中的链接为(https://dev.mysql.com/downloads/connector/cpp/),此链接后续可能会改变;
  • 需要根据实际情况(如OS及其版本)选择对应的发行版。

在上述页面中下载下来的文件,直接包含了“mysql-connector”的头文件和共享库,如下:

注意:“mysql-connector”提供的头文件“mysql_connection.h”放在了“jdbc”目录下(如上图)。

1.2 安装boost

使用Yum命令直接安装,如下:

yum install boost-devel.x86_64

2 示例代码

数据库连接池头文件(connection_pool.h),代码如下:

  1. #ifndef __CONNECTION_POOL_H__
  2. #define __CONNECTION_POOL_H__
  3. #include <mysql_connection.h>
  4. #include <mysql_driver.h>
  5. #include <cppconn/exception.h>
  6. #include <cppconn/driver.h>
  7. #include <cppconn/connection.h>
  8. #include <cppconn/resultset.h>
  9. #include <cppconn/prepared_statement.h>
  10. #include <cppconn/statement.h>
  11. #include <pthread.h>
  12. #include <list>
  13. #include <string>
  14. using namespace std;
  15. using namespace sql;
  16. class ConnPool
  17. {
  18. public:
  19. ~ConnPool();
  20. // 获取数据库连接
  21. Connection* GetConnection();
  22. // 将数据库连接放回到连接池的容器中
  23. void ReleaseConnection(Connection *conn);
  24. // 获取数据库连接池对象
  25. static ConnPool* GetInstance();
  26. private:
  27. // 当前已建立的数据库连接数量
  28. int curSize;
  29. // 连接池定义的最大数据库连接数
  30. int maxSize;
  31. string username;
  32. string password;
  33. string url;
  34. // 连接池容器
  35. list<Connection*> connList;
  36. // 线程锁
  37. pthread_mutex_t lock;
  38. static ConnPool* connPool;
  39. Driver* driver;
  40. // 创建一个连接
  41. Connection* CreateConnection();
  42. // 初始化数据库连接池
  43. void InitConnection(int iInitialSize);
  44. // 销毁数据库连接对象
  45. void DestoryConnection(Connection *conn);
  46. // 销毁数据库连接池
  47. void DestoryConnPool();
  48. // 构造方法
  49. ConnPool(string url, string user,string password, int maxSize);
  50. };
  51. #endif/*_CONNECTION_POOL_H */

数据库连接池实现文件(connection_pool.cpp),代码如下:

  1. #include <stdexcept>
  2. #include <exception>
  3. #include <stdio.h>
  4. #include "connection_pool.h"
  5. using namespace std;
  6. using namespace sql;
  7. ConnPool* ConnPool::connPool = NULL;
  8. // 获取连接池对象,单例模式
  9. ConnPool* ConnPool::GetInstance()
  10. {
  11. if (connPool == NULL)
  12. {
  13. connPool = new ConnPool("tcp://192.168.213.130:3306", "root", "", 20);
  14. }
  15. return connPool;
  16. }
  17. // 数据库连接池的构造函数
  18. ConnPool::ConnPool(string url, string userName, string password, int maxSize)
  19. {
  20. this->maxSize = maxSize;
  21. this->curSize = 0;
  22. this->username = userName;
  23. this->password = password;
  24. this->url = url;
  25. try
  26. {
  27. this->driver = sql::mysql::get_driver_instance();
  28. }
  29. catch (sql::SQLException& e)
  30. {
  31. perror("get driver error.\n");
  32. }
  33. catch (std::runtime_error& e)
  34. {
  35. perror("[ConnPool] run time error.\n");
  36. }
  37. // 在初始化连接池时,建立一定数量的数据库连接
  38. this->InitConnection(maxSize/2);
  39. }
  40. // 初始化数据库连接池,创建最大连接数一半的连接数量
  41. void ConnPool::InitConnection(int iInitialSize)
  42. {
  43. Connection* conn;
  44. pthread_mutex_lock(&lock);
  45. for (int i = 0; i < iInitialSize; i++)
  46. {
  47. conn = this->CreateConnection();
  48. if (conn)
  49. {
  50. connList.push_back(conn);
  51. ++(this->curSize);
  52. }
  53. else
  54. {
  55. perror("Init connection error.");
  56. }
  57. }
  58. pthread_mutex_unlock(&lock);
  59. }
  60. // 创建并返回一个连接
  61. Connection* ConnPool::CreateConnection()
  62. {
  63. Connection* conn;
  64. try
  65. {
  66. // 建立连接
  67. conn = driver->connect(this->url, this->username, this->password);
  68. return conn;
  69. }
  70. catch (sql::SQLException& e)
  71. {
  72. perror("create connection error.");
  73. return NULL;
  74. }
  75. catch (std::runtime_error& e)
  76. {
  77. perror("[CreateConnection] run time error.");
  78. return NULL;
  79. }
  80. }
  81. // 从连接池中获得一个连接
  82. Connection* ConnPool::GetConnection()
  83. {
  84. Connection* con;
  85. pthread_mutex_lock(&lock);
  86. // 连接池容器中还有连接
  87. if (connList.size() > 0)
  88. {
  89. // 获取第一个连接
  90. con = connList.front();
  91. // 移除第一个连接
  92. connList.pop_front();
  93. // 判断获取到的连接的可用性
  94. // 如果连接已经被关闭,删除后重新建立一个
  95. if (con->isClosed())
  96. {
  97. delete con;
  98. con = this->CreateConnection();
  99. // 如果连接为空,说明创建连接出错
  100. if (con == NULL)
  101. {
  102. // 从容器中去掉这个空连接
  103. --curSize;
  104. }
  105. }
  106. pthread_mutex_unlock(&lock);
  107. return con;
  108. }
  109. // 连接池容器中没有连接
  110. else
  111. {
  112. // 当前已创建的连接数小于最大连接数,则创建新的连接
  113. if (curSize < maxSize)
  114. {
  115. con = this->CreateConnection();
  116. if (con)
  117. {
  118. ++curSize;
  119. pthread_mutex_unlock(&lock);
  120. return con;
  121. }
  122. else
  123. {
  124. pthread_mutex_unlock(&lock);
  125. return NULL;
  126. }
  127. }
  128. // 当前建立的连接数已经达到最大连接数
  129. else
  130. {
  131. perror("[GetConnection] connections reach the max number.");
  132. pthread_mutex_unlock(&lock);
  133. return NULL;
  134. }
  135. }
  136. }
  137. // 释放数据库连接,将该连接放回到连接池中
  138. void ConnPool::ReleaseConnection(sql::Connection* conn)
  139. {
  140. if (conn)
  141. {
  142. pthread_mutex_lock(&lock);
  143. connList.push_back(conn);
  144. pthread_mutex_unlock(&lock);
  145. }
  146. }
  147. // 数据库连接池的析构函数
  148. ConnPool::~ConnPool()
  149. {
  150. this->DestoryConnPool();
  151. }
  152. // 销毁连接池,需要先销毁连接池的中连接
  153. void ConnPool::DestoryConnPool()
  154. {
  155. list<Connection*>::iterator itCon;
  156. pthread_mutex_lock(&lock);
  157. for (itCon = connList.begin(); itCon != connList.end(); ++itCon)
  158. {
  159. // 销毁连接池中的连接
  160. this->DestoryConnection(*itCon);
  161. }
  162. curSize = 0;
  163. // 清空连接池中的连接
  164. connList.clear();
  165. pthread_mutex_unlock(&lock);
  166. }
  167. // 销毁数据库连接
  168. void ConnPool::DestoryConnection(Connection* conn)
  169. {
  170. if (conn)
  171. {
  172. try
  173. {
  174. // 关闭连接
  175. conn->close();
  176. }
  177. catch(sql::SQLException& e)
  178. {
  179. perror(e.what());
  180. }
  181. catch(std::exception& e)
  182. {
  183. perror(e.what());
  184. }
  185. // 删除连接
  186. delete conn;
  187. }
  188. }

数据库连接池测试代码(test_dbpool.cpp):

  1. #include "connection_pool.h"
  2. //初始化连接池
  3. ConnPool *connpool = ConnPool::GetInstance();
  4. int main(int argc, char* argv[])
  5. {
  6. Connection *con;
  7. Statement *state;
  8. ResultSet *result;
  9. // 从连接池中获取连接
  10. con = connpool->GetConnection();
  11. for (int i = 0; i < 100; i++)
  12. {
  13. state = con->createStatement();
  14. state->execute("use testdb");
  15. // 查询数据库
  16. result = state->executeQuery("select * from roles");
  17. // 打印数据库查询结果
  18. cout << "================================" << endl;
  19. while (result->next())
  20. {
  21. int nRoleId = result->getInt("role_id");
  22. string strOccupation = result->getString("occupation");
  23. string strCamp = result->getString("camp");
  24. cout << nRoleId << " , " << strOccupation << " , " << strCamp << endl;
  25. }
  26. cout << "i is: " << i << endl;
  27. cout << "================================" << endl;
  28. }
  29. delete result;
  30. delete state;
  31. connpool->ReleaseConnection(con);
  32. return 0;
  33. }

编译上述文件,生成数据库连接池测试程序,如下:

g++ -o test_dbpool test_dbpool.cpp connection_pool.cpp -I /opt/liitdar/mysql-connector-c++-8.0.11-linux-el7-x86-64bit/include/jdbc/ -L /opt/liitdar/mysql-connector-c++-8.0.11-linux-el7-x86-64bit/lib64/ -lmysqlcppconn

执行上面生成的测试程序,命令如下:

./test_dbpool

程序执行(部分)结果如下:

上述结果表明我们编写的数据库连接池正常运行了。

3 总结

这里对数据库连接池的实现代码进行简单地总结。

3.1 存放空闲连接的容器

数据库连接池头文件(connection_pool.h)中定义了一个list容器connList,使用connList存放空闲的连接。

3.2 线程锁

在对connList内的连接进行操作的时候,需要通过加锁来保证程序的安全性,所以头文件中定义了一个线程锁lock,通过使用lock保证同一时间只能有一个线程对容器connList进行操作。

3.3 单例模式

由于连接池类ConnPool要统一管理系统中的所有连接,所以在整个系统中只需要维护一个连接池对象。如果系统中定义了多个连接池对象,那么每一个对象都可以建立maxSize个连接,在这种情况下使用连接池没有任何意义(与不使用连接池效果一样),也破环了通过连接池统一管理系统中连接的初衷,所以数据库连接池需要使用单例模式编写连接池类:单例模式确保一个类只有一个实例,该类自己进行实例化,并且向整个系统提供这个实例。

在头文件“connection_pool.h”中,我们定义了一个静态的连接池对象connPool ,连接池类ConnPool提供一个静态的公共方法GetInstance() ,外部程序通过调用这个方法来获得连接池对象。由于把连接池类ConnPool的构造函数定义为私有的,所以外部的应用程序不能够通过new的方式来实例化连接池类,只能通过GetInstance()方法获得连接池对象。在GetInstance()方法中,需要判断连接池类中定义的连接池对象connPool是否为“NULL”,若为“NULL”则调用私有构造函数实例化connPool;若不为“NULL”,则直接返回connPool,这样就实现了连接池类的单例模式,从而保证了系统运行过程中只建立一个连接池类的实例对象。

3.4 连接池的初始化

在实例化连接池类的对象connPool时,要对连接池做一些初始化操作,即建立一定数量的数据库连接。本文的程序通过“void InitConnection(int iInitialSize)”方法对连接池进行初始化,创建iInitialSize个连接,并且将这些连接放到连接池中的容器connList中,每新建一个连接,当前已建立的连接数curSize就加1。

3.5 从连接池中获取连接

当程序进行访问数据库时,需要从连接池中获取一个连接,本文是通过GetConnection()方法实现的,大致步骤如下:

1. 首先判断容器中是否还有空闲连接,如果有,则取出容器中的第一个连接,并且将该连接从容器中移除。判断上一步中获得的连接是否已经关闭,如果已关闭,则回收该连接的内存空间,并且重新创建一个连接,然后判断新创建的连接是否为空,如果为空,则需要将已经建立连接的数量减1,去掉这个空连接(也可以说是前面已经关闭的连接)。

2. 如果容器中已经没有空闲连接了,则要判断当前的curSize值是否已经达到规定的最大连接数maxSize:

  • 如果小于maxSize,就建立一个新的连接,同时“++curSize”;
  • 如果不小于maxSize ,则等待其他数据库访问请求释放数据库连接。

3.6 将连接放回到连接池中

连接使用完后,需要将该连接放回连接池中,本文通过“void ReleaseConnection(Connection *conn)”方法实现。该方法的具体实现就是将传进来的数据库连接(重新)添加到连接池的容器connList中。

3.7 销毁连接池

当程序需要销毁数据库连接池(回收连接池的内存空间)时,需要先关闭、销毁连接池中的连接(回收连接池中所有连接的内存空间),然后再释放连接池对象的内存空间(即容器的clear操作)。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号