mysql连接池的实现

概要:本文介绍mysql连接池的实现,要求读者了解线程池

一、为什么需要mysql连接池?

资源复用 :不使用连接池,每次数据库请求都新建一条连接,将耗费系 统资源。 流程如下:

  1. 通过三次握手建立 TCP 连接
  2. MySQL 认证
  3. SQL 执行
  4. 通过四次挥手断开 TCP 连接

更快的系统响应速度

1.一次连接建立和销毁,可复用同一条连接多次执行 SQL 语句。

2.统一的连接管理,避免数据库连接泄露

二、mysql连接池运行原理

在这里插入图片描述

在这里插入图片描述

三、代码实现

1.结构体定义
typedef struct task_t {

    struct task_t *next; // 指向下一个任务节点

    int clientfd; // 客户端fd

    char SQL[MAX_SQL_LENGTH]; // SQL语句缓冲区

} task_t;

typedef struct task_queue_t { // task队列

    task_t *head; // 指向队列的第一个task节点
    task_t *tail; // 指向队列的最后一个task节点
    int block; // 阻塞标志
    pthread_spinlock_t lock; // 自旋锁变量
    pthread_mutex_t mutex; // 互斥锁变量
    pthread_cond_t cond; // 条件变量

} task_queue_t;

typedef struct argc {
    MYSQL *mysql;
    task_queue_t *queue;
} argc;
2.资源创建

a.任务队列

task_queue_t *task_queue_create() { // 创建一个任务队列

    int ret;
    task_queue_t *queue = (task_queue_t *)malloc(sizeof(task_queue_t));
    if (queue) {
        ret = pthread_mutex_init(&queue->mutex, NULL);
        if (ret == 0) {
            ret = pthread_cond_init(&queue->cond, NULL);
            if (ret == 0) {
                pthread_spin_init(&queue->lock, 0);
                queue->head = NULL;
                queue->tail = NULL;
                queue->block = 1;

                return queue;
            }
        }
        free(queue);
    }
    return NULL;

}

b.mysql连接句柄

void mysql_conn_init(MYSQL* mysql) {
    
    mysql_init(mysql); // 初始化mysql句柄

    // 连接到MySQL数据库
    mysql_real_connect(mysql, MYSQL_SERVER_IP, MYSQL_SERVER_USERNAME, MYSQL_SERVER_PASSWORD, 
    MYSQL_SERVER_DEFAULT_DB, MYSQL_SERVER_PORT, NULL, 0);

}
2.sql任务的添加、执行

a.push、pop

void add_task(task_queue_t *queue, task_t *task) { // 向任务队列中添加一个task

    pthread_spin_lock(&queue->lock);
    if (!queue->tail) {
        queue->tail->next = task;
        queue->tail = task;
    }
    else {
        queue->head = task;
        queue->tail = task;
    }
    pthread_spin_unlock(&queue->lock);
    pthread_cond_signal(&queue->cond);
}

void *pop_task(task_queue_t *queue) { // 从任务队列中取出一个任务

    pthread_spin_lock(&queue->lock);
    if (queue->head == NULL) {
        pthread_spin_unlock(&queue->lock);
        return NULL;
    }
    // 取出队列中第一个任务
    task_t *task;
    task = queue->head;
    queue->head = task->next;
    
    //判断队列是否为空
    if (queue->head == NULL) {
        queue->tail = queue->head;
    }

    pthread_spin_unlock(&queue->lock);

    return task;
}

task_t *get_task(task_queue_t *queue) { // 原子地从队列中取出一个任务

    task_t *task;

    while ((task = pop_task(queue)) == NULL) {

        pthread_mutex_lock(&queue->mutex);
        if (queue->block == 0) {
            pthread_mutex_unlock(&queue->mutex);
            return NULL;
        }

        pthread_cond_wait(&queue->cond, &queue->mutex);

        pthread_mutex_unlock(&queue->mutex);
    }
    return task;
}

b.执行任务并将mysql服务器的回复信息转发给客户端

