 参数到连接生命周期与底层协议)
数据库数据库客户端后端【免费下载链接】PyMySQLMySQL client library for Python项目地址https://gitcode.com/gh_mirrors/py/PyMySQL点击查看免费下载导读本文以 PyMySQL 官方 API 参考文档 docs/source/modules/connections.rst 所定义的Connection对象为主线系统讲解该对象的全部构造参数、连接建立流程、事务与会话控制、SSL/TLS 与认证插件机制并结合 pymysql/connections.py 源码逐一印证其底层实现。读完本文你将能够熟练创建、配置、诊断并安全关闭 PyMySQL 的 MySQL 连接理解autocommit、read_timeout、ssl_verify_identity、auth_plugin_map等关键参数的真实行为并在生产代码中正确运用上下文管理器、ping()与defer_connect等生命周期工具。Connection 对象在 PyMySQL 中的核心角色Connection是pymysql.connections模块中的核心类官方文档中的定位是 Representation of a socket with a mysql server——即一个与 MySQL 服务端通信的套接字封装。它负责与 MySQL 服务端建立 TCP/IP 或 UNIX 域套接字连接完成握手与认证支持多种认证插件为Cursor提供执行 SQL、读取结果集、事务控制等底层能力维护连接状态open、_closed、字符集、SSL 上下文与超时配置。从入口角度看pymysql/__init__.py中有一行关键别名定义Connect connect Connection connections.Connection因此pymysql.connect(...)与pymysql.connections.Connection(...)实际指向同一个类。同时模块顶部声明的 DB-API 兼容性元数据也值得注意apilevel 2.0、threadsafety 1、paramstyle pyformat即 PyMySQL 遵循 Python DB-API 2.0 规范docs/source/modules/index.rst 中也指引读者参考 PEP 249使用%(name)s风格的 pyformat 参数占位。构造参数全景一个可复制的完整示例Connection.__init__的全部参数在 pymysql/connections.py 中定义其 docstringconnections.py即为官方文档的 autodoc 内容来源。下面先给出一份覆盖主要参数的完整、可直接运行的连接示例import pymysql conn pymysql.connect( host127.0.0.1, # 数据库服务器地址 userapp_user, # 登录用户名 passwords3cret, # 密码 databaseshop, # 默认数据库可省略之后用 select_db 切换 port3306, # 端口默认 3306 charsetutf8mb4, # 字符集默认 utf8mb4 collationutf8mb4_0900_ai_ci, # 排序规则可选 connect_timeout10, # 建连超时默认 10 秒 read_timeoutNone, # 读超时None 表示不设超时 write_timeoutNone, # 写超时None 表示不设超时 autocommitFalse, # 默认关闭自动提交 cursorclasspymysql.cursors.Cursor, # 游标类默认为 Cursor client_flag0, # 自定义客户端标志见 constants.CLIENT local_infileFalse, # 是否启用 LOAD DATA LOCAL max_allowed_packet16 * 1024 * 1024, # 发送给服务端的包上限默认 16MB use_unicodeTrue, # 默认返回 Unicode 字符串 init_commandNone, # 建连后执行的初始 SQL sql_modeNone, # 建连后设置的 SQL_MODE defer_connectFalse, # True 则延迟到显式调用 connect() ) cur conn.cursor() cur.execute(SELECT id, name FROM users WHERE id %s, (1,)) print(cur.fetchone()) cur.close() conn.close()参数速查表参数默认值说明hostlocalhost服务器主机名或 IP传unix_socket时走本地套接字user当前系统用户名getpass.getuser()登录用户名password密码内部以 latin1 编码为字节串database/dbNone默认库db为已弃用别名port3306TCP 端口必须为 int否则抛ValueErrorunix_socketNoneUNIX 域套接字路径优先于 TCPbind_addressNone多网卡时指定出站接口主机名或 IPread_timeout/write_timeoutNone读写超时秒None不设超时必须 0charsetutf8mb4连接字符集推荐 utf8mb4collationNone排序规则sql_modeNone建连后通过SET sql_mode应用read_default_fileNone读取[client]等节下的 my.cnf 参数read_default_groupNonemy.cnf 中读取的节名默认clientconvconverters.conversions自定义类型转换字典use_unicodeTrue是否返回 Unicode 字符串client_flag0追加的客户端能力标志见constants.CLIENTcursorclassCursor默认游标类init_commandNone建连成功后执行的初始 SQLconnect_timeout10建连超时秒合法区间 (0, 31536000]autocommitFalse自动提交None表示采用服务端默认local_infileFalse开启后自动追加CLIENT.LOCAL_FILES标志max_allowed_packet16 * 1024 * 1024发送数据包上限主要限制 LOAD LOCAL INFILE 分片defer_connectFalse延迟建连稍后手动调用connect()auth_plugin_mapNone自定义认证插件处理器映射实验性server_public_keyNoneSHA256 认证插件的服务端公钥ssl/ssl_ca/ssl_cert/ssl_key/ssl_key_password/ssl_verify_cert/ssl_verify_identity/ssl_disabled见下节TLS 配置program_nameNone写入连接属性的程序名compress/named_pipeNone不支持传入会直接抛NotImplementedErrorbinary_prefixFalse已弃用passwdNone已弃用password的别名关键参数的源码级细节字符集与编码self.charset charset or DEFAULT_CHARSETDEFAULT_CHARSET utf8mb4并通过charset_by_name(self.charset).encoding确定 Python 侧的编解码器。docstring 特别提醒遗留的多字节编码可能存在安全风险不建议用于公网系统。超时校验connect_timeout必须满足0 connect_timeout 31536000否则抛ValueErrorread_timeout、write_timeout若给定则必须 0。超时最终作用于底层socket.settimeout()见connect()与_read_packet()/_write_bytes()。自动提交autocommitFalse只是构造时的初始值autocommitNone则不动服务端使用服务器默认。真正生效逻辑见后文“事务控制”。转换器conv未传时使用converters.conversions随后被拆分为self.encoders键为非 int 类型与self.decoders键为 int 字段类型码这同时服务于 MySQLdb 兼容性。弃用别名传入db/passwd会触发DeprecationWarningdb is deprecated, use database并自动回填到新参数。不支持的参数compressTrue或named_pipeTrue直接抛NotImplementedError避免静默失败。连接建立流程connect() 内部发生了什么当defer_connectFalse时构造器末尾直接调用self.connect()。connect()connections.py的完整流程如下建立传输层连接若指定unix_socket创建AF_UNIX套接字并sock.connect(unix_socket)host_info记为Localhost via UNIX socket否则用socket.create_connection((host, port), connect_timeout)建立 TCP 连接bind_address通过source_address注入随后设置TCP_NODELAY与SO_KEEPALIVE两个套接字选项host_info记为socket host:port。读取服务端信息_get_server_information()解析握手包填充protocol_version、server_version、server_thread_id、salt、server_capabilities、server_status、server_charset以及服务端声明的默认认证插件名_auth_plugin_name。认证握手_request_authentication()按client_flag组装 HandshakeResponse 数据包根据认证插件计算认证响应详见“认证插件”一节。同步字符集与排序规则调用set_character_set(self.charset, self.collation)发送SET NAMES。源码注释解释了原因握手包中的collation_id可能被忽略且不同服务端对同一字符集的默认排序规则不同如 utf8mb4 在 MySQL 5.7 默认utf8mb4_general_ci、MySQL 8.0 默认utf8mb4_0900_ai_ci显式SET NAMES可保证一致。应用会话参数若传了sql_mode执行SET sql_mode%s若传了init_command执行该初始 SQL若autocommit_mode非None调用autocommit()同步服务端状态。任何一步失败都会进入except BaseException分支调用_force_close()清理资源OSError 会被转换为带CR.CR_CONN_HOST_ERROR错误码的OperationalError错误码定义见 pymysql/constants/CR.py并保留原始异常的original_exception与traceback属性便于排查。defer_connect延迟建连的使用场景conn pymysql.connect(useru, passwordp, defer_connectTrue) # ... 做一些准备工作例如注册信号处理器、设置超时... conn.connect() # 显式建立真实连接在__init__中defer_connectTrue时self._sock None跳过connect()之后手动调用conn.connect()才真正建连。连接生命周期open、close、ping 与上下文管理器open 属性与 close()print(conn.open) # 连接未关闭时为 True即 _sock 非 None conn.close()close()会先向服务端发送COM_QUIT报文struct.pack(iB, 1, COMMAND.COM_QUIT)再关闭底层文件对象与套接字若连接已关闭再次调用close()会抛err.Error(Already closed)。与之相对的_force_close()是内部方法直接关闭套接字而不发送 QUIT也用作__del__兜底。上下文管理器Connection实现了__enter__/__exit__connections.py因此可以这样写with pymysql.connect(host127.0.0.1, useru, passwordp) as conn: with conn.cursor() as cur: cur.execute(SELECT 1) print(cur.fetchone()) # 退出 with 块时 conn.close() 被自动调用注意__exit__无条件调用close()不会自动提交或回滚事务性工作仍需显式commit()/rollback()。ping()探测连接是否存活ping()通过COM_PING命令检查服务端是否可达成功则读取 OK 包。reconnect参数已弃用源码中只要传入即发出DeprecationWarning提示“Create a new connection if you want to reconnect”。实现上若连接已关闭且reconnectFalse会抛err.Error(Already closed)异常发生时若reconnectTrue会重建连接后再次 ping。建议的新写法try: conn.ping() except pymysql.Error: conn pymysql.connect(...) # 重建连接事务控制autocommit、begin、commit、rollbackconn.autocommit(True) # 开启自动提交等价于 SET AUTOCOMMIT 1 print(conn.get_autocommit()) # 读取服务端 server_status 中的 AUTOCOMMIT 位 conn.begin() # 发送 BEGIN # ... 执行若干 DML ... conn.commit() # 发送 COMMIT conn.rollback() # 发送 ROLLBACK实现细节autocommit(value)先更新self.autocommit_mode再用get_autocommit()读取服务端状态SERVER_STATUS_AUTOCOMMIT位常量见 pymysql/constants/SERVER_STATUS.py仅在值不一致时才发送SET AUTOCOMMIT %s减少不必要的往返。begin()/commit()/rollback()全部走_execute_command(COMMAND.COM_QUERY, ...)_read_ok_packet()即直接发送对应 SQL 文本。_send_autocommit_mode中的self.escape(self.autocommit_mode)会得到1/0这样的 SQL 字面量。会话级操作select_db、show_warnings、set_character_set 等方法作用底层命令select_db(db)切换当前数据库COM_INIT_DBshow_warnings()返回SHOW WARNINGS结果行COM_QUERYset_character_set(charset, collationNone)发送SET NAMES ... [COLLATE ...]并更新编码COM_QUERYset_charset(charset)set_character_set的弃用别名触发 DeprecationWarning—kill(thread_id)终止指定线程 ID 的查询KILL idCOM_QUERYaffected_rows()最近一次查询影响的行数—insert_id()最近一次 INSERT 的自增 ID—thread_id()服务端会话线程 IDserver_thread_id[0]—character_set_name()当前连接字符集名—get_host_info()/get_proto_info()/get_server_info()主机信息 / 协议版本 / 服务端版本—例如切换字符集conn.set_character_set(utf8mb4, utf8mb4_0900_ai_ci)set_character_set先通过charset_by_name校验字符集存在再发送SET NAMES {charset} COLLATE {collation}无 collation 则省略 COLLATE 子句最后同步self.charset与self.encoding。游标创建与转义机制cursor(cursorNone)用于创建游标connections.pycur conn.cursor() # 使用 cursorclass默认 Cursor cur conn.cursor(pymysql.cursors.DictCursor) # 显式指定游标类型cursorclass默认值为 pymysql/cursors.py 中的Cursor常用的还有SSCursor服务端无缓冲、DictCursor、SSDictCursor参见 docs/source/modules/cursors.rst。escape(obj)是内部转义工具官方文档将其排除在公开成员之外docstring 亦注明 Non-standard, for internal use字符串被转义为单引号包裹的字面量字节串被转义为Xhex形式其他对象交给converters.escape_item结合self.encoders处理。literal(obj)是它的弃用别名源码明确警告“will be removed in the next version”。应用代码应始终使用参数化查询pyformat 风格%s而非手工拼装 SQL。从 my.cnf 读取连接参数read_default_file与read_default_group允许复用 MySQL 客户端配置文件如/etc/my.cnf。源码逻辑connections.py未显式传read_default_file而只传了read_default_group时Windows 默认c:\my.ini其他平台默认/etc/my.cnf未指定read_default_group时默认取client节使用 pymysql/optionfile.py 中的Parser继承configparser.RawConfigParser解析文件命令行参数优先于配置文件中的同名键。optionfile.Parser有两个值得注意的行为optionxform将键名小写并把下划线转换为连字符所以配置里写default-character-setget会剥离首尾匹配的单双引号。示例~/.my.cnf[client] host 127.0.0.1 user app_user password s3cret port 3306 default-character-set utf8mb4 socket /var/run/mysqld/mysqld.sock ssl-ca /etc/mysql/certs/ca.pem对应代码conn pymysql.connect(read_default_file~/.my.cnf, read_default_groupclient)此时ssl配置也可从配置文件的ssl-ca、ssl-cert、ssl-key、ssl-password、ssl-cipher等键读取。测试用例 pymysql/tests/test_optionfile.py 对该行为有专门覆盖。SSL/TLS 连接三种配置方式与 REQUIRED/PREFERRED 模式Connection支持三种 SSL 配置方式ssl.SSLContext对象直接传入sslctx最灵活独立ssl_*参数ssl_ca、ssl_cert、ssl_key、ssl_key_password、ssl_verify_cert、ssl_verify_identity源码会将其组装为内部 dictssl字典docstring 明确标注“Passing a dict is deprecated”建议改用上述两种方式。import ssl ctx ssl.create_default_context(cafile/etc/mysql/certs/ca.pem) ctx.check_hostname True ctx.verify_mode ssl.CERT_REQUIRED conn pymysql.connect( hostdb.example.com, useru, passwordp, sslctx, )或使用独立参数conn pymysql.connect( hostdb.example.com, useru, passwordp, ssl_ca/etc/mysql/certs/ca.pem, ssl_cert/etc/mysql/certs/client-cert.pem, ssl_key/etc/mysql/certs/client-key.pem, ssl_verify_certTrue, ssl_verify_identityTrue, )关键实现细节_create_ssl_ctx未显式提供 CA 时使用ssl.create_default_context()系统信任库显式提供 CA 后默认开启CERT_REQUIRED与check_hostname由于 Python 3.13 默认启用VERIFY_X509_STRICT而 MySQL 自动生成的自签名证书无法通过该校验源码会主动清除该标志ctx.verify_flags ~ssl.VERIFY_X509_STRICT强制禁用 SSLv2/SSLv3OP_NO_SSLv2、OP_NO_SSLv3ssl_disabledTrue会明确禁止使用 TLS即使服务端支持也不协商。REQUIRED 与 PREFERRED 模式源码中存在两种 TLS 协商模式connections.py、connections.pyREQUIRED显式传入任一 SSL 选项时client_flag | CLIENT.SSL_ssl_requiredTrue若服务端不支持 SSL认证阶段直接抛CR.CR_SSL_CONNECTION_ERRORSSL is required but the server doesnt support it。PREFERRED未指定任何 SSL 选项且 Python 可用ssl模块时同样创建 SSL 上下文但_ssl_requiredFalse握手时若服务端未通告CLIENT.SSL能力则优雅回退到明文连接。TLS 升级发生在握手响应阶段先发送不含认证数据的 HandshakeResponse 帧再用self.ctx.wrap_socket(...)包裹套接字server_hostnameself.host用于 SNI 与主机名校验随后继续发送完整认证数据。相关测试见 pymysql/tests/test_connection.py 中的test_ssl_connect、test_ssl_required_error、test_ssl_preferred_no_server_ssl。认证插件握手状态机与 auth_plugin_map认证过程是一个循环状态机_request_authentication/_process_authconnections.py服务端可能下发 AuthSwitchRequest0xfe、额外认证数据包如caching_sha2_password的快速路径失败后要求公钥或最终 OK 包客户端持续分派直到收到终止包。内置支持的插件见 pymysql/_auth.py插件说明mysql_native_passwordSHA1 加扰scramble_native_password经典 MySQL 5.x 默认caching_sha2_passwordMySQL 8.0 默认先尝试快速路径失败后走 RSA 公钥加密或 TLS 通道需cryptography包否则按_have_cryptography分支处理sha256_password通过 TLS 明文发送或请求服务端公钥后 RSA 加密mysql_clear_password明文传输仅建议在安全通道内使用client_ed25519MariaDB 的 Ed25519 签名认证依赖pynacl未安装时抛 RuntimeErrormysql_old_password旧式认证dialog服务端交互式提示认证auth_plugin_map允许为插件注入自定义处理器类实验性类的构造器接收Connection对象需实现authenticate(auth_packet)方法dialog插件也可通过prompt(echo, prompt)返回用户输入见 connections.py。server_public_key参数则用于在非 TLS 环境下提供sha256_password/caching_sha2_password所需的服务端公钥。底层协议机制数据包分帧与大 SQL 拆分Connection内部通过write_packet/_read_packet/_execute_command与 MySQL 协议交互理解这些有助于诊断大查询与超时问题包格式每个 MySQL 数据包由 3 字节小端长度 1 字节序列号 载荷组成。MAX_PACKET_LEN 2**24 - 1约 16MB是单包上限_read_packet在读到不足 16MB 的短包前会循环拼接多个分片协议规定超大结果按 16MB 分片发送。序列号校验_read_packet会校验包序列号与_next_seq_id一致不一致时强制关闭连接并抛InternalError服务端停机时序列号 0 的包则报CR_SERVER_LOST。大 SQL 拆分_execute_command首包携带命令字节 最多 16MB 数据超出部分继续以write_packet分片发送。读超时优化_read_packet仅在超时配置变化时才调用sock.settimeout()注释明确说明这是为避免高频调用 setattr 在释放 GIL 时拖慢多线程应用的性能。连接丢失语义_read_bytes/_write_bytes捕获 OSError 后统一转换为CR.CR_SERVER_LOST或CR.CR_SERVER_GONE_ERROR的OperationalError并执行_force_close()。MySQLResult类connections.py负责结果集读取区分 OK 包、LOAD LOCAL 包与结果集包unbuffered模式下逐行读取供SSCursor使用_get_descriptions依据字段类型选择解码编码TEXT 类按连接编码、二进制按原样、数值/日期按 ascii并挂接self.decoders中注册的类型转换器。异常体系Connection 上的错误类虽然官方 autodoc 把DataError、DatabaseError、Error、InterfaceError、IntegrityError、InternalError、NotSupportedError、OperationalError、ProgrammingError、Warning排除在Connection文档成员之外但它们在源码中均以类属性挂载在Connection上connections.py全部来自 pymysql/err.py。这意味着try: conn.cursor().execute(sql) except pymysql.err.OperationalError as e: print(e.args[0]) # 数字错误码如 CR.CR_SERVER_GONE_ERROR这是 MySQLdb 兼容接口的一部分便于conn.Error、conn.OperationalError等写法直接可用。实测佐证与最佳实践小结仓库测试 pymysql/tests/test_connection.py 覆盖了autocommit、select_db、连接断开test_connection_gone_away、SSL 三态、多结果集提交等场景可作为验证上述行为的参考。综合全文生产环境使用Connection的推荐姿势显式管理生命周期优先使用with上下文管理器或保证try/finally中调用close()连接复用长连接时用ping()探测存活且不要依赖已弃用的reconnect参数断开后直接重建连接。字符集一律 utf8mb4默认值已是utf8mb4并配合collation参数显式指定排序规则避免服务端版本差异带来的默认排序不一致。SSL 按需开启公网环境至少设置ssl_verify_certTrue追求严格校验再叠加ssl_verify_identityTrue使用SSLContext可以更精确地控制 CA 与校验模式。SQL 永远参数化不要手写字符串拼接使用cursor.execute(sql, params)的 pyformat 风格escape/literal是内部工具不应在应用代码中调用。关注超时三件套connect_timeout控制建连、read_timeout/write_timeout控制读写事务操作务必匹配autocommit设置避免隐式提交导致的脏数据。赞分享数据库数据库客户端后端【免费下载链接】PyMySQLMySQL client library for Python项目地址https://gitcode.com/gh_mirrors/py/PyMySQL点击查看免费下载相关推荐彻底搞懂LiveKit参与者状态机从连接到断开的全生命周期管理彻底搞懂LiveKit参与者状态机从连接到断开的全生命周期管理 你是否曾遇到过视频会议中用户状态异常、连接不稳定的问题作为实时音视频RTC系统的核心参后端音视频即时通讯AI Agentquiche连接状态机QUIC协议状态转换全解析quiche连接状态机QUIC协议状态转换全解析 QUICQuick UDP Internet Connections协议作为新一代传输层协议以其低延迟网络通信后端Relay GraphQL Server Specification 完全指南Node 对象重取与 Connection 连接分页协议Relay GraphQL Server Specification 完全指南Node 对象重取与 Connection 连接分页协议 导读 本文以 Rela前端开发工具上一篇把伴奏从歌曲里拆出来免费 AI 音频分离工具 Ultimate Vocal Remover 完整指南下一篇AListFlutter完整指南无需Root的Android文件管理神器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考