
简介mysql-connector-python-2.1.7.tar.gz 是 MySQL 官方推出的 Python 数据库适配器源码包面向需要在 Python 项目中访问和管理 MySQL 的开发者尤其适合使用 Python 2.x 或 3.x 进行后端开发、数据处理与自动化脚本编写的中级学习者。该适配器遵循 DBAPIPEP 249规范提供连接管理、游标操作、事务控制、结果集处理、类型映射、连接池及错误异常处理等能力并支持多种认证插件便于在不同 MySQL 环境下稳定交互。压缩包共 123 个文件约 11.24MB以 90 个 py 源码文件为核心辅以 pem 证书、cnf 配置、h 与 c 底层实现文件及少量 txt、csv 说明数据结构完整便于阅读源码与本地编译安装。目前已有 364 人学习下载。通过该资源读者可获取 2.1.7 版本完整源码理解适配器内部实现与接口设计并可直接用于项目依赖或二次开发参考。1. 从 mysql-connector-python-2.1.7.tar.gz 说起一个老版本驱动包为什么还有人翻出来如果你在 Python 项目里连 MySQL大概率写过import mysql.connector。这个包就是 mysql-connector-pythonMySQL 官方出的纯 Python 驱动不依赖 C 库装完即用。而mysql-connector-python-2.1.7.tar.gz是一个源码分发包不是 wheel不是 exe是那种需要先解压、再python setup.py install或者pip install指向本地路径才能装上的 tar.gz。问题来了2024 年了为什么还有人专门去找 2.1.7 这个版本我踩过这个坑。一些老系统跑在 Python 2.7 上或者 Python 3.4/3.5 环境新版本驱动直接不兼容报语法错误或者依赖缺失。2.1.7 是少数还能在这些老解释器上跑起来的版本。另外有些内网环境没有外网 pip 源只能拿 tar.gz 离线装。所以这个标题背后不是“怀旧”是真实的离线部署和老环境兼容需求。这篇文章面向两类人一是手里已经拿到这个 tar.gz想知道怎么装、怎么配、怎么不翻车二是正在维护老 Python 项目需要判断这个版本能不能用、边界在哪。我会按“先搞清楚它是什么 → 怎么装怎么连 → 参数怎么调 → 坑在哪 → 怎么验证”的顺序讲每一步都给可复现的命令和代码。2. 拆开 tar.gz 看结构2.1.7 里到底装了什么2.1 源码包的目录布局与关键文件拿到mysql-connector-python-2.1.7.tar.gz之后先别急着装。解压看一眼目录能省掉后面很多“为什么 import 失败”的排查时间。tar -tzf mysql-connector-python-2.1.7.tar.gz | head -40你会看到类似这样的结构mysql-connector-python-2.1.7/ ├── setup.py ├── README.txt ├── LICENSE.txt ├── mysql/ │ └── connector/ │ ├── __init__.py │ ├── connection.py │ ├── cursor.py │ ├── protocol.py │ ├── conversion.py │ ├── errors.py │ ├── constants.py │ └── ... └── mysql_connector_python.egg-info/关键点真正的包代码在mysql/connector/下面顶层包名是mysql但mysql目录下只有connector这一个子包。这意味着如果你环境里已经装了别的提供mysql命名空间的库比如某些老版 MySQLdb 的变体可能会冲突。我一般会先pip list | grep -i mysql确认一下。setup.py里定义了版本号、依赖和入口。2.1.7 这个版本的setup.py里install_requires基本是空的它不强制依赖第三方库纯 Python 实现协议。这也是它在离线环境好用的原因——一个 tar.gz 丢进去就能装不用再拉一堆依赖。2.2 为什么是纯 Python 实现以及它和 C 扩展驱动的差别mysql-connector-python 是纯 Python 写的走 MySQL 客户端/服务端协议。对比 MySQLdb 或者 mysqlclient 那种基于 libmysqlclient C 库的驱动差别很明显维度mysql-connector-python 2.1.7mysqlclientC 扩展安装依赖无外部 C 库纯 Python需要 libmysqlclient-dev 和编译工具链性能协议解析在 Python 层吞吐偏低C 层解析批量插入快Python 2.7 支持支持新版本已放弃离线部署tar.gz 直接装需要先解决系统库协议特性支持 SSL、压缩、连接池部分依赖 C 库版本选它的理由通常不是性能而是“装得上”。内网、老系统、没有编译环境这三点凑齐纯 Python 就是唯一解。但你要接受它的性能边界单连接每秒几千次简单查询到顶了大批量写入要用executemany并且调大batch_size不然会慢到怀疑人生。2.3 安装前必须确认的 Python 版本与 pip 行为2.1.7 支持的 Python 版本范围从它的setup.py分类器看覆盖 Python 2.7 到 3.5 左右。如果你在 Python 3.8 上硬装可能会遇到collections.Iterable这类已移除的导入报错。所以第一步永远是确认解释器版本python --version python -c import sys; print(sys.version_info)如果版本在 3.6 以上我建议直接换新版本驱动不要跟 2.1.7 较劲。如果确实锁死在 2.7 或 3.4那就继续。安装命令有两种写法区别在于是否走 pip 的构建隔离# 方式一直接 pip 安装本地 tar.gz pip install mysql-connector-python-2.1.7.tar.gz # 方式二解压后进入目录安装便于看报错 tar -xzf mysql-connector-python-2.1.7.tar.gz cd mysql-connector-python-2.1.7 python setup.py install方式二的好处是报错信息直接打在终端上不会藏在 pip 的构建日志里。老版本 pip 对 tar.gz 的处理有时候会先尝试拉取依赖离线环境会卡住这时候加--no-deps --no-indexpip install --no-deps --no-index mysql-connector-python-2.1.7.tar.gz装完验证python -c import mysql.connector; print(mysql.connector.__version__)输出2.1.7就对了。如果报ImportError先看是不是当前目录下有个mysql文件夹把包路径遮住了——这是血泪经验很多人解压后直接在解压目录里跑 Python结果 import 到的是源码目录而不是安装后的包。3. 用 2.1.7 连上 MySQL最小可用代码与连接参数3.1 一个能跑通的最小连接脚本装好之后先别写复杂逻辑用最小脚本确认能连上。下面这段代码在 Python 2.7 和 3.4 上都能跑# minimal_connect.py import mysql.connector from mysql.connector import errorcode # 连接配置按实际环境替换 config { user: app_user, password: app_pass, host: 127.0.0.1, port: 3306, database: test_db, charset: utf8mb4, use_unicode: True, connection_timeout: 10, } try: conn mysql.connector.connect(**config) print(connected, server version:, conn.get_server_info()) cursor conn.cursor() cursor.execute(SELECT 1) print(select 1 -, cursor.fetchone()) cursor.close() conn.close() 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)逻辑说明connect(**config)把字典展开成关键字参数2.1.7 的connect签名接受这些键。connection_timeout单位是秒不设的话默认可能很长网络不通时会卡住。charset设成utf8mb4是为了支持 emoji 和四字节字符2.1.7 对utf8mb4的支持是完整的但前提是服务端和表也用这个字符集。参数说明use_unicodeTrue让返回的字符串是 unicode 对象Python 2 下尤其重要不然中文会变乱码。port默认 3306如果走非标准端口必须显式写。database可以省略连上后再cursor.execute(USE test_db)但我不推荐因为连接池场景下容易串库。3.2 连接参数逐个拆哪些必须设哪些设了会翻车2.1.7 的connect()支持几十个参数但日常真正要调的没几个。下面这张表是我实际项目里会显式设置的参数建议值作用与注意点hostIP 或域名用 127.0.0.1 比 localhost 稳避免走 socket 时的权限差异port3306非标准端口必须写user / password按环境不要硬编码在代码里用环境变量或配置文件database库名建议显式指定避免后续 USE 语句charsetutf8mb4老版本默认可能是 latin1中文会乱码use_unicodeTruePython 2 下必须Python 3 默认就是connection_timeout5~10不设的话网络故障时线程会挂很久autocommitFalse默认就是 False需要自动提交再改 Truesql_mode按需2.1.7 可以传但服务端会覆盖别依赖它有一个参数叫buffered默认 False。意思是查询结果不一次性拉到客户端而是按需从服务端读。好处是省内存坏处是如果你在同一个连接上又发新查询会报Unread result found。我一般对结果集小的查询设bufferedTrue大结果集保持默认并尽快读完。# buffered 用法 conn mysql.connector.connect(**config) cursor conn.cursor(bufferedTrue) cursor.execute(SELECT id, name FROM users LIMIT 100) rows cursor.fetchall() # 一次性读完后续可继续用同一连接3.3 执行增删改查与事务提交的正确姿势2.1.7 默认不开 autocommit所以 INSERT/UPDATE/DELETE 之后必须conn.commit()否则连接关闭时数据回滚。这是新手最容易翻车的地方——代码跑完没报错但数据没进去。# crud_demo.py import mysql.connector conn mysql.connector.connect(**config) cursor conn.cursor() try: # 插入 cursor.execute( INSERT INTO users (name, age) VALUES (%s, %s), (张三, 28) ) print(inserted id:, cursor.lastrowid) # 更新 cursor.execute( UPDATE users SET age %s WHERE name %s, (29, 张三) ) print(rows affected:, cursor.rowcount) # 查询 cursor.execute(SELECT id, name, age FROM users WHERE name %s, (张三,)) for row in cursor.fetchall(): print(row) conn.commit() # 关键不 commit 等于白干 except mysql.connector.Error as err: conn.rollback() print(操作失败已回滚:, err) finally: cursor.close() conn.close()参数说明占位符用%s不管字段是字符串还是数字2.1.7 的驱动会做类型转换。不要用 Python 的%或format拼 SQL那是 SQL 注入的入口。cursor.lastrowid返回自增主键cursor.rowcount返回受影响行数UPDATE 时如果值没变rowcount 可能是 0这不是错误。事务边界要自己控制。如果一次业务操作有多条 SQL把它们放在同一个 try 里最后统一 commit异常时 rollback。2.1.7 不支持 savepoint 的嵌套回滚那么细但基本的事务原子性够用。4. 参数调优与批量操作让 2.1.7 在老环境里跑得动4.1 executemany 的 batch_size 与性能拐点单条 INSERT 循环一万次在 2.1.7 上大概要几十秒。换成executemany能降到几秒但前提是设对batch_size。2.1.7 的executemany内部会把数据按 batch 拼成多值 INSERT 语句batch 太大反而会因为 SQL 过长被服务端拒绝。# batch_insert.py import mysql.connector import time conn mysql.connector.connect(**config) cursor conn.cursor() data [(i, user_%d % i) for i in range(10000)] start time.time() cursor.executemany( INSERT INTO users (age, name) VALUES (%s, %s), data ) conn.commit() print(elapsed: %.2fs % (time.time() - start)) cursor.close() conn.close()逻辑说明executemany接收 SQL 和可迭代的元组序列。2.1.7 默认会把所有数据一次性拼成一条大 SQL数据量大时可能超过max_allowed_packet。我一般会手动分批batch_size 500 for i in range(0, len(data), batch_size): cursor.executemany( INSERT INTO users (age, name) VALUES (%s, %s), data[i:ibatch_size] ) conn.commit()参数说明batch_size设 500 到 1000 比较稳具体看单行数据大小。如果单行有长文本降到 100。max_allowed_packet服务端默认 4MB 或 16MB超过就报Packet too large。这个错误在 2.1.7 上不会自动重试只能自己分批。4.2 连接池在 2.1.7 里的可用性与替代方案2.1.7 自带mysql.connector.pooling模块提供MySQLConnectionPool。但说实话这个版本的连接池有几个已知问题池满时行为不够优雅连接回收依赖pool_reset_session老 MySQL 服务端可能不支持。# pool_demo.py from mysql.connector import pooling pool pooling.MySQLConnectionPool( pool_namemypool, pool_size5, pool_reset_sessionTrue, **config ) conn pool.get_connection() cursor conn.cursor() cursor.execute(SELECT 1) print(cursor.fetchone()) cursor.close() conn.close() # 归还连接到池不是真正关闭参数说明pool_size是池内连接数上限pool_reset_session为 True 时每次取连接会重置会话变量避免上一个使用者的SET残留。如果服务端是 MySQL 5.5 以下这个参数可能报错改成 False 并自己在业务层清理。如果连接池不够稳替代方案是自己用threading.local()做线程级连接复用或者用 DBUtils 这类第三方池。但在纯离线环境里多一个依赖就多一份麻烦我通常先用自带池压测有问题再换。4.3 超时、重试与断线重连的边界老环境网络抖动是常态。2.1.7 的connect()有connection_timeout但查询执行没有独立的读超时。一个慢查询可能把连接挂死。我一般会在服务端设wait_timeout和interactive_timeout客户端配合做重试。# retry_connect.py import mysql.connector import time def get_conn(retries3, delay2): for attempt in range(retries): try: return mysql.connector.connect(**config) except mysql.connector.Error as err: print(connect attempt %d failed: %s % (attempt 1, err)) if attempt retries - 1: time.sleep(delay) raise RuntimeError(cannot connect after retries) conn get_conn()逻辑说明重试只针对连接建立阶段不针对查询。查询失败要看错误码2006 MySQL server has gone away和2013 Lost connection可以重连后重试但要注意幂等性——INSERT 重试可能产生重复数据。我的习惯是写操作带唯一键重试时用INSERT IGNORE或ON DUPLICATE KEY UPDATE。参数说明retries设 3 次够了delay用指数退避更好但 2.1.7 时代很多项目就是固定 sleep。connection_timeout设 5 秒配合重试总耗时可控。5. 避坑与排查2.1.7 在真实环境里的五个翻车现场5.1 现象import mysql.connector 报 SyntaxError原因Python 3.8 上装了 2.1.7源码里用了async作为变量名或collections.Iterable新解释器不认。解决确认解释器版本3.6 以上换新驱动。如果必须用 2.1.7把解释器降到 3.5 或 2.7。不要试图改源码改了一处还有十处。5.2 现象中文写入后查询出来是问号原因连接 charset 没设或设成了 latin1服务端表字符集也不对。解决连接参数加charsetutf8mb4和use_unicodeTrue同时确认服务端SHOW VARIABLES LIKE character_set%里character_set_server是 utf8mb4。表建的时候也要DEFAULT CHARSETutf8mb4。三处缺一不可。5.3 现象executemany 报 Packet too large原因一次性提交的数据超过max_allowed_packet。解决手动分批batch_size 降到 500 以下。或者临时调大服务端max_allowed_packet但生产环境不建议随便调分批更可控。5.4 现象连接池 get_connection 卡住不返回原因池内连接被泄漏没有正确 close 归还。2.1.7 的池不会自动回收超时连接。解决确保每次get_connection()后都在 finally 里conn.close()。如果代码路径复杂用上下文管理器封装。另外pool_size不要设太大5 到 10 够用设大了反而容易泄漏。5.5 现象查询报 Unread result found原因上一个查询的结果没读完又发了新查询。bufferedFalse时尤其常见。解决要么把结果fetchall()读完要么创建 cursor 时设bufferedTrue。我一般对交互式查询用 buffered对大批量导出用非 buffered 并确保读完。6. 验证 2.1.7 是否真的可用一个自检脚本与版本迁移判断装完、连上、跑完 CRUD 之后怎么确认这个版本在你的环境里是“真的可用”而不是“碰巧没报错”我习惯写一个自检脚本把关键路径都过一遍。# self_check.py import mysql.connector from mysql.connector import errorcode def check(name, fn): try: fn() print([PASS] %s % name) except Exception as e: print([FAIL] %s - %s % (name, e)) conn mysql.connector.connect(**config) cursor conn.cursor(bufferedTrue) check(basic select, lambda: cursor.execute(SELECT 1)) check(utf8mb4 insert, lambda: cursor.execute( CREATE TEMPORARY TABLE t_chk (v VARCHAR(10) CHARACTER SET utf8mb4))) check(utf8mb4 roundtrip, lambda: ( cursor.execute(INSERT INTO t_chk VALUES (%s), (测试,)), cursor.execute(SELECT v FROM t_chk), cursor.fetchone() )) check(transaction rollback, lambda: ( conn.rollback(), )) check(executemany, lambda: cursor.executemany( INSERT INTO t_chk VALUES (%s), [(a,), (b,)] )) cursor.close() conn.close()这个脚本覆盖了连接、字符集、事务、批量四个核心能力。如果全 PASS说明 2.1.7 在你的环境里能承担基本业务。如果有 FAIL按错误码定位别硬扛。关于版本迁移的判断如果你的项目还在 Python 2.7短期内没法升级解释器那 2.1.7 就是合理选择但要在代码里隔离数据库访问层方便以后换驱动。如果解释器能升到 3.6直接上 mysql-connector-python 8.xAPI 大体兼容主要改connect()里几个废弃参数。迁移时先跑上面的自检脚本再跑业务回归。我自己的习惯是任何老版本驱动上线前先在这个自检脚本基础上加一条“连续 1000 次查询不报错”的压测确认连接不会莫名断掉。老版本驱动的玄学问题往往在长时间运行后才暴露。希望帮到你。本文还有配套的精品资源点击获取