图床项目MySQL连接池代码阅读

37 篇文章 2 订阅

存放结果集合的类

class CResultSet
{
    public:
    CResultSet(MYSQL_RES *res);
    virtual ~CResultSet();

    bool Next();
    int GetInt(const char *key);
    char *GetString(const char *key);

    private:
    int _GetIndex(const char *key); //获取列名对应的索引

    MYSQL_RES *m_res;        // 该结构代表返回行的查询结果(SELECT, SHOW, DESCRIBE, EXPLAIN)
    MYSQL_ROW m_row;        // 这是1行数据的“类型安全”表示。它目前是按照计数字节字符串的数组实施的。
    map<string, int> m_key_map;
};

功能是用于存放MySQL查询结果。

CResultSet::CResultSet

CResultSet::CResultSet(MYSQL_RES *res)
{
    m_res = res;

    // map table field key to index in the result array
    int num_fields = mysql_num_fields(m_res); // 返回结果集中的列数。
    MYSQL_FIELD *fields = mysql_fetch_fields(m_res); // 关于结果集所有列的MYSQL_FIELD结构的数组
    for (int i = 0; i < num_fields; i++)
    {
        // 多列
        m_key_map.insert(make_pair(fields[i].name, i)); // 每个结构提供了结果集中1列的字段定义
        LOG_INFO << num_fields << " - [" << i << "] - " << fields[i].name;
    }
}

这个构造函数是读取出一行的查询结果,然后将每一列存储到map中

析构函数,释放结果集的内存

CResultSet::~CResultSet()
{
    if (m_res)
    {
    	mysql_free_result(m_res);
    	m_res = NULL;
    }
}

mysql_free_result 释放 mysql_store_result(), mysql_use_result(), mysql_list_dbs() 为结果集分配的内存*

获取下一行的结果

bool CResultSet::Next()
{
    m_row = mysql_fetch_row(m_res);        // 检索结果集的下一行,行内值的数目由mysql_num_fields(result)给出
    if (m_row)
    {
    	return true;
    }
    else
    {
    	return false;
    }
}

CPrepareStatement是用于插入数据使用:

class CPrepareStatement
{
    public:
    CPrepareStatement();
    virtual ~CPrepareStatement();

    bool Init(MYSQL *mysql, string &sql);

    void SetParam(uint32_t index, int &value);
    void SetParam(uint32_t index, uint32_t &value);
    void SetParam(uint32_t index, string &value);
    void SetParam(uint32_t index, const string &value);

    bool ExecuteUpdate();
    uint32_t GetInsertId();

    private:
    MYSQL_STMT *m_stmt;
    MYSQL_BIND *m_param_bind;
    uint32_t m_param_cnt;
};

CPrepareStatement析构

CPrepareStatement::~CPrepareStatement()
{
    if (m_stmt)
    {
        mysql_stmt_close(m_stmt);
        m_stmt = NULL;
    }

    if (m_param_bind)
    {
        delete[] m_param_bind;
        m_param_bind = NULL;
    }
}

CPrepareStatement初始化函数

bool CPrepareStatement::Init(MYSQL *mysql, string &sql)
{
    mysql_ping(mysql); // 当mysql连接丢失的时候,使用mysql_ping能够自动重连数据库

    // g_master_conn_fail_num ++;
    m_stmt = mysql_stmt_init(mysql);//创建MYSQL_STMT句柄
    if (!m_stmt)
    {
   		LOG_ERROR << "mysql_stmt_init failed";
    	return false;
    }

    if (mysql_stmt_prepare(m_stmt, sql.c_str(), sql.size()))
    {
    	LOG_ERROR << "mysql_stmt_prepare failed: " << mysql_stmt_error(m_stmt);

   	 	return false;
    }

    m_param_cnt = mysql_stmt_param_count(m_stmt);
    if (m_param_cnt > 0)
    {
        m_param_bind = new MYSQL_BIND[m_param_cnt];
        if (!m_param_bind)
        {
        	LOG_ERROR << "new failed";
        	return false;
        }

        memset(m_param_bind, 0, sizeof(MYSQL_BIND) * m_param_cnt);
    }

    return true;
}

CPrepareStatement::ExecuteUpdate()

执行MySQL 的 Update操作

bool CPrepareStatement::ExecuteUpdate()
{
    if (!m_stmt)
    {
    LOG_ERROR << "no m_stmt";
    return false;
    }
 
if (mysql_stmt_bind_param(m_stmt, m_param_bind))
{
    LOG_ERROR << "mysql_stmt_bind_param failed: " << mysql_stmt_error(m_stmt);
    return false;
}
 
if (mysql_stmt_execute(m_stmt))
{
    LOG_ERROR << "mysql_stmt_execute failed: " << mysql_stmt_error(m_stmt);
    return false;
}
// 返回上次执行语句更改、删除或插入的总行数。对于UPDATE、DELETE或INSERT语句,可在mysql_stmt_execute()之后立刻调用它们。
if (mysql_stmt_affected_rows(m_stmt) == 0)
{
    LOG_ERROR << "ExecuteUpdate have no effect";
    return false;
}
 
	return true;
}

