首发于 c/c++linux

mysql连接池的实现

前言

本文介绍mysql连接池的实现。我记得go提供的原生sql包里面就是用的连接池,重在理解连接池的概念即可。

池化技术

池化技术能够减少资源对象的创建次数,提高程序的响应性能,特别是在高并发的场景下这种提升会更加明显,所以池化技术相当于一层缓冲。使用池化技术缓存的资源对象有如下共同特点:

  1. 对象创建时间长
  2. 对象创建需要大量资源;
  3. 对象创建后可被重复使用

像 线程池,内存池,请求池,对象池,连接池 都具备以上的共同特点。

数据库连接池

什么是数据库连接池

数据库连接池是程序启动时建立足够数量的数据库连接,并将这些连接统一管理起来组成一个连接池,程序动态的从池中取连接与归还连接。

创建数据库连接相对来说是比较耗时的,不仅有三次握手,还有mysql的三次认证过程。所以在程序启动时就创建多个连接统一管理,可以提高程序的响应性能。

为什么使用数据库连接池

  1. 资源复用:由于数据库的连接得到的复用,避免频繁的创建和销毁连接的性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
  2. 更快的系统响应速度:数据库连接池在初始化后,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了从数据库连接初始化和释放过程的开销,从而缩减了系统整体响应时间。
  3. 统一的连接管理,避免数据库连接泄露:在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄露。

不使用连接池的执行过程

可以看到,为了执行一条sql,要经历五个步骤:

    在这里插入图片描述


    不使用连接池的优点是 代码实现起来比较简单,但是不使用连接池的缺点有很多:

    使用连接池的执行过程

    在连接池初始化的时候,会按照需求建立N个连接,但是之后的访问,都会从池里面取,复用初始化时创建的连接,直接执行sql语句。


    在这里插入图片描述


    使用连接池的优点:

    但是缺点也很明显,就是代码实现较为复杂。

    数据库连接池运行机制

    1. 连接池初始化时创建多条连接
    2. 从连接池获取可用连接
    3. 使用完毕后,将连接归还置连接池
    4. 在程序结束前,断开所有连接,释放连接资源


    在这里插入图片描述


    连接池和线程池的关系

    连接池和线程池的区别: - 线程池:主动去调用任务,当任务队列不为空的时候从任务队列中取任务(比如去银行办理业务,窗口柜员是线程,多个窗口组成了线程池,柜员从排号队列叫号执行。) - 连接池:被动被任务使用,当某任务需要操作数据库时,只要从连接池中取出一个连接对象,当任务使用完该连接对象后,将该连接对象放回到连接池中。如果连接池中没有连接对象可以用,那么该任务就必须等待。(比如去银行用笔填单,笔是连接对象,我们要用笔的时候去取,用完了还回去。)

    连接池和线程池设置数量的关系:

    线程池设计与代码实现

    线程池设计要点

    使用连接池需要预先建立数据库连接。线程池设计思路:

    构造函数


    在这里插入图片描述


    初始化


    在这里插入图片描述


    获取连接池内的连接


    在这里插入图片描述


    CDBConn *pDBConn = pDBPool->GetDBConn(); // 获取连接
    
    CDBConn *CDBPool::GetDBConn(const int timeout_ms) {
        std::unique_lock<std::mutex> lock(m_mutex);
        if (m_abort_request) {
            log_warn("have aboort\n");
            return NULL;
        }
    
        if (m_free_list.empty()) {       // 当没有连接可以用时
            // 第一步先检测 当前连接数量是否达到最大的连接数量
            if (m_db_cur_conn_cnt >= m_db_max_conn_cnt) {
                // 如果已经到达了,看看是否需要超时等待
                if (timeout_ms <= 0)        // 死等,直到有连接可以用 或者 连接池要退出
                {
                    log_info("wait ms:%d\n", timeout_ms);
                    m_cond_var.wait(lock, [this] {
                        // log_info("wait:%d, size:%d\n", wait_cout++, m_free_list.size());
                        // 当前连接数量小于最大连接数量 或者请求释放连接池时退出
                        return (!m_free_list.empty()) | m_abort_request;
                    });
                }
                else {
                    // return如果返回 false,继续wait(或者超时),  如果返回true退出wait
                    // 1.m_free_list不为空
                    // 2.超时退出
                    // 3. m_abort_request被置为true,要释放整个连接池
                    m_cond_var.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this] {
                        // log_info("wait_for:%d, size:%d\n", wait_cout++, m_free_list.size());
                        return (!m_free_list.empty()) | m_abort_request;
                    });
                    // 带超时功能时还要判断是否为空
                    if (m_free_list.empty())    // 如果连接池还是没有空闲则退出
                    {
                        return NULL;
                    }
                }
    
                if (m_abort_request) {
                    log_warn("have aboort\n");
                    return NULL;
                }
            }
            else // 还没有到最大连接则创建连接
            {
                CDBConn *pDBConn = new CDBConn(this);    //新建连接
                int ret = pDBConn->Init();
                if (ret) {
                    log_error("Init DBConnecton failed\n\n");
                    delete pDBConn;
                    return NULL;
                }
                else {
                    m_free_list.push_back(pDBConn);
                    m_db_cur_conn_cnt++;
                    // log_info("new db connection: %s, conn_cnt: %d\n", m_pool_name.c_str(), m_db_cur_conn_cnt);
                }
            }
        }
    
        CDBConn *pConn = m_free_list.front();    // 获取连接
        m_free_list.pop_front();    // STL 吐出连接,从空闲队列删除
        m_used_list.push_back(pConn);        //
        return pConn;
    }
    

    归还连接至连接池


    在这里插入图片描述


    析构函数


    在这里插入图片描述


    连接池名的作用

    给每个池分配不同的名称,那么就可以用多个连接池实现不同的业务。

    在这里插入图片描述


    mysql连接重连机制

    设置启用(当发现连接断开时的)自动重连

    检测连接是否正常


    在这里插入图片描述

    深圳SEO优化公司自贡模板制作报价那曲关键词排名价格巴中网站定制报价池州网站优化按天扣费多少钱天门seo哪家好乐山企业网站设计多少钱木棉湾英文网站建设报价桐城至尊标王推荐木棉湾关键词按天计费价格淮北如何制作网站推荐四平网站定制报价眉山外贸网站制作哪家好长春至尊标王报价温州建网站推荐南充设计网站价格阳江网站定制公司南山模板制作多少钱信阳企业网站制作报价呼和浩特百度爱采购自贡网站定制报价汕尾关键词排名包年推广塘坑网站优化排名推荐重庆网站优化按天收费公司鹤岗营销网站哪家好通化百度网站优化哪家好临夏SEO按天扣费推荐迪庆关键词按天收费价格大芬网站推广系统哪家好临猗百姓网标王推广哪家好西乡网站排名优化哪家好歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

    深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化