You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

向MariaDB存储/读取Pickle对象时遇UnpicklingError问题求助

Pickle序列化对象存入MariaDB后反序列化报错:_pickle.UnpicklingError: unpickling stack underflow

问题重现

执行读取操作时触发反序列化错误:

Traceback (most recent call last):
File "read_pickle_data.py", line 49, in <module>
   unpickled = pickle.loads(item[0].encode())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_pickle.UnpicklingError: unpickling stack underflow

环境信息:

  • 数据库:MariaDB
  • Python版本:3.11
  • Pickle协议:尝试过1、2及pickle.HIGHEST_PROTOCOL

错误原因

你的代码存在两个关键问题:

  1. 插入时错误处理二进制数据:手动使用conn.escape_string()处理pickle序列化后的二进制数据,再通过字符串拼接SQL语句,导致原始二进制中的特殊字符被转义(比如\n变成\\n),存储到数据库的已经不是完整的原始pickle数据。
  2. 读取时重复损坏数据:读取后又调用conn.escape_string(),再次转义已经被损坏的数据,还尝试用encode()处理,完全偏离了原始的pickle二进制格式。

从日志也能看出:插入后的car_obj_escaped已经把原始的\n转成了\\n,读取后的pickled_obj里的十六进制字符(比如\x01)变成了x01,数据结构完全被破坏,自然无法反序列化。

修复方案

1. 确保数据库字段类型正确

把存储pickle数据的字段car_pickled_object设置为BLOB类型(比如MEDIUMBLOB),专门用于存储二进制数据,避免字符编码转换导致的损坏。

2. 插入操作:使用参数化查询,避免手动转义

不要用字符串拼接SQL,改用MySQLdb的参数化查询,让驱动自动处理二进制数据的存储,不需要手动调用escape_string()。

3. 读取操作:直接使用原始二进制数据

读取后直接获取字段的二进制值,不需要任何额外的转义或编码操作,直接传给pickle.loads()。

修正后的完整代码

import MySQLdb
import pickle


class Car:
    def __init__(self, model, year, color):
        self.model = model
        self.year = year
        self.color = color

    def display_info(self):
        return f"{self.year} {self.color} {self.model}"


conn = MySQLdb.connect(host="<host>", port=<port>,
                       user="<user>", password="<passwd>", database="<db>")
cursor = conn.cursor()

task = "read"
if task == "insert":
    car = Car("123", 2024, "red")
    car_obj_serialized = pickle.dumps(car, protocol=pickle.HIGHEST_PROTOCOL)
    
    # 使用参数化查询,不需要手动escape
    query = """INSERT INTO car_models(car_pickled_object, status) VALUES(%s, %s);"""
    # 直接传入二进制数据作为参数
    cursor.execute(query, (car_obj_serialized, "Booked"))
    conn.commit()
else:
    query = """SELECT car_pickled_object FROM car_models WHERE id=10"""
    cursor.execute(query)
    row = cursor.fetchone()
    
    if row:
        # 直接获取二进制数据,不需要转义或encode
        pickled_obj = row[0]
        unpickled_obj = pickle.loads(pickled_obj)
        print(unpickled_obj.display_info())  # 验证反序列化结果

cursor.close()
conn.close()

验证说明

  • 插入时,car_obj_serialized是原始的pickle二进制数据,参数化查询会自动将其写入BLOB字段,不会做任何转义。
  • 读取时,从数据库获取的pickled_obj就是原始的二进制数据,直接用pickle.loads()即可成功反序列化出Car对象。

内容的提问来源于stack exchange,提问作者Sanket

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 07:33:17