数据库连接类:

class CDBConn
{
    public:
    CDBConn(CDBPool *pDBPool);
    virtual ~CDBConn();
    int Init();

    // 创建表
    bool ExecuteCreate(const char *sql_query);
    // 删除表
    bool ExecuteDrop(const char *sql_query);
    // 查询
    CResultSet *ExecuteQuery(const char *sql_query);

    bool ExecutePassQuery(const char *sql_query);
    /**
     * 执行DB更新,修改
     *
     * @param sql_query   sql
     * @param care_affected_rows 是否在意影响的行数,false:不在意;true:在意
     *
     * @return 成功返回true 失败返回false
     */
    bool ExecuteUpdate(const char *sql_query, bool care_affected_rows = true);
    uint32_t GetInsertId();

    // 开启事务
    bool StartTransaction();
    // 提交事务
    bool Commit();
    // 回滚事务
    bool Rollback();
    // 获取连接池名
    const char *GetPoolName();
    MYSQL *GetMysql() { return m_mysql; }
    int GetRowNum() { return row_num; }

    private:
    int row_num = 0;
    CDBPool *m_pDBPool; // to get MySQL server information
    MYSQL *m_mysql;        // 对应一个连接
    char m_escape_string[MAX_ESCAPE_STRING_LEN + 1];
};

CDBPool类是数据库连接池类

class CDBPool
{ // 只是负责管理连接CDBConn,真正干活的是CDBConn
    public:
    CDBPool() {} // 如果在构造函数做一些可能失败的操作,需要抛出异常,外部要捕获异常
    CDBPool(const char *pool_name, const char *db_server_ip, uint16_t db_server_port,
    const char *username, const char *password, const char *db_name,
    int max_conn_cnt);
    virtual ~CDBPool();

    int Init();                                     // 连接数据库,创建连接
    CDBConn *GetDBConn(const int timeout_ms = 0); // 获取连接资源
    void RelDBConn(CDBConn *pConn);                 // 归还连接资源

    const char *GetPoolName() { return m_pool_name.c_str(); }
    const char *GetDBServerIP() { return m_db_server_ip.c_str(); }
    uint16_t GetDBServerPort() { return m_db_server_port; }
    const char *GetUsername() { return m_username.c_str(); }
    const char *GetPasswrod() { return m_password.c_str(); }
    const char *GetDBName() { return m_db_name.c_str(); }

private:
    string m_pool_name;             // 连接池名称
    string m_db_server_ip;         // 数据库ip
    uint16_t m_db_server_port;     // 数据库端口
    string m_username;             // 用户名
    string m_password;             // 用户密码
    string m_db_name;             // db名称
    int m_db_cur_conn_cnt;         // 当前启用的连接数量
    int m_db_max_conn_cnt;         // 最大连接数量
    list<CDBConn *> m_free_list; // 空闲的连接

    list<CDBConn *> m_used_list; // 记录已经被请求的连接
    std::mutex m_mutex;
    std::condition_variable m_cond_var;
    bool m_abort_request = false;
};

从连接池获取一条MYSQL连接

