Mysql5的auto Reconnect错误

本文分享了一种解决Mysql数据库在服务器时间调整后导致连接异常的方法,通过使用proxool连接池并设置适当的检查参数,确保连接的有效性。
最近在一个J2EE项目的开发过程中,遇到了这样的问题:

在服务器上部署好这个Web系统后,这时访问系统是很正常的。当把服务器的时间(例如:2008-03-31)加一天或更多天(例如:2008-04-01,2008-04-02...),这时再访问这个Web系统,报出如下的异常:

  

com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception:



** BEGIN NESTED EXCEPTION **



java.io.EOFException

MESSAGE: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.



STACKTRACE:



java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.

at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1997)

at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2411)

at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2916)

at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1631)

at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1723)

at com.mysql.jdbc.Connection.execSQL(Connection.java:3250)

at com.mysql.jdbc.Connection.setAutoCommit(Connection.java:5395)

...





我们首先判断是连接池出了问题,即当系统时间改变后,数据库连接发现超时,因此需要重新连接或完全重建连接池。我们换用了一下数据库,如果使用Oracle数据库,同样的修改不会出现问题,系统运行正常,根据不会出现这样的异常。这就说明了出现这个异常是Mysql相关的,通过查阅资料得知可能和Mysql的运行参数(超时)有关,顺着这个思路我们来分析Mysql的一些运行参数。

在MySQL Command Line Client中执行show variables like '%timeout%';如下图所示:


在图中我们可以看到有两个变量wait_timeout和interactive-timeout,它们的默认值都为28800秒,即为8小时。也就是说默认情况下,Mysql在经过8小时(28800秒)不使用后会自动关闭已打开的连接。

为了解决这个问题,对于MySQL5之前的版本,如Mysql4.x,只需要修改连接池配置中的URL,添加一个参数:autoReconnect=true,如果是MySQL5及以后的版本,则需要修改my.cnf(或者my.ini)文件,在[mysqld]后面添加上:

wait_timeout = n

interactive-timeout = n

n为服务器关闭交互式连接前等待活动的秒数。

可是就部署而言每次修改my.ini比较麻烦,而且n等于多少才是合适的呢?所以这个解决办法不好。

怎么才能比较好的解决这个问题呢?通过仔细分析,原因大致为:当修改系统日期后Mysql自动关闭已打开的连接,可以数据库连接池(DBCP)还认为这些连接是活动的,如果这时有请求(需要执行读写数据库的操作),连接池就用一个连接去操作数据库,而这个连接在Mysql的连接池中并不存在,所以会出现以上的异常。如果一个连接在和Mysql建立连接时能检查就不会有这样的问题了。

这时我们同事想起了他以前用过的一个数据库连接池proxool,它有两个属性:一个是test-before-use,还有一个是test-after-use,这两个属性就是在使用前和使用后都要进行对连接的检查,如果连接无效就扔掉再创建一个新的连接,它们的解释如下:



test-before-use:


If you set this to true then each connection is tested (with whatever is defined in house-keeping-test-sql) before being served. If a connection fails then it is discarded and another one is picked. If all connections fail a new one is built. If that one fails then you get an SQLException saying so.


test-after-use:

If you set this to true then each connection is tested (with whatever is defined in house-keeping-test-sql) after it is closed (that is, returned to the connection pool). If a connection fails then it is discarded.


于是我们在项目中换上proxool的连接池,再运行并访问系统,更改系统时间,再访问,一切OK,问题就这样解决了!

不但如此,proxool的连接池中的连接的数量,活动数以及每个连接的的具体情况都可以一目了然的看到,如下图所示:


通过上面的测试和分析,针对于Mysql5数据库,选择proxool数据库连接池是比较合适的。

