如何解决pymysql中query方法返回结果乱码的问题?

一、问题现象与本质分析

当开发者使用pymysql.connect()建立数据库连接后,执行cursor.query()cursor.execute()方法时,返回结果中经常出现类似æ–‡å—å˜åœ¨的乱码。这种现象的本质是字符集不匹配的三层问题:

  1. 客户端编码:Python环境的默认编码(通常UTF-8)
  2. 传输层编码:MySQL协议使用的编码(默认可能为latin1)
  3. 服务端编码:MySQL服务器配置的character_set_server

二、5种解决方案对比

1. 连接时显式指定字符集

conn = pymysql.connect(
    host='localhost',
    user='root',
    password='123456',
    database='test',
    charset='utf8mb4',  # 关键参数
    cursorclass=pymysql.cursors.DictCursor
)

2. 运行时动态设置编码

在已建立的连接上执行SQL命令:

cursor.execute("SET NAMES utf8mb4")

3. 数据库层面永久配置

修改MySQL配置文件my.cnf:

[client]
default-character-set=utf8mb4

[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

4. 结果集后处理解码

对获取的结果进行二次编码转换:

result = cursor.fetchall()
decoded_result = [dict((k, v.encode('latin1').decode('utf8')) 
                     for k, v in row.items()) 
                for row in result]

5. 使用连接池配置

在使用DBUtils等连接池时的配置示例:

pool = PooledDB(
    creator=pymysql,
    charset='utf8mb4',
    **other_params
)

三、深入原理:MySQL字符集体系

MySQL的字符集处理涉及四个关键参数:

参数名 默认值 影响范围
character_set_client latin1 客户端发送的SQL语句
character_set_connection latin1 服务器解析SQL时的转换
character_set_results latin1 返回结果的编码
character_set_database 继承server设置 新建表的默认编码

四、最佳实践建议

  • 始终使用utf8mb4替代旧的utf8(支持完整的Unicode包括emoji)
  • 在建立连接时显式声明charset参数
  • 对已有数据库使用ALTER DATABASE dbname CHARACTER SET utf8mb4转换
  • 检查Python文件头部是否包含# -*- coding: utf-8 -*-声明
  • 使用SHOW VARIABLES LIKE 'character_set%'诊断当前设置