资讯详情

资讯详情

Python 2.7 老项目救星:mysql-connector-python 2.1.7 源码包安装与避坑指南

简介mysql-connector-python-2.1.7.tar.gz 是 MySQL 官方推出的 Python 数据库适配器 2.1.7 版本源码包面向需要在 Python 项目中访问和管理 MySQL 数据库的开发者尤其适合使用 Python 2.x 或 3.x 环境、希望遵循 DBAPIPEP 249规范进行数据库编程的中初级程序员。资源包共 123 个文件以 90 个 py 源码文件为核心另含 10 个 pem 证书、5 个 cnf 配置、5 个 h 头文件、4 个 c 源文件及若干 txt、csv 等辅助文件整体约 11.24MB覆盖连接管理、游标操作、事务处理、类型映射、连接池与错误处理等模块。该版本支持多种认证插件并可通过 pip 或源码方式安装。目前已有 364 人学习下载适合需要研究适配器内部实现、排查连接与认证问题或进行二次开发的读者参考。1. 从一次 Python 2 老项目的数据库连接翻车说起上周帮朋友救一个 2016 年的 Django 1.8 项目服务器上 Python 还是 2.7pip install mysql-connector-python直接拉下来最新版结果 import 就报SyntaxError。翻回去看requirements.txt写的是mysql-connector-python2.1.7。这个版本号不是随便写的——它是 MySQL 官方 Connector/Python 在 Python 2.7 时代最后一个稳定分支也是很多老系统里唯一能跑通的驱动。mysql-connector-python-2.1.7.tar.gz这个源码包解决的就是「Python 2.7 MySQL 5.5/5.6/5.7 环境下用官方驱动而不是 MySQLdb」这件事。它适合两类人一是维护遗留系统的运维和后台开发二是需要在离线内网环境里编译安装 Python MySQL 驱动的工程师。如果你手上全是 Python 3.8 的新项目这篇可以直接跳过用mysql-connector-python8.x 或PyMySQL更省事。2. 为什么是 2.1.7版本选型与源码包结构拆解2.1 官方驱动 vs MySQLdb vs PyMySQL 的取舍在 Python 2.7 时代连 MySQL 有三条路。MySQLdb是 C 扩展性能好但编译依赖mysql-develWindows 上装起来经常卡在mysql_config not found。PyMySQL是纯 Python装起来无痛但当年对utf8mb4和存储过程的支持不如官方驱动完整。mysql-connector-python是 MySQL 官方维护的纯 Python 驱动2.1.7 这个版本在 2017 年发布兼容 Python 2.6/2.7/3.3/3.4API 遵循 PEP 249同时支持连接池和cursor(preparedTrue)预处理。选 2.1.7 的核心理由是它是 2.x 分支里对 Python 2.7 支持最完整的版本再往后的 8.x 直接砍掉了 Python 2 支持。源码包mysql-connector-python-2.1.7.tar.gz解压后主要包含setup.py、mysql/connector/目录下的connection.py、cursor.py、protocol.py、conversion.py以及mysql/connector/locales/下的错误消息翻译文件。没有 C 扩展所以不需要mysql-devel这是它在离线环境里最大的优势。2.2 源码包目录结构与关键文件解压后先别急着python setup.py install花两分钟看清楚结构能省掉后面很多麻烦。常见做法是先tar -tzf看一眼顶层目录名因为有些源码包解压出来是mysql-connector-python-2.1.7/有些是mysql-connector-python/写脚本时路径写错就白跑。# 查看压缩包顶层结构确认解压后的目录名 tar -tzf mysql-connector-python-2.1.7.tar.gz | head -20 # 解压到当前目录 tar -xzf mysql-connector-python-2.1.7.tar.gz # 进入源码目录按实际解压出的目录名调整 cd mysql-connector-python-2.1.7 # 查看关键文件是否齐全 ls setup.py mysql/connector/connection.py mysql/connector/cursor.pysetup.py里定义了包名、版本和依赖2.1.7 的install_requires基本为空不强制拉其他包这对内网离线安装很友好。connection.py是连接建立和协议握手的主逻辑cursor.py负责 SQL 执行和结果集处理conversion.py管 Python 类型和 MySQL 类型之间的转换。如果后面遇到TypeError或编码问题八成要回到conversion.py里看_python_to_mysql的映射表。2.3 安装方式源码编译 vs 直接拷贝源码包有两种用法。第一种是标准安装python setup.py install会把mysql包装进site-packages。第二种是「绿色」用法直接把解压出来的mysql目录拷到项目根目录靠 Python 的当前目录搜索路径生效。第二种在没法动系统site-packages的受限环境里很常见但要注意别和已安装的其他版本冲突。# 方式一标准安装需要写 site-packages 权限 python setup.py build python setup.py install # 方式二绿色拷贝适合无 root 权限或不想污染环境 cp -r mysql /path/to/your/project/ # 然后在项目代码里正常 import mysql.connector 即可build阶段会做字节码编译如果这一步报SyntaxError说明你当前 Python 版本和源码不匹配——2.1.7 的代码里有 Python 2 的print语句残留用 Python 3 跑setup.py会直接挂。参数上setup.py install可以加--prefix/opt/py27指定安装前缀适合多版本 Python 共存的机器。绿色拷贝方式没有参数但拷完后建议python -c import mysql.connector; print(mysql.connector.__version__)验证一下加载的是不是 2.1.7。3. 连接、查询与参数化2.1.7 的 API 实操3.1 建立连接与连接池配置2.1.7 的connect()参数和现在 8.x 大体一致但有几个默认值不同。比如charset默认不是utf8mb4autocommit默认Falseconnection_timeout默认10秒。在老项目里最常见的翻车是中文乱码根因就是没显式指定charsetutf8mb4。# Python 2.7 下的连接示例注意文件头编码声明 # -*- coding: utf-8 -*- import mysql.connector from mysql.connector import errorcode config { user: app_user, password: your_password, host: 127.0.0.1, port: 3306, database: legacy_db, charset: utf8mb4, # 必须显式指定否则中文可能变问号 autocommit: False, # 2.1.7 默认就是 False写出来更清楚 connection_timeout: 10, # 连接超时内网可适当调小 use_unicode: True, # 让驱动返回 unicode 而不是 bytes } try: conn mysql.connector.connect(**config) print(connected, server version:, conn.get_server_info()) except mysql.connector.Error as err: if err.errno errorcode.ER_ACCESS_DENIED_ERROR: print(用户名或密码错误) elif err.errno errorcode.ER_BAD_DB_ERROR: print(数据库不存在) else: print(err)use_unicodeTrue在 2.1.7 里很关键Python 2 的str和unicode是两套东西不开这个参数cursor.fetchone()返回的中文是str后续json.dumps会抛UnicodeDecodeError。connection_timeout设太小在跨机房场景会误报设太大又会让故障连接卡住线程内网一般 5 到 10 秒够用。连接池用mysql.connector.pooling.MySQLConnectionPoolpool_size建议不超过 MySQL 的max_connections除以应用实例数。3.2 参数化查询与批量插入2.1.7 支持%s占位符的参数化查询也支持executemany批量插入。这里有个血泪经验executemany在 2.1.7 里默认不是真正的批量协议而是一条条发数据量大时慢得让人怀疑人生。要提速得配合cursor(preparedTrue)或者手动拼INSERT ... VALUES (...),(...)。cursor conn.cursor() # 单条参数化查询防止 SQL 注入 cursor.execute( SELECT id, name FROM users WHERE status %s AND created_at %s, (active, 2024-01-01) ) for row in cursor.fetchall(): print(row[0], row[1]) # 批量插入executemany 写法 data [(1, alice), (2, bob), (3, carol)] cursor.executemany( INSERT INTO users (id, name) VALUES (%s, %s), data ) conn.commit() # autocommitFalse 时必须手动提交 cursor.close() conn.close()参数说明%s是 2.1.7 唯一支持的占位符别用?或:name那是别的驱动。executemany的第二个参数必须是元组列表传生成器在 2.1.7 里会报TypeError。commit()不能忘忘了就是「代码没报错但数据没进去」的经典玄学问题。如果批量数据超过一万条建议分批executemany每批 1000 条避免单次事务过大导致innodb_log_file_size撑爆。3.3 异常处理与错误码对照2.1.7 的异常体系是mysql.connector.Error基类下面分InterfaceError、DatabaseError、OperationalError等。老项目里最常遇到的是OperationalError: (2006, MySQL server has gone away)原因是连接空闲超过wait_timeout被服务端掐了而客户端还以为连接活着。import mysql.connector from mysql.connector import errorcode def safe_query(conn, sql, paramsNone): try: cursor conn.cursor() cursor.execute(sql, params or ()) return cursor.fetchall() except mysql.connector.OperationalError as e: # 2006 和 2013 都表示连接断了需要重连 if e.errno in (2006, 2013): conn.reconnect(attempts3, delay2) cursor conn.cursor() cursor.execute(sql, params or ()) return cursor.fetchall() raise finally: cursor.close()reconnect(attempts3, delay2)是 2.1.7 自带的自动重连attempts是重试次数delay是每次间隔秒数。注意重连后事务会丢所以只适合读操作写操作重连后要重新组织事务。错误码2006是服务端断开2013是查询过程中断开两者处理方式一样。如果频繁出现去 MySQL 侧把wait_timeout调大或者在应用层加心跳SELECT 1。4. 避坑与排查2.1.7 在真实环境里的五个坑4.1 坑一Python 3 环境强行安装报 SyntaxError现象在 Python 3.6 下执行python setup.py install报SyntaxError: Missing parentheses in call to print。原因2.1.7 的setup.py和部分模块里还有 Python 2 的print语句不是合法的 Python 3 语法。解决确认项目 Python 版本是 2.7如果必须在 Python 3 下用换mysql-connector-python8.x别硬改源码改了conversion.py里的字节串处理会引发更多问题。4.2 坑二中文乱码存进去是问号现象插入中文后SELECT出来是???或者 Python 侧报UnicodeDecodeError。原因连接时没指定charset2.1.7 默认走latin1而表是utf8mb4。解决连接参数加charsetutf8mb4和use_unicodeTrue同时确认 MySQL 服务端character_set_server是utf8mb4。如果表已经建错字符集ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4补一刀。4.3 坑三executemany 批量插入慢到超时现象一万条数据executemany跑了三分钟还没完最后OperationalError超时。原因2.1.7 的executemany默认逐条发送没有走 MySQL 的批量协议。解决改用手动拼多值INSERT或者升级到支持preparedTrue批量优化的版本在 2.1.7 里只能分批加多值拼接每批 500 到 1000 条。4.4 坑四连接空闲后 gone away现象服务跑一晚上第二天第一个请求必报2006 MySQL server has gone away。原因MySQL 默认wait_timeout28800秒连接池里的空闲连接被服务端单方面关闭客户端不知情。解决连接池配置pool_reset_session或在应用层定时SELECT 1保活捕获2006后调conn.reconnect()重试。根治办法是把wait_timeout和连接池pool_size一起评估别让连接闲太久。4.5 坑五源码包解压后目录名不对导致脚本失败现象CI 脚本里写死cd mysql-connector-python-2.1.7换了个来源的 tar 包后解压出来是mysql-connector-python/脚本报No such file or directory。原因不同渠道打包时顶层目录名不一致。解决脚本里别写死目录名用tar -xzf xxx.tar.gz后cd $(tar -tzf xxx.tar.gz | head -1 | cut -d/ -f1)动态进入或者先tar -tzf确认再写。5. 离线环境部署与版本验证的实操技巧内网离线部署 2.1.7最稳的路径是「在有网的机器上 build 好把 build 产物拷进去」。python setup.py build会在build/lib/mysql/下生成纯 Python 文件把这个mysql目录打包拷到目标机器的site-packages或项目根目录即可不需要目标机器有编译工具。验证是否加载正确版本别只看pip list因为绿色拷贝方式pip根本看不到。# 在目标机器上验证实际加载的版本和路径 python -c import mysql.connector; print(mysql.connector.__version__); print(mysql.connector.__file__) # 预期输出 2.1.7 和指向你拷贝的 mysql/connector/__init__.py 路径如果输出的是 8.x 或路径指向系统site-packages说明有旧版本抢先被 import 了检查sys.path顺序把项目目录放到最前面。另一个技巧是写一个最小连通性测试脚本部署完立刻跑一遍别等业务代码报错才发现驱动没装对。# health_check.py —— 部署后第一件事就跑它 import sys import mysql.connector print(python:, sys.version) print(connector:, mysql.connector.__version__) try: conn mysql.connector.connect( host127.0.0.1, port3306, userapp_user, passwordyour_password, databaselegacy_db, charsetutf8mb4, connection_timeout5 ) cur conn.cursor() cur.execute(SELECT VERSION()) print(mysql server:, cur.fetchone()[0]) cur.execute(SHOW VARIABLES LIKE character_set_server) print(cur.fetchone()) cur.close() conn.close() print(OK) except Exception as e: print(FAIL:, repr(e)) sys.exit(1)这个脚本把 Python 版本、驱动版本、MySQL 版本、服务端字符集一次性打出来出问题时一眼能定位是驱动层还是服务端配置层。我一般会把它塞进部署流水线的最后一步失败就阻断发布。参数上connection_timeout5故意设短离线环境网络通常很快5 秒连不上基本就是配置错了没必要等 10 秒。从那以后我每次碰 Python 2.7 老项目第一件事就是python -c import mysql.connector; print(mysql.connector.__version__)确认驱动版本再跑一遍 health check绝不靠「上次能跑」的经验。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →