如何将Python字典储存到MySQL数据库中?

7

我查看了几个SO问题,了解如何pickle一个python对象并将其存储到数据库中。我收集到的信息是:

  • import pickleimport cpickle。如果性能是一个问题,请导入后者。
  • 假设dict是一个Python字典(或其他任何Python对象):pickled = pickle.dumps(dict)
  • 使用任何通信模块将pickled存储到MySQL BLOB列中。
  • 再次获取它。并使用pickle.loads(pickled)来恢复Python字典。

我只想确保我理解得正确。我有什么遗漏的关键点吗?是否有副作用?它真的那么简单吗?

背景信息:我想要做的唯一一件事就是存储Googlegeocoder响应,在我的情况下,这些响应是嵌套的python字典。我只使用响应对象的一小部分,我不知道以后是否会需要更多。这就是为什么我考虑存储响应以节省我重复一些百万查询的时间。


3
只有Python会将“pickle”作为关键字使用... #哈哈。 - Tieson T.
我会将对象序列化为JSON,而不是pickle,因为JSON更易于阅读,并且在其他编程语言中也更容易阅读。性能差异并不大。 - Mikko Ohtamaa
3个回答

2

只要你的数据库不需要了解字典内容,这就变得非常容易。如果你需要对字典内容进行任何结构化数据访问,那么你就需要更深入地了解它。

另一个需要注意的是你打算将什么放入字典中。Python的pickle序列化非常智能,可以处理大多数情况而无需添加自定义支持。然而,当它无法正常工作时,很难理解到底出了什么问题。因此,如果可能的话,请将字典内容限制为Python内置类型。如果你开始添加自定义类的实例,请保证它们是简单的自定义类,不会对属性存储或访问进行任何有趣的操作。同时要注意不要添加来自附加组件的类或类型实例。总之,如果你开始遇到难以理解的pickle或unpickle问题,请查看字典中的非内置类型。


我只需要处理一些嵌套的列表/字典,而数据库不必知道这些对象的任何信息。所以我认为在这种情况下我会没问题的。感谢您的回答! - Aufwind
关于可能出现的问题,请问一下,可以被pickle的内容有哪些限制? - Thomas Kimber

1
如果速度真的很重要,我刚刚进行了一个测试,从pickle中加载一个大型Python字典(35MB),与从MySql表中选择所有键和值存储在行中相比:
Pickle方法:
import time, pickle
t1 = time.clock()
f = open('story_data.pickle','rb')
s = pickle.load(f)
print time.clock() - t1

MySQL 方法:

import database as db
t1 = time.clock()
data,msg = db.mysql(""" SELECT id,story from story_data;""")
data_dict = dict([(int(x),y.split(',')) for x,y in data])
print time.clock() - t1

输出: pickle方法:32.0785171704 mysql方法:3.25916336479
如果十倍的速度提升足够了,那么数据库的结构可能并不重要。请注意,我将所有逗号分隔的数据拆分为列表作为36,000个键的值,仍然只需要3秒钟。因此,我已经停止使用pickle来处理大型数据集,因为我正在使用的其余400行程序大约需要3秒钟,而pickle加载需要32秒钟。
还要注意:
cPickle的工作方式与pickle完全相同,并且速度快50%以上。
不要尝试将充满字典的类pickle并保存在mysql中:它无法正确地重新组合自己,至少对我来说是这样。

1
注意 - 通过去掉我存储的每个列表中每个字符串周围的引号,我将数据大小从35MB缩小到24MB,这可能也有助于加速MYSQL方法。 Python非常快地将它们添加回来并从逗号分隔的数据重建列表。 - Marc Maxmeister

0

如果您有嵌套的字典,您必须小心。大多数Python对象不会pickle(并且您可以将任何对象作为值塞入dict中)。更糟糕的是,即使是更少的Python对象也无法转换为字符串并存储在SQL中。

然而,如果您使用klepto,序列化和存储在数据库中就非常透明,并且适用于大多数Python对象。

让我们在dict(或字典)中构建一些典型的Python对象:

>>> class Foo(object):                                 
...   def bar(self, x):
...     return self.y + x
...   y = 1
... 
>>> d1 = {'a': min, 'b': lambda x:x**2, 'c': [1,2,3], 'd': Foo()}
>>> f = Foo(); f.y = 100
>>> d2 = {'a': max, 'b': lambda x:x**3, 'c': [2,1,3], 'd': f}

现在,让我们构建一个嵌套的dict,并将其转储到MYSQL存档中。
>>> import klepto
>>> a = klepto.archives.sql_archive('mysql://user:pass@localhost/foo', dict={'d1':d1, 'd2':d2})
>>> a.dump()

现在,我们删除了与存档相关的接口...并建立了一个新的接口。 load 将所有对象加载到内存中。

>>> del a
>>> b = klepto.archives.sql_archive('mysql://user:pass@localhost/foo')
>>> b.load()

我们现在访问内存中的对象副本。

>>> b['d1']
{'a': <built-in function min>, 'c': [1, 2, 3], 'b': <function <lambda> at 0x1037ccd70>, 'd': <__main__.Foo object at 0x103938ed0>}
>>> b['d1']['b'](b['d1']['d'].bar(1))
4
>>> b['d2']['b'](b['d2']['d'].bar(1))
1030301
>>> 

我们退出Python…然后启动一个新会话。这一次,我们决定使用 cached=False,以便直接与数据库交互。

dude@hilbert>$ python
Python 2.7.10 (default, May 25 2015, 13:16:30) 
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import klepto
>>> b = klepto.archives.sql_archive('mysql://user:pass@localhost/foo', cached=False)
>>> b['d2']['b'](b['d2']['d'].bar(1))
1030301
>>> b['d1']['b'](b['d1']['d'].bar(1))
4
>>> 

klepto 利用 sqlalchemy,因此它可以在多个数据库后端上运行...并且还提供了相同的基于 dict 的接口,以便将数据存储在磁盘上(文件或目录中)。


哦,是的,我是“klepto”的作者。 - Mike McKerns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接