linux基礎——linux下數據庫連接池的原理及編程實現

一、數據庫連接池簡介
對於一個簡單的數據庫應用,如果對數據庫的訪問不是很頻繁,通常的做法是創建一個連接,用完就關閉,這樣做不會有明顯的性能上的開銷,但是對於一個副複雜的數據應用,建立一個數據庫連接需要消耗大量系統資源,頻繁的創建數據庫連接會大大的削弱應用性能,因此考慮到性能的問題,並不是每個用戶都創建並獨佔一個數據庫連接,數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重複使用一個現有的數據庫連接,而不是重新建立一個,使用完不會銷燬而是放回連接池。這項技術能明顯提高對數據庫操作的性能。

二、linux下的mysql函數
1.初始化一個連接句柄
  1. MYSQL *mysql_init(MYSQL *mysql);//mysql是NULL指針,該函數將分配、初始化、並返回新對象。否則,將初始化對象,並返回對象的地址。  
2.建立物理連接
  1. MYSQL *mysql_real_connect(MYSQL *connection,const char *server_host,const char *sql_user_name,const char *sql_password,const char *db_name,unsigned int port_number,const char *unix_socket_name,unsigned int flags);//connection參數爲通過mysql_init函數創建的句柄,port_number和*unix_socket_name的值分別爲0和NULL即可。如果連接成功,返回MYSQL*連接句柄(和第一個參數相同)。如果連接失敗,返回NULL。  
3.斷開連接
  1. void mysql_close(MYSQL *connection);//斷開指定的鏈接。  
4.選項設置
  1. int mysql_options(MYSQL *mysql, enum mysql_option option, const void *arg);//可用於設置額外的連接選項,並影響連接的行爲。可多次調用該函數來設置數個選項。應在mysql_init()之後、以及mysql_connect()或mysql_real_connect()之前調用mysql_options()。  
  2. 主要選項:  
  3. MYSQL_OPT_RECONNECT //顯式方式設置再連接行爲的方法。  
  4. MYSQL_OPT_CONNECT_TIMEOUT //以秒爲單位的連接超時。  
5. 錯誤處理
  1. unsigned int mysql_errno(MYSQL *connection);  
  2. eg.if (mysql_errno(&my_connection))  
  3. {  
  4.     fprintf(stderr, “Connection error %d: %s/n”, mysql_errno(&my_connection),mysql_error(&my_connection));  
  5. }  
6.執行SQL命令
  1. int mysql_query(MYSQL *connection, const char *query);//該函數用來執行SQL命令,如果執行成功則返回0。  
7.數據控制
  1. MYSQL_RES *mysql_store_result(MYSQL *connection);//該函數成功時返回一個指向返回集的指針,否則返回NULL。  
  2. my_ulonglong mysql_num_rows(MYSQL_RES *result);//該函數獲取返回集中的行數,若爲0表示沒有返回行。  
  3. MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);//該函數從mysql_store_result返回集中取出一行,當沒有數據或者出差時返回NULL  
  4. my_ulonglong mysql_num_rows(MYSQL_RES *result);//該函數獲取返回集中的行數,若爲0表示沒有返回行。  
8. 數據處理
  1. unsigned int mysql_field_count(MYSQL *connection);//該函數獲取返回集中數據的列數。此外,我們還可以將該函數用於其它情況,比如說確定mysql_store_result函數失敗的原因;如果mysql_store_result返回NULL,而mysql_filed_count返回了一個大於0的數,說明是檢索錯誤,但如果mysql_field_count返回0,則表明爲存儲錯誤。  
  2. MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);//該函數可獲得與列字段相關信息的結構指針。  