void *mysql_conn_thrd_worker(void *argc) {
    
    task_t *task;

    struct argc *arg = (struct argc*)argc;
    task_queue_t *queue = arg->queue;
    MYSQL *mysql = arg->mysql;

    while (!destroy_pool) {
        task = get_task(queue);
        if(!task) break;
        // 执行其中的SQL语句
        mysql_real_query(mysql, task->SQL, strlen(task->SQL)); // 注入sql语句
        MYSQL_RES *res = mysql_store_result(mysql); // 存储mysql返回信息

        char response[64];
        // 将mysql回复结果cpoy进response
        if (res) {
            MYSQL_ROW row;
            row = mysql_fetch_row(res);
            if (row) {
                snprintf(response, sizeof(response), "%s", row[0]); // 假设结果为字符串类型,仅复制第一列数据
            } else {
                snprintf(response, sizeof(response), "No result found");
            }
            mysql_free_result(res); // 释放结果集
        } else {
            snprintf(response, sizeof(response), "Error retrieving result");
        }
        // 发送回复信息
        send(task->clientfd, response, 64, 0);

        // 销毁任务
        free(task);
    }
}

3.主线程接收客户端连接、sql请求

int tcp_server(task_queue_t *queue) {

    //  初始化服务器套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("create sockfd fail\n");
        return -1;
    }
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));
    
    addr.sin_family = AF_INET;
    addr.sin_port = htons(2024);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (-1 == bind(sockfd, (struct sockaddr*)&addr, sizeof(addr))) {

        perror("bind fail\n");
        return -2;
    }
    
    if (-1 == listen(sockfd, 5)) {
        perror("listen fail\n");
        return -3;
    }
   
    //IO多路复用

    int epfd = epoll_create(1);
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    
    epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);

    struct epoll_event events[1024] = {0}; 

    while (1) {

        int ret = epoll_wait(epfd, events, 1024, -1);
        
        if (ret == -1) {
            perror("epoll_wait fail");
            break;
        }
        
        int i = 0;
        for (i = 0; i < ret; i++) {

            if (sockfd == events[i].data.fd) { 
                struct sockaddr_in clientaddr;
                socklen_t len = sizeof(clientaddr);

                int clientfd = accept(sockfd, (struct sockaddr*)&clientaddr, &len);
                fcntl(clientfd, F_SETFL, SOCK_NONBLOCK);
            
                ev.events = EPOLLIN;
                ev.data.fd = clientfd;
                
                epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);

            } else if (events[i].events & EPOLLIN){

                while (1) {
                    char buffer[256] = {0};
                    int count = recv(events[i].data.fd, buffer, 10, 0);

                    if (count < 0) {//读取完毕或当前没有数据可读或者出错

                        if( (errno == EAGAIN) || (errno == EWOULDBLOCK)) {//读取完毕
                        printf("recv finished\n");
                        break;
                        }
                        //recv出错
                        close(events[i].data.fd);//关闭事件的套接字
                        break;
                    } 
                    else if (count == 0) {//对方发送fin断开连接

                        epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);//移除该事件
                        close(events[i].data.fd);//关闭事件的套接字
                        break;
                    }
                    else {//接收到数据

                        task_t *task;  // 将clientfd和buffer包装进task

                        init_task(task); // 初始化task的next指针

                        strcpy(task->SQL, buffer); // 装入sql请求

                        task->clientfd = events[i].data.fd; 
                        
                        add_task(queue, task); // 将此task添加到任务队列
                    } 

                }

            }

        }

    }

    close(sockfd);
    return 0;
}

4.main函数

int main() {
    // 工作队列
    task_queue_t *queue = task_queue_create();
    if (!queue) exit(1);
    
    // 创建MySQL连接
    MYSQL mysqls[NUM_MYSQL_CONNECTION] = {0};
    int i;
    for (i = 0; i < NUM_MYSQL_CONNECTION; i++) {
        mysql_conn_init(&mysqls[i]);
    }
    
    // 创建工作线程
    pthread_t threadid[NUM_MYSQL_CONNECTION];
    for (i = 0; i < NUM_MYSQL_CONNECTION; i++) {
        struct argc *argc = (struct argc *)malloc(sizeof(struct argc));
        argc->mysql = &mysqls[i];
        argc->queue = queue;
        pthread_create(&threadid[i], NULL, mysql_conn_thrd_worker, argc);
        free(argc);
    }

    tcp_server(queue); // Tcp 服务器,接收客户端连接,包装SQL请求信息并添加到工作队列

    return 0;
}