以上内容大部分是我同事执笔写的,我做了微量的修改,主要是和大家分享一下经验。
<think>我们参考引用[1][2][3][4]中关于PyQt5MySQL集成的信息,特别是引用[4]中提到的数据库连接方法,来编写一个将数据保存到MySQL数据库的示例程序。 ### 实现步骤 1. **创建数据库连接**:使用`QSqlDatabase`建立与MySQL的连接 2. **创建数据模型**:使用`QSqlTableModel`或直接执行SQL语句 3. **插入数据**:通过模型插入或直接执行INSERT语句 ### 完整示例代码 ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QVBoxLayout, QMessageBox from PyQt5.QtSql import QSqlDatabase, QSqlQuery class DatabaseApp(QWidget): def __init__(self): super().__init__() self.initUI() self.initDB() def initUI(self): self.setWindowTitle('PyQt5 MySQL示例') self.setGeometry(300, 300, 300, 200) # 创建按钮 self.btn_insert = QPushButton('插入数据', self) self.btn_insert.clicked.connect(self.insertData) # 布局 layout = QVBoxLayout() layout.addWidget(self.btn_insert) self.setLayout(layout) def initDB(self): # 创建MySQL数据库连接 [^4] self.db = QSqlDatabase.addDatabase('QMYSQL') self.db.setHostName('localhost') # 数据库主机 self.db.setDatabaseName('testdb') # 数据库名称 self.db.setUserName('root') # 用户名 self.db.setPassword('password') # 密码 if not self.db.open(): QMessageBox.critical(self, '数据库错误', self.db.lastError().text()) return False return True def insertData(self): # 使用SQL查询插入数据 [^5] query = QSqlQuery() # 准备插入语句 query.prepare("INSERT INTO users (name, age) VALUES (?, ?)") # 绑定值 query.addBindValue("张三") query.addBindValue(30) if not query.exec_(): QMessageBox.critical(self, '插入失败', query.lastError().text()) else: QMessageBox.information(self, '成功', '数据插入成功!') # 另一种方法:直接执行SQL字符串 # query.exec_("INSERT INTO users (name, age) VALUES ('李四', 25)") def closeEvent(self, event): # 关闭数据库连接 self.db.close() event.accept() if __name__ == '__main__': app = QApplication(sys.argv) ex = DatabaseApp() ex.show() sys.exit(app.exec_()) ``` ### 关键代码说明 1. **数据库连接配置** [^4]: ```python self.db = QSqlDatabase.addDatabase('QMYSQL') self.db.setHostName('localhost') self.db.setDatabaseName('testdb') self.db.setUserName('root') self.db.setPassword('password') ``` 2. **参数化查询**(防止SQL注入)[^5]: ```python query.prepare("INSERT INTO users (name, age) VALUES (?, ?)") query.addBindValue("张三") query.addBindValue(30) query.exec_() ``` ### 准备工作 1. 创建MySQL数据库和表: ```sql CREATE DATABASE testdb; USE testdb; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT ); ``` 2. 安装MySQL驱动: ```bash pip install PyMySQL # 或在代码中添加驱动(如果遇到QMYSQL未加载错误) import PyMySQL PyMySQL.install_as_MySQLdb() ``` ### 常见问题解决 1. **驱动未加载错误**: ```python # 在创建连接前添加 from PyQt5.QtSql import QSqlDatabase QSqlDatabase.isDriverAvailable('QMYSQL') # 返回True表示可用 ``` 2. **连接错误处理**: ```python if not self.db.open(): error = self.db.lastError().text() QMessageBox.critical(None, "数据库错误", f"连接失败: {error}") ``` ### 性能优化建议 - 批量插入时使用事务: ```python self.db.transaction() # 执行多条插入 self.db.commit() ``` - 对于大数据量插入,考虑使用`execBatch()`方法 ### 引用说明 本示例参考了引用[1][2][3][4]中关于PyQt5MySQL集成的技术方案,特别是引用[4]中提供的数据库连接方法和引用[5]中的SQL执行方式[^1][^2][^3][^4][^5]。 §§相关问题§§ 1. PyQt5如何连接SQLite数据库? 2. 如何在PyQt5中实现数据库事务处理? 3. PyQt5的QSqlTableModel如何使用? 4. 如何防止PyQt5数据库操作中的SQL注入? 5. PyQt5中如何实现数据库查询结果的分页显示?</think>### PyQt5 写入 MySQL 数据库完整示例 以下是基于引用[1][2][3][4]的 PyQt5MySQL 数据库集成方案,实现数据存储功能: #### 完整代码实现 ```python import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLineEdit, QVBoxLayout, QWidget, QMessageBox from PyQt5.QtSql import QSqlDatabase, QSqlQuery class DatabaseApp(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('PyQt5 MySQL 数据存储示例') self.setGeometry(300, 300, 400, 200) # 创建数据库连接 self.init_database() # 创建UI self.init_ui() def init_database(self): """初始化数据库连接[^4]""" # 添加MySQL数据库驱动 self.db = QSqlDatabase.addDatabase('QMYSQL') # 配置数据库连接参数 self.db.setHostName('localhost') # 数据库主机地址 self.db.setDatabaseName('pyqt_demo') # 数据库名称 self.db.setUserName('root') # MySQL用户名 self.db.setPassword('your_password') # MySQL密码 # 尝试连接数据库 if not self.db.open(): QMessageBox.critical(self, '数据库错误', self.db.lastError().text()) sys.exit(1) else: print("数据库连接成功!") # 创建数据表(如果不存在) self.create_table() def create_table(self): """创建数据表[^5]""" query = QSqlQuery() create_table_sql = """ CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, email VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """ if not query.exec_(create_table_sql): QMessageBox.critical(self, '创建表失败', query.lastError().text()) def init_ui(self): """初始化用户界面""" # 创建输入框 self.name_input = QLineEdit(self) self.name_input.setPlaceholderText("请输入姓名") self.email_input = QLineEdit(self) self.email_input.setPlaceholderText("请输入邮箱") # 创建按钮 self.save_btn = QPushButton("保存到数据库", self) self.save_btn.clicked.connect(self.save_to_database) # 布局 layout = QVBoxLayout() layout.addWidget(self.name_input) layout.addWidget(self.email_input) layout.addWidget(self.save_btn) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def save_to_database(self): """保存数据到MySQL数据库[^1][^2]""" name = self.name_input.text().strip() email = self.email_input.text().strip() # 验证输入 if not name or not email: QMessageBox.warning(self, '输入错误', '姓名和邮箱不能为空!') return # 准备SQL查询 query = QSqlQuery() query.prepare("INSERT INTO users (name, email) VALUES (?, ?)") # 绑定参数(防止SQL注入)[^5] query.addBindValue(name) query.addBindValue(email) # 执行插入操作 if query.exec_(): QMessageBox.information(self, '成功', '数据已保存到数据库!') # 清空输入框 self.name_input.clear() self.email_input.clear() else: QMessageBox.critical(self, '保存失败', query.lastError().text()) if __name__ == '__main__': app = QApplication(sys.argv) # 检查MySQL驱动是否可用 if 'QMYSQL' not in QSqlDatabase.drivers(): QMessageBox.critical(None, "驱动错误", "QMYSQL驱动不可用,请安装MySQL驱动") sys.exit(1) window = DatabaseApp() window.show() sys.exit(app.exec_()) ``` ### 使用前准备步骤 1. **安装MySQL数据库**: - 安装MySQL服务器([MySQL下载](https://dev.mysql.com/downloads/mysql/)) - 创建数据库:`CREATE DATABASE pyqt_demo;` 2. **安装PyQt5MySQL驱动**: ```bash pip install PyQt5 PyQt5-tools PyMySQL ``` 3. **配置MySQL连接参数**: ```python self.db.setHostName('localhost') # 改为你的MySQL主机 self.db.setDatabaseName('pyqt_demo') # 数据库名称 self.db.setUserName('root') # MySQL用户名 self.db.setPassword('your_password') # MySQL密码 ``` ### 关键功能说明 1. **数据库连接**: - 使用`QSqlDatabase`建立与MySQL的连接[^4] - 错误处理机制确保连接可靠性 2. **数据表创建**: - 自动创建`users`表(如果不存在)[^5] - 包含ID、姓名、邮箱和时间戳字段 3. **数据插入**: - 使用参数化查询防止SQL注入[^5] - 输入验证确保数据完整性 - 用户友好的反馈消息 4. **错误处理**: - 数据库连接错误 - SQL执行错误 - 输入验证错误 ### 常见问题解决 1. **QMYSQL驱动不可用**: ```python # 在代码开头添加 import pymysql pymysql.install_as_MySQLdb() ``` 2. **连接超时问题**: ```python # 在连接配置后添加 self.db.setConnectOptions("MYSQL_OPT_RECONNECT=1;") ``` 3. **批量插入优化**: ```python # 对于大数据量插入 self.db.transaction() # 开始事务 # 执行多个插入操作 self.db.commit() # 提交事务 ``` ### 进阶功能建议 1. **数据查询与展示**:添加查询界面显示数据库记录 2. **数据更新/删除**:实现CRUD完整功能 3. **数据验证**:添加邮箱格式验证等 4. **分页功能**:对于大数据量实现分页显示 本示例完整实现了PyQt5MySQL的集成,可直接运行并保存数据到数据库[^1][^2][^3][^4][^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值