三、實現
以下實現了一個簡單的數據庫連接池
  1. #include   
  2. #include   
  3. #include   
  4. #include   
  5. #include   
  6.   
  7. #define IP_LEN 15  
  8. #define PORT_LEN 8  
  9. #define DBNAME_LEN 64  
  10. #define DBUSER_LEN 64  
  11. #define PASSWD_LEN 64  
  12. #define POOL_NUMBER 5  
  13. #define LOG_STR_BUF_LEN 64  
  14.   
  15. //連接節點  
  16. typedef struct _sql_node  
  17. {  
  18.         MYSQL fd;//mysql對象文件描述符  
  19.         MYSQL *mysql_sock;//連接句柄  
  20.         pthread_mutex_t mutex;//互斥鎖  
  21.         char used;//連接使用標誌  
  22.         int index;//下標  
  23.         enum{DB_DISCONN, DB_CONN} sql_state;//當前節點與數據庫的連接狀態  
  24. }sql_node;  
  25.   
  26. //數據庫連接池  
  27. typedef struct _sql_conn_pool  
  28. {  
  29.         sql_node sql_pool[POOL_NUMBER];//連接池中的連接  
  30.         char ip[IP_LEN + 1];//ip  
  31.         int port;//端口 mysql端口默認3306  
  32.         char db_name[DBNAME_LEN + 1];//數據庫名稱  
  33.         char user[DBUSER_LEN + 1];//用戶名  
  34.         char passwd[PASSWD_LEN + 1];//密碼  
  35.         int pool_number;//池中連接數量  
  36. }sql_conn_pool;  
  37.   
  38. //創建數據庫連接池  
  39. sql_conn_pool * sql_pool_create(int connect_pool_number, char ip[], int port, char name[], char user[], char passwd[])  
  40. {  
  41.         sql_conn_pool *sp = NULL;  
  42.   
  43.         if (!connect_pool_number || connect_pool_number < 1)  
  44.         {  
  45.                 connect_pool_number = 1;  
  46.         }  
  47.   
  48.         sp = (sql_conn_pool *)malloc(sizeof(sql_conn_pool));  
  49.   
  50.         if (sp == NULL)  
  51.         {  
  52.                 printf("malloc for sql connect pool error!\n");  
  53.                 return NULL;  
  54.         }  
  55.   
  56.         strcpy(sp->ip, ip);  
  57.         sp->port = port;  
  58.         strcpy(sp->db_name, name);  
  59.         strcpy(sp->user, user);  
  60.         strcpy(sp->passwd, passwd);  
  61.   
  62.         //創建連接  
  63.         for (int index = 0; index < connect_pool_number; index++)  
  64.         {  
  65.                 //創建失敗  
  66.                 if (-1 == create_connect(sp, &(sp->sql_pool[index])))  
  67.                 {  
  68.                         sql_pool_destroy(sp);  
  69.                         return NULL;  
  70.                 }  
  71.   
  72.                 printf("create database pool success\n");  
  73.                 sp->sql_pool[index].index = index;  
  74.                 sp->pool_number++;  
  75.         }  
  76.   
  77.         return sp;  
  78. }  
  79.   
  80. //創建連接  
  81. static int create_connect(sql_conn_pool *sp, sql_node *node)  
  82. {  
  83.         int ret = 0;  
  84.         int opt = 1;  
  85.   
  86.         //初始化對象並返回對象地址  
  87.         if (NULL == mysql_init(&node->fd))  
  88.         {  
  89.                 printf("mysql init error\n");  
  90.                 ret = -1;  
  91.         }  
  92.         else  
  93.         {  
  94.                 //mysql_real_connect非線程安全,用前需要加鎖  
  95.                 pthread_mutex_init(&node->mutex, NULL);  
  96.   
  97.                 if (!(node->mysql_sock = mysql_real_connect(&node->fd, sp->ip, sp->user, sp->passwd, sp->db_name, sp->port, NULL, 0)))  
  98.                 {  
  99.                         printf("couldn't connect to engine! %s \n", mysql_error(&node->fd));  
  100.                         node->sql_state = DB_DISCONN;  
  101.                         ret = 1;  
  102.                 }  
  103.                 else  
  104.                 {  
  105.                         node->used = 0;  
  106.                         node->sql_state = DB_CONN;  
  107.                         mysql_options(&node->fd, MYSQL_OPT_RECONNECT, &opt);  
  108.                         opt = 3;//3s  
  109.                         mysql_options(&node->fd, MYSQL_OPT_CONNECT_TIMEOUT, &opt);  
  110.                         ret = 0;                          
  111.                 }  
  112.   
  113.         }  
  114.   
  115.         return ret;  
  116. }  
  117.   
  118. //銷燬數據庫連接池  
  119. void sql_pool_destroy(sql_conn_pool *sp)  
  120. {  
  121.         for (int index = 0; index < sp->pool_number; index++)  
  122.         {  
  123.                 if (NULL != sp->sql_pool[index].mysql_sock)  
  124.                 {  
  125.                         mysql_close(sp->sql_pool[index].mysql_sock);  
  126.                         sp->sql_pool[index].mysql_sock = NULL;  
  127.                 }  
  128.                 sp->sql_pool[index].sql_state = DB_DISCONN;  
  129.         }  
  130. }  
  131.   
  132. //從數據庫連接池中獲取一個未使用的連接  
  133. sql_node *get_db_connect_from_pool(sql_conn_pool *sp)  
  134. {  
  135.         int start_index = 0, index = 0, loop_index;  
  136.         int ping_ret;  
  137.   
  138.         srand((int)time(0));  
  139.         start_index = rand() % sp->pool_number;  
  140.   
  141.         for (loop_index = 0; loop_index < sp->pool_number; loop_index++)  
  142.         {  
  143.                 index = (start_index + loop_index) % sp->pool_number;  
  144.   
  145.                 if (!pthread_mutex_trylock(&sp->sql_pool[index].mutex))  
  146.                 {  
  147.                         if (DB_DISCONN == sp->sql_pool[index].sql_state)  
  148.                         {  
  149.                                 //重新連接  
  150.                                 if (0 != create_connect(sp, &(sp->sql_pool[index])))  
  151.                                 {  
  152.                                         //重新連接失敗  
  153.                                         release_sock_to_sql_pool(&(sp->sql_pool[index]));  
  154.                                         continue;  
  155.                                 }  
  156.                         }  
  157.                 }  
  158.   
  159.                 ping_ret = mysql_ping(sp->sql_pool[index].mysql_sock);  
  160.   
  161.                 if (0 != ping_ret)  
  162.                 {  
  163.                         printf("mysql ping error!\n");  
  164.                         sp->sql_pool[index].sql_state = DB_DISCONN;  
  165.                         release_sock_to_sql_pool(&(sp->sql_pool[index]));  
  166.                 }  
  167.                 else  
  168.                 {  
  169.                         sp->sql_pool[index].used = 1;  
  170.                         break;  
  171.                 }  
  172.         }  
  173.   
  174.         if (loop_index == sp->pool_number)  
  175.         {  
  176.                 return NULL;  
  177.         }  
  178.         else  
  179.         {  
  180.                 return &(sp->sql_pool[index]);  
  181.         }  
  182. }  
  183.   
  184. //將使用完的連接還給連接池  
  185. void release_sock_to_sql_pool(sql_node *node)  
  186. {  
  187.         node->used = 0;  
  188.         pthread_mutex_unlock(&node->mutex);  
  189. }  
  190.   
  191. int main()  
  192. {  
  193.         MYSQL_FIELD *fd;  
  194.         sql_conn_pool *sp = sql_pool_create(POOL_NUMBER, "192.168.238.132", 3306, "yexin""root""1234567890");  
  195.         sql_node *node = get_db_connect_from_pool(sp);  
  196.   
  197.         if (NULL == node)  
  198.         {  
  199.                 printf("get sql pool node error.\n");  
  200.                 return -1;  
  201.         }  
  202.   
  203.         if (mysql_query(&(node->fd), "delete from student where sno = '0174'"))  
  204.         {  
  205.                 printf("query error.\n");  
  206.                 return -1;  
  207.         }  
  208.         else  
  209.         {  
  210.                 printf("delete succeed!\n");  
  211.         }  
  212.   
  213.         sql_pool_destroy(sp);  
  214.   
  215.         return 0;  
  216. }  
運行結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章