推荐学习 https://xxetb.xetslk.com/s/p5Ibb

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/657213.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

海博思创储能系统产品再获认可,获得杰出项目类别入围资格

近日&#xff0c;2024年欧洲智慧能源展览会大奖&#xff08;The smarter E AWARD 2024&#xff09;公布了入围名单&#xff0c;该奖项设有五个类别&#xff1a;光伏、储能、电动出行、智能综合能源和杰出项目奖&#xff0c;旨在表彰能源领域中的卓越创新成果。 在入围项目中&a…

linux线程,线程控制与线程相关概念

线程概念 线程这个词或多或少大家都听过&#xff0c;今天我们正式的来谈一下线程&#xff1b; 在我一开始的概念中线程就是进程的一部分&#xff0c;一个进程中有很多个线程&#xff0c;这个想法基本是正确的&#xff0c;但细节部分呢我们需要细细讲解一下&#xff1b; 什么…

Web渗透-MySql-Sql注入:联合查询注入

SQL注入&#xff08;SQL Injection&#xff09;是一种网络攻击技术&#xff0c;攻击者通过将恶意的SQL代码插入到应用程序的输入字段&#xff0c;从而欺骗应用程序执行未经授权的操作。这种攻击方式可以导致严重的安全问题&#xff0c;包括&#xff1a; 数据泄露&#xff1a;攻…

HCIP-Datacom-ARST自选题库__BGP多选【22道题】

1.BGP认证可以防止非法路由器与BGP路由器建立邻居&#xff0c;BGP认证可以分为MD5认证和Keychain认证&#xff0c;请问以下哪些BGP报文会携带BCGP Keychain认证信息?(报头携带) open Update Notication Keepalive 2.传统的BGP-4只能管理IPv4单播路由信息&#xff0c;MP-B…

数据库(10)——图形化界面工具DataGrip

以后关于数据库的图片演示就使用DataGrip了 : ) 创建数据库和表 在连接上数据库之后&#xff0c;可以选择Schema创建一个新的数据库。 点击OK后&#xff0c;就已经创建了一个空的表。 要在数据库中建立一张新的表&#xff0c;右键数据库&#xff0c;点击new table 要给新表添…

【软考】下篇 第19章 大数据架构设计理论与实践

目录 大数据处理系统架构特征Lambda架构Lambda架构介绍Lambda架构实现Lambda架构优缺点Lambda架构与其他架构模式对比 Kappa架构Kappa架构介绍Kappa架构实现Kappa架构优缺点 常见Kappa架构变形&#xff08;Kappa、混合分析系统&#xff09;Kappa架构混合分析系统的Kappa架构 La…

数组长度属性的安排与深度学习中的数据类型探索

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、数组长度属性的理解与应用 1. 数组形状信息的获取 2. 数组形状的变换 3. 数组类型的指…

关于本人VIP付费文章说明

郑重声明&#xff1a;我写博客只是为了记录分享经验 自从上次写完数据结构系列后我就一直没有登陆&#xff0c;目前也没打算继续开新内容。今天偶然发现我之前写的文章被设为vip文章&#xff0c;要vip解锁才能看&#xff0c;我很确定当初我发布的时候选择的是公开&#xff0c;…

SpirngMVC框架学习笔记(一):SpringMVC基本介绍

1 SpringMVC 特点&概述 SpringMVC 从易用性&#xff0c;效率上 比曾经流行的 Struts2 更好 SpringMVC 是 WEB 层框架&#xff0c;接管了 Web 层组件, 比如控制器, 视图, 视图解析, 返回给用户的数据格式, 同时支持 MVC 的开发模式/开发架构SpringMVC 通过注解&#xff0c;…

【kubernetes】关于k8s集群的污点、容忍、驱逐以及k8s集群故障排查思路

