本文重点是 六、避坑指南(核心重点!)的第四点:
mysql.connector 不兼容 MySQL 8.0 认证方式,或 DNS 反向解析超时,改用 PyMySQL;z
用局域网的另一台电脑访问本机MySQL数据时,用Navicat查询没问题,但是python代码查询时,不报错,也没有数据返回。其实时链接数据库是卡死了,直接退出了程序。
原因就是mysql.connector 的包不行,PyMySQL这个包可以。
一、需求背景
最近需要实现一个「局域网数据同步系统」,核心需求:
- 本机每 3 秒采集时间数据,写入 MySQL 数据库;
- 自动从时间字段中提取年 / 月 / 日,补全对应字段;
- 局域网内其他电脑可通过代码查询数据库,实时获取数据;
- 稳定不丢数据、不卡死、跨设备兼容。
最终实现了「3 件套」程序:自动上传 + 自动补全 + 跨机查询,全程踩了 N 个坑,特此整理成教程,帮大家少走弯路!
二、技术选型
工具 / 框架 | 选择理由 | 替代方案对比 |
MySQL 8.0 | 免费、稳定、支持局域网并发 | SQLite(不支持网络共享)、Access(易损坏) |
PyMySQL | Python 连接 MySQL 的稳定驱动 | mysql.connector(局域网易卡死,不推荐) |
Navicat | 可视化管理数据库,方便调试 | phpMyAdmin(网页版,操作繁琐) |
关键选型逻辑:文件型数据库(SQLite/Access)不适合局域网频繁写入,直接选择 C/S 架构的 MySQL,从根源避免数据丢失、锁表问题。
三、前置准备
1. 环境配置
- 服务器端(部署 MySQL):Windows 10/11 + MySQL 8.0
- 客户端(查询 / 上传):Windows 10/11 + Python 3.8+
- 网络:所有设备在同一局域网(路由器 / 交换机连接)
2. MySQL 基础配置
(1)安装 MySQL
推荐用 XAMPP 一键安装(免配置),或官网下载安装包,记住:
- 主机:localhost(服务器本机)
- 端口:3306(默认)
- 用户名:root
- 密码:自定义(本文示例用 123456)
(2)创建数据库和表
用 Navicat 连接本机 MySQL,执行以下 SQL:
-- 创建数据库(本文库名:hnx) CREATE DATABASE IF NOT EXISTS hnx CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 切换数据库 USE hnx; -- 创建数据表(存储时间数据) CREATE TABLE IF NOT EXISTS upload_data ( id INT PRIMARY KEY AUTO_INCREMENT, -- 自增ID time_full VARCHAR(20) NOT NULL, -- 完整时间(格式:20250103123456) year VARCHAR(4), -- 提取的年 month VARCHAR(2), -- 提取的月 day VARCHAR(2) -- 提取的日 ); |
四、核心程序实现(全套代码)
1. 程序 1:每 3 秒自动上传时间数据(服务器 / 本机)
功能:持续采集当前时间,写入 MySQL,支持后台运行。
# 安装依赖:pip install pymysql import pymysql import time from datetime import datetime # 配置信息(根据实际修改) CONFIG = { "host": "localhost", # 服务器本机IP(localhost/192.168.3.1) "user": "root", "password": "123456", "database": "hnx", "port": 3306, "charset": "utf8mb4" } def connect_db(): """创建数据库连接""" return pymysql.connect(**CONFIG, connect_timeout=5) def upload_time_data(): """每3秒上传一次时间数据""" conn = connect_db() cursor = conn.cursor() print("✅ 数据上传程序启动!每3秒上传一条数据(Ctrl+C停止)") try: while True: # 获取当前时间(年月日时分秒) time_full = datetime.now().strftime("%Y%m%d%H%M%S") # 插入数据(只传完整时间,年/月/日后续自动补全) sql = "INSERT INTO upload_data (time_full) VALUES (%s)" cursor.execute(sql, (time_full,)) conn.commit() print(f"✅ 上传成功 | 时间:{time_full}") time.sleep(3) except KeyboardInterrupt: print("\n�� 程序手动停止") finally: cursor.close() conn.close() if __name__ == "__main__": upload_time_data() |
2. 程序 2:自动补全年 / 月 / 日(服务器 / 本机)
功能:扫描表中year为空的数据,从time_full提取年 / 月 / 日并补全。
import pymysql import time # 同上面的CONFIG配置 CONFIG = { "host": "localhost", "user": "root", "password": "123456", "database": "hnx", "port": 3306, "charset": "utf8mb4" } def connect_db(): return pymysql.connect(**CONFIG, connect_timeout=5) def auto_fill_date(): print("✅ 自动补全年月日程序启动(PyMySQL版)") print("�� 正在扫描需要补全的数据...\n") while True: try: conn = connect_db() cursor = conn.cursor() # 1. 查询year为空的数据 select_sql = "SELECT id, time_full FROM upload_data WHERE year IS NULL OR year = ''" cursor.execute(select_sql) empty_data = cursor.fetchall() if not empty_data: print("⏳ 暂无需要补全的数据,10秒后重试...") time.sleep(10) continue # 2. 逐条提取并更新 for data_id, time_full in empty_data: if len(time_full) >= 8: # 确保时间格式正确 year = time_full[0:4] # 前4位:年 month = time_full[4:6] # 第5-6位:月 day = time_full[6:8] # 第7-8位:日 # 更新数据 update_sql = """ UPDATE upload_data SET year = %s, month = %s, day = %s WHERE id = %s """ cursor.execute(update_sql, (year, month, day, data_id)) conn.commit() print(f"✅ 补全成功 | ID:{data_id} | {year}年{month}月{day}日") cursor.close() conn.close() time.sleep(3) # 每3秒扫描一次 except Exception as e: print(f"❌ 出错重试:{str(e)}") time.sleep(3) if __name__ == "__main__": auto_fill_date() |
3. 程序 3:局域网跨机查询数据(客户端)
功能:局域网内任意电脑,通过服务器 IP 查询数据库,显示所有数据。
import pymysql # 配置信息(重点:host改为服务器IP) CONFIG = { "host": "192.168.3.1", # 服务器局域网IP(不是localhost!) "user": "root", "password": "123456", "database": "hnx", "port": 3306, "charset": "utf8mb4" } def connect_db(): return pymysql.connect(**CONFIG, connect_timeout=5) def query_data(): try: conn = connect_db() cursor = conn.cursor() # 查询所有数据(按ID排序) query_sql = "SELECT id, time_full, year, month, day FROM upload_data ORDER BY id ASC" cursor.execute(query_sql) all_data = cursor.fetchall() # 格式化输出 print("=" * 70) print("�� 局域网MySQL查询结果(hnx.upload_data)") print("=" * 70) for row in all_data: data_id, time_full, year, month, day = row print(f"ID:{data_id:>3} | 完整时间:{time_full} | 年:{year or '未填'} | 月:{month or '未填'} | 日:{day or '未填'}") print(f"\n✅ 共查询到 {len(all_data)} 条数据") cursor.close() conn.close() except Exception as e: print(f"❌ 查询失败:{str(e)}") if __name__ == "__main__": query_data() |
五、关键配置:让 MySQL 支持局域网访问
这是跨机查询的核心,少一步都不行!
1. 授权局域网用户访问
在服务器端(部署 MySQL 的电脑),用 Navicat 执行以下 SQL:
-- 允许root用户从任意局域网IP访问(%表示所有IP) CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '123456'; -- 授予所有权限(生产环境可限制权限,测试用全开) GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'; -- 刷新权限 FLUSH PRIVILEGES; -- 关键:修改认证方式(解决Python连接卡死) ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES; |
2. 开放 MySQL 3306 端口(防火墙)
服务器端必须开放 3306 端口,否则客户端连不上:
方法 1:图形界面(推荐)
1.按 Win+R → 输入wf.msc→ 打开「高级防火墙」;
2.左侧「入站规则」→ 右侧「新建规则」;
3.选择「端口」→ 下一步;
4.协议选 TCP → 特定本地端口填 3306 → 下一步;
5.选择「允许连接」→ 下一步;
6.勾选「专用 / 公用 / 域」→ 下一步;
7.名称填「MySQL 3306」→ 完成。
方法 2:命令行(管理员模式)
New-NetFirewallRule -DisplayName "MySQL 3306" -Direction Inbound -Protocol TCP -LocalPort 3306 -Action Allow |
3. 关闭 MySQL DNS 反向解析(可选,解决卡死)
这是AI给的方法,这一步我没有操作,也实现了功能。
如果客户端连接仍卡死,修改 MySQL 配置文件my.ini(通常在ProgramData\MySQL\MySQL Server 8.0\):
# 新增一行,关闭DNS反向解析 skip_name_resolve |
保存后,重启 MySQL 服务(服务面板找到 MySQL → 重启)。
六、避坑指南(核心重点!)
全程踩了 8 个坑,整理成「问题 - 原因 - 解决方案」,直接对照解决:
重点是第四点:
mysql.connector 不兼容 MySQL 8.0 认证方式,或 DNS 反向解析超时,改用 PyMySQL;
问题现象 | 根本原因 | 解决方案 |
报错no such table: upload_data | 表未创建,或数据库名错误 | 1. 执行建表 SQL;2. 确认代码中database参数正确 |
报错NameError: name 'conn' is not defined | 连接失败时,conn未初始化就关闭 | 提前定义conn = None,关闭前判断if conn is not None |
写入成功但查询为空 | 1. 数据库名错误(如误用系统库mysql);2. 写入未 commit | 1. 确认代码中database是自己建的库;2. 插入后执行conn.commit() |
客户端 Navicat 能连,Python 卡死不报错 | mysql.connector 不兼容 MySQL 8.0 认证方式,或 DNS 反向解析超时 | 1. 改用 PyMySQL;2. 执行ALTER USER修改认证方式;3. 关闭 DNS 反向解析 |
报错module 'pymysql' has no attribute 'connect' | 文件名或目录下有pymysql.py,覆盖了真实库 | 重命名文件(如改为mysql_query.py),删除__pycache__文件夹 |
局域网连接失败TimeoutError | 服务器防火墙未开放 3306 端口 | 按步骤开放 3306 端口,或临时关闭防火墙测试 |
报错Access denied for user 'root'@'xxx.xxx.xxx.xxx' | 未授权局域网用户访问 | 执行授权 SQL(CREATE USER+GRANT) |
补全程序提取年 / 月 / 日错误 | 时间格式长度不足 8 位 | 代码中添加if len(time_full) >=8判断,确保格式正确 |
七、最终效果
- 服务器端运行「上传程序」+「补全程序」:
- 每 3 秒生成一条时间数据;
- 自动补全年 / 月 / 日,日志实时输出;
- 局域网客户端运行「查询程序」:
- 秒连数据库,显示所有数据;
- 无卡死、无报错、数据实时同步。
八、扩展建议
- 生产环境优化:
- 给 MySQL 用户分配最小权限(不用ALL PRIVILEGES);
- 定期备份数据库(避免数据丢失);
- 限制上传频率(根据需求调整sleep时间);
- 功能扩展:
- 增加数据导出 Excel 功能;
- 加入异常邮件提醒;
- 打包成 exe 文件(用 PyInstaller),双击运行;
- 替代方案:
- 若不想用 MySQL,可选择 PostgreSQL(更稳定);
- 若需跨互联网访问,可配置端口映射 + 动态域名。
总结
这套方案的核心是「放弃文件型数据库,选择 C/S 架构的 MySQL」,从根源解决局域网数据同步的稳定性问题。全程最关键的是「MySQL 局域网授权 + 防火墙端口开放 + PyMySQL 驱动选择」,避开这三个坑,基本就能一次成功。
所有代码都经过实测,直接复制修改配置即可运行,希望能帮大家少走弯路!如果遇到其他问题,欢迎在评论区交流~
(注:文档部分内容可能由 AI 生成)