yii2随笔(五):mysql 防注入 实现 mysql_real_escape_string 功能

原文地址:http://ivhong.com/?p=106(ivhong.com 是我的博客主址)


Yii::$app->db->quoteValue() 等价 PDO::quote  
<think> 我们讨论的是MySQL C++连接器中断线重连的实现方法。 用户提到的函数`sql::mysql::get_mysql_driver_instance()`是MySQL Connector/C++中的API,用于获取MySQL驱动实例。 在长连接应用中,数据库连接可能由于不活动而超时断开(如MySQL的wait_timeout和interactive_timeout设置)。 我们需要实现断线重连机制,即当执行查询时发现连接已经断开,则自动重新连接并重新执行查询。 根据引用[1]中描述的断线重连原理(检测到特定错误码2006或2013时重新连接并重试),我们可以将其应用于Connector/C++。 然而,Connector/C++本身并没有内置的自动重连机制(引用[2]中Go语言的驱动也是类似,需要自己处理)。因此,我们需要在应用层实现实现步骤: 1. 在执行SQL语句之前,检查连接是否有效(如果可能的话)。 2. 如果执行SQL语句失败,检查错误码是否为CR_SERVER_GONE_AWAY(2006)或CR_SERVER_LOST(2013)。 3. 如果是上述错误,则尝试重新连接(注意:重新连接可能需要重新创建连接对象)。 4. 重连成功后,重新执行之前的SQL语句。 但是,Connector/C++的连接对象(Connection)没有提供直接的“重连”方法。通常的做法是: - 在检测到连接断开时,释放当前连接对象。 - 然后重新创建一个新的连接对象。 因此,我们需要封装一个具有重连功能的数据库操作函数。 示例代码框架: ```cpp #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/statement.h> #include <cppconn/exception.h> // 封装一个执行SQL的函数,具有重连机制 void executeWithReconnect(sql::Connection *&conn, const std::string &sql) { sql::mysql::MySQL_Driver *driver = sql::mysql::get_mysql_driver_instance(); try { sql::Statement *stmt = conn->createStatement(); stmt->execute(sql); delete stmt; } catch (sql::SQLException &e) { // 检查错误码:CR_SERVER_GONE_AWAY (2006), CR_SERVER_LOST (2013) if (e.getErrorCode() == 2006 || e.getErrorCode() == 2013) { // 断开连接,然后重新连接 delete conn; // 释放旧的连接 // 重新建立连接(需要连接信息,这里假设我们保存了连接信息) conn = driver->connect("tcp://127.0.0.1:3306", "user", "password"); conn->setSchema("database"); // 重连后重新执行 sql::Statement *stmt = conn->createStatement(); stmt->execute(sql); delete stmt; } else { // 其他错误,重新抛出 throw; } } } ``` 但是,上述代码存在几个问题: 1. 连接信息(如URL、用户名、密码、数据库名)需要保存,以便重连时使用。 2. 如果重连后再次执行失败(比如又一次断开),则可能进入循环。因此,可以设置重试次数。 3. 多线程环境下,重连操作需要同步(例如,一个线程发现连接断开并重连,其他线程应等待或重新使用新连接)。 改进建议: - 封装一个数据库连接管理类,存储连接信息和驱动实例,并提供执行方法(支持重连和重试)。 - 使用互斥锁保证重连过程线程安全。 另一种思路:使用连接池(如引用[3]中提到Yii2中的连接池)来管理连接。当连接失效时,将连接从池中移除并创建新的连接放入池中。 由于用户直接询问`sql::mysql::get_mysql_driver_instance()`,我们重点放在如何利用它来实现重连。 具体实现步骤: 1. 获取驱动实例:`sql::mysql::MySQL_Driver *driver = sql::mysql::get_mysql_driver_instance();` 2. 使用驱动实例创建连接:`sql::Connection *conn = driver->connect(url, user, pass);` 3. 执行查询时,捕获异常,如果是断线错误,则删除当前连接,然后重新调用`driver->connect`创建新连接。 注意:`get_mysql_driver_instance`返回的是同一个驱动实例(单例),所以可以多次调用。 因此,我们可以构建一个可重连的数据库操作类: ```cpp #include <iostream> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/statement.h> #include <cppconn/exception.h> #include <mutex> class MySQLConnector { public: MySQLConnector(const std::string &url, const std::string &user, const std::string &pass, const std::string &db) : url_(url), user_(user), pass_(pass), db_(db) { driver_ = sql::mysql::get_mysql_driver_instance(); connect(); // 初始连接 } ~MySQLConnector() { delete conn_; } void executeQuery(const std::string &sql) { try { std::unique_lock<std::mutex> lock(mutex_); sql::Statement *stmt = conn_->createStatement(); stmt->execute(sql); delete stmt; } catch (sql::SQLException &e) { if (e.getErrorCode() == 2006 || e.getErrorCode() == 2013) { // 断线错误,重连 reconnect(); // 重连后重新执行 std::unique_lock<std::mutex> lock(mutex_); sql::Statement *stmt = conn_->createStatement(); stmt->execute(sql); delete stmt; } else { throw; // 其他异常抛出 } } } private: void connect() { if (conn_) { delete conn_; conn_ = nullptr; } conn_ = driver_->connect(url_, user_, pass_); conn_->setSchema(db_); } void reconnect() { std::unique_lock<std::mutex> lock(mutex_); connect(); // 重新连接 } sql::mysql::MySQL_Driver *driver_; sql::Connection *conn_ = nullptr; std::string url_; std::string user_; std::string pass_; std::string db_; std::mutex mutex_; }; ``` 说明: - 该类封装了连接和执行查询,并在遇到断线错误时自动重连。 - 使用互斥锁保证多线程安全(重连和执行查询互斥)。 - 注意:每次重连都会创建新连接,删除旧连接。 注意事项: - 由于Connector/C++的连接对象不是线程安全的,所以我们在每个操作前加锁(这里使用一个互斥锁保护整个连接对象)。 - 如果重连后再次失败,这里没有处理(会再次抛出异常,由调用者处理)。可以根据需要增加重试次数。 引用[3]中提到在长连接服务中少查询数据库,多用缓存,这也是减少断线影响的一种策略。 因此,针对用户的问题,答案是:通过捕获特定异常(错误码2006/2013),然后重新建立连接并重试操作,即可实现断线重连。 引用: [^1]: 使用错误码检测断线并重连的原理 [^2]: 驱动实例的获取方式(通过`get_mysql_driver_instance`) [^3]: 长连接服务中断线重连的必要性 </think> ### `sql::mysql::get_mysql_driver_instance()` 实现断线重连的方法 在 MySQL Connector/C++ 中,`sql::mysql::get_mysql_driver_instance()` 是获取 MySQL 驱动实例的核心函数,但**它本身不直接支持自动断线重连**。需要结合错误处理和连接管理机制实现重连功能。以下是具体实现方法: --- ### 1. **基本原理与流程** ```mermaid graph TD A[执行查询] --> B{是否成功?} B -- 成功 --> C[继续操作] B -- 失败 --> D{错误码是否为<br>2006/2013?} D -- 是 --> E[关闭旧连接<br>重建连接] E --> F[重试查询] D -- 否 --> G[抛出异常] ``` 关键点: 1. 检测特定错误码:`CR_SERVER_GONE_AWAY (2006)` 或 `CR_SERVER_LOST (2013)`[^1][^3] 2. 销毁旧连接后重建连接 3. **不直接重用原连接对象**(需新建对象) --- ### 2. **代码实现示例** ```cpp #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/exception.h> // 封装带重连功能的查询执行 void executeWithReconnect(sql::Connection *&conn, const std::string &query) { try { sql::Statement *stmt = conn->createStatement(); stmt->execute(query); // 尝试执行查询 delete stmt; } catch (sql::SQLException &e) { // 检测断线错误码 if (e.getErrorCode() == 2006 || e.getErrorCode() == 2013) { std::cerr << "Connection lost, reconnecting...\n"; // 关键步骤:销毁旧连接并重建 delete conn; // 释放旧连接 sql::mysql::MySQL_Driver *driver = sql::mysql::get_mysql_driver_instance(); conn = driver->connect("tcp://127.0.0.1:3306", "user", "password"); // 新建连接 // 重试查询 sql::Statement *newStmt = conn->createStatement(); newStmt->execute(query); delete newStmt; } else { throw; // 非断线错误重新抛出 } } } int main() { sql::mysql::MySQL_Driver *driver = sql::mysql::get_mysql_driver_instance(); sql::Connection *conn = driver->connect("tcp://127.0.0.1:3306", "user", "password"); // 使用带重连机制的查询 executeWithReconnect(conn, "SELECT * FROM users"); delete conn; // 最终清理 return 0; } ``` --- ### 3. **关键注意事项** 1. **连接对象生命周期管理**: - 必须彻底销毁旧连接 (`delete conn`) - 通过 `get_mysql_driver_instance()->connect()` 创建新连接对象[^2] 2. **线程安全**: ```cpp std::mutex conn_mutex; // 全局互斥锁 //... { std::lock_guard<std::mutex> lock(conn_mutex); delete conn; conn = driver->connect(...); } ``` 3. **重连策略优化**: - 指数退避重试:`retry_delay = min(2^retry_count * 100ms, 10s)` - 最大重试次数限制(建议 3-5 次) --- ### 4. **替代方案:连接池管理** 对于高频访问场景,推荐使用连接池(如参考引用[3]的Yii2方案): ```mermaid graph LR A[请求连接] --> B{连接池有<br>有效连接?} B -- 是 --> C[取用连接] B -- 否 --> D[新建连接] C --> E[执行操作] E --> F{操作失败?} F -- 是且断线 --> G[丢弃失效连接] G --> D F -- 否 --> H[归还连接] ``` 优点: - 避免频繁创建/销毁连接 - 自动隔离失效连接 - 支持心跳保活机制 --- ### 总结 实现 `sql::mysql::get_mysql_driver_instance()` 的断线重连需要: 1. 通过错误码 `2006/2013` 识别断线[^1] 2. **销毁并重建连接对象**(不可直接重用) 3. 结合互斥锁保证线程安全 4. 高频场景建议使用连接池管理[^3] > 注意:Connector/C++ 原生不支持自动重连,需手动实现上述逻辑。对于新建连接的开销敏感场景,连接池是最佳实践[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值