目录 一、污点(Taint) 1.1污点介绍 1.2污点的组成格式 1.3当前 taint effect 支持如下三个选项&#xff1a; 1.4污点的增删改查 1.4.1验证污点的作用——NoExecute 1.4.2验证污点的作用——NoSchedule 1.4.3 验证污点的作用——PreferNoSchedule 1.5污点的配置与管理…

单链表的相关题目

1.删除链表中给定值val的所有结点 public void removeall(int key) {//由于是删除链表中所有和key值相同的结点,所以可以设置两个ListNode类型的数据,一个在前面,一个在后面.//直到前面的走到链表的最后,这样完成了遍历.//先判断一下这个链表是否为空if(headnull){System.out.…

脑图工具 在学习系统架构中的使用

系统&#xff0c;有人把它比作一个黑盒&#xff0c;有人比作一个树洞。呃&#xff0c;其实二者都隐含的表达了一个意思&#xff0c;盘根错节&#xff0c;一言难尽&#xff0c;欲说还休&#xff0c;说了又像是隔靴搔痒&#xff0c;感觉没说透。 学习&#xff0c;理解和展示一个…

边缘计算网关的用途及其使用方法-天拓四方

在数字化日益深入的今天&#xff0c;边缘计算网关作为一种重要的设备&#xff0c;正在越来越多地被应用于各种场景中。它不仅能够提升数据处理的速度和效率&#xff0c;还能在降低网络延迟的同时确保数据的安全性。本文将详细介绍边缘计算网关的用途及其使用方法&#xff0c;帮…

MFC里的工具栏按钮图标如何使用外部图片?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

Revit——(2)模型的编辑、轴网和标高

目录 一、关闭缩小的隐藏窗口 二、标高&#xff08;可创建平面&#xff0c;其他标高线复制即可&#xff09; 三、轴网 周围的四个圈和三角表示四个里面&#xff0c;可以移动&#xff0c;不要删除 一、关闭缩小的隐藏窗口 二、标高&#xff08;可创建平面&#xff0c;其他标…

[书生·浦语大模型实战营]——在茴香豆 Web 版中创建自己领域的知识问答助手

茴香豆是一个基于LLM的领域知识助手&#xff0c;可以用于解答群聊中的问题。接下来是创建过程。 1.打开茴香豆Web版&#xff0c;创建自己的领域库。 地址&#xff1a;茴香豆Web版 这里类似于注册账号&#xff0c;你输入知识库的名称以及密码&#xff0c;然后它就会创建一个知识…

Collection(一)[集合体系]

说明&#xff1a;Collection代表单列集合&#xff0c;每个元素&#xff08;数据&#xff09;只包含一个值。 Collection集合体系&#xff1a; Collection<E> 接口 (一&#xff09;List<E> 接口 说明&#xff1a;添加的元素是有序、可重复、有索引。 1. ArrayLi…

Ai终点站,全系统商业闭环矩阵打造,帮电商、实体降70%成本,12款Ai联合深度实战

说白了&#xff0c;你之前5个人的团队&#xff0c;当团队人数不变的情况下&#xff0c;借助于ChatGPT和各种软件的结合&#xff0c;赋能电商直播带货&#xff0c;可以让之前一年销售额2.000万变成2.500万或者是3.000万&#xff0c;这就是这套课程的核心作用: 【1】系统课程从1…

SpringCloud之SSO单点登录-基于Gateway和OAuth2的跨系统统一认证和鉴权详解

单点登录&#xff08;SSO&#xff09;是一种身份验证过程&#xff0c;允许用户通过一次登录访问多个系统。本文将深入解析单点登录的原理&#xff0c;并详细介绍如何在Spring Cloud环境中实现单点登录。通过具体的架构图和代码示例&#xff0c;我们将展示SSO的工作机制和优势&a…

Excel单元格格式无法修改的原因与解决方法

Excel单元格格式无法更改可能由多种原因造成。以下是一些可能的原因及相应的解决方法&#xff1a; 单元格或工作表被保护&#xff1a; 如果单元格或工作表被设置为只读或保护状态&#xff0c;您将无法更改其中的格式。解决方法&#xff1a;取消单元格或工作表的保护。在Excel中…