CDBConn *CDBPool::GetDBConn(const int timeout_ms)
{
    std::unique_lock<std::mutex> lock(m_mutex);
    if (m_abort_request)
    {
        LOG_WARN << "have aboort";
        return NULL;
    }

    if (m_free_list.empty()) // 2 当没有连接可以用时
    {
    	// 第一步先检测 当前连接数量是否达到最大的连接数量
    	if (m_db_cur_conn_cnt >= m_db_max_conn_cnt) // 等待的逻辑
    	{
    	// 如果已经到达了,看看是否需要超时等待
    		if (timeout_ms <= 0) // 死等,直到有连接可以用 或者 连接池要退出
    		{
    			// lambda为true才会退出 while
    			m_cond_var.wait(lock, [this] 
    			{
    			// 当前连接数量小于最大连接数量 或者请求释放连接池时退出
    			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] 
            {
            return (!m_free_list.empty()) | m_abort_request; 
            });
            // 带超时功能时还要判断是否为空
            if (m_free_list.empty()) // 如果连接池还是没有空闲则退出
            {
            	return NULL;
            }
    	}

        if (m_abort_request)
        {
            LOG_WARN << "have abort";
            return NULL;
        }
    }
    else // 还没有到最大连接则创建连接
    {
    	CDBConn *pDBConn = new CDBConn(this); //新建连接
    	int ret = pDBConn->Init();
    	if (ret)
    	{
    		LOG_ERROR << "Init DBConnecton failed";
    		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 吐出连接,从空闲队列删除

    return pConn;
}

归还MySQL连接资源

void CDBPool::RelDBConn(CDBConn *pConn)
{
    std::lock_guard<std::mutex> lock(m_mutex);

    list<CDBConn *>::iterator it = m_free_list.begin();
    for (; it != m_free_list.end(); it++) // 避免重复归还
    {
        if (*it == pConn)
        {
        	break;
        }
    }

    if (it == m_free_list.end()) // 不在m_free_list中就表示要归还
    {
        // m_used_list.remove(pConn);
        m_free_list.push_back(pConn); //
        m_cond_var.notify_one(); // 通知取队列
    }
    else
    {
    	LOG_WARN << "RelDBConn failed";// 不再次回收连接
    }
}

管理数据库连接池的类,可以有多个连接池

class CDBManager
{
public:
    virtual ~CDBManager();

    static CDBManager *getInstance(); // mark1

    int Init();

    CDBConn *GetDBConn(const char *dbpool_name);
    void RelDBConn(CDBConn *pConn);

private:
    CDBManager();

private:
    static CDBManager *s_db_manager; // 对应mark1
    map<string, CDBPool *> m_dbpool_map;
};

有一个主数据连接池和一个从数据连接池

初始化主从数据库的连接池

int CDBManager::Init()
{
    CConfigFileReader config_file("tc_http_server.conf");

    char *db_instances = config_file.GetConfigName("DBInstances");

    if (!db_instances)
    {
    	LOG_ERROR << "not configure DBInstances";
    	return 1;
    }

    char host[64];
    char port[64];
    char dbname[64];
    char username[64];
    char password[64];
    char maxconncnt[64];
    CStrExplode instances_name(db_instances, ',');

    for (uint32_t i = 0; i < instances_name.GetItemCnt(); i++)
    {
        char *pool_name = instances_name.GetItem(i);
        snprintf(host, 64, "%s_host", pool_name);
        snprintf(port, 64, "%s_port", pool_name);
        snprintf(dbname, 64, "%s_dbname", pool_name);
        snprintf(username, 64, "%s_username", pool_name);
        snprintf(password, 64, "%s_password", pool_name);
        snprintf(maxconncnt, 64, "%s_maxconncnt", pool_name);

        char *db_host = config_file.GetConfigName(host);
        char *str_db_port = config_file.GetConfigName(port);
        char *db_dbname = config_file.GetConfigName(dbname);
        char *db_username = config_file.GetConfigName(username);
        char *db_password = config_file.GetConfigName(password);
        char *str_maxconncnt = config_file.GetConfigName(maxconncnt);

    	LOG_INFO << "db_host:" << db_host << ", db_port:" << str_db_port << ", db_dbname:" << db_dbname
     << ", db_username:" << db_username << ", db_password:" << db_password;

    	if (!db_host || !str_db_port || !db_dbname || !db_username || !db_password || !str_maxconncnt)
    	{
            LOG_FATAL << "not configure db instance: " << pool_name;
            return 2;
    	}

        int db_port = atoi(str_db_port);
        int db_maxconncnt = atoi(str_maxconncnt);
        CDBPool *pDBPool = new CDBPool(pool_name, db_host, db_port, db_username, db_password, db_dbname, db_maxconncnt);
        if (pDBPool->Init())
        {
            LOG_ERROR << "init db instance failed: " << pool_name;
            return 3;
        }
        m_dbpool_map.insert(make_pair(pool_name, pDBPool));
    }

    return 0;
}

获取指定MySQL连接池中的一条连接

CDBConn *CDBManager::GetDBConn(const char *dbpool_name)
{
	// 先找到对应连接池,在获取其中的一条连接
    map<string, CDBPool *>::iterator it = m_dbpool_map.find(dbpool_name); // 主从
    if (it == m_dbpool_map.end())
    {
    	return NULL;
    }
    else
    {
    	return it->second->GetDBConn();
    }
}

自动释放数据库连接类

class AutoRelDBCon
{
public:
    AutoRelDBCon(CDBManager *manger, CDBConn *conn) : manger_(manger), conn_(conn) {}
    ~AutoRelDBCon() // 对象声明周期时自动调用该析构函数
    {
    	if (manger_)
        {
            manger_->RelDBConn(conn_);
        }
    } //在析构函数规划
private:
    CDBManager *manger_ = NULL;
    CDBConn *conn_ = NULL;
};
 
// 创建数据库连接对象,使得对象销毁时能够自动析构
#define AUTO_REAL_DBCONN(m, c) AutoRelDBCon autoreldbconn(m, c)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值