为什么我们应该更喜欢将序列化数据而不是原始代码存储到DB?

时间:2009-11-06 04:03:16

标签: python database serialization

如果我们有一些应该存储在DB中的代码(数据结构),有人总是建议我们存储序列化数据而不是原始代码字符串。

所以我不太清楚为什么我们应该更喜欢序列化数据。

给出一个简单的实例(在python中):

我们有一个字段可以存储python的字典,比如

{ "name" : "BMW", "category":"car", "cost" : "200000"}

所以我们可以使用pickle(python模块)对其进行序列化,然后将pickle数据存储到db字段。

或者我们可以直接将dict字符串存储到DB而无需序列化。

由于我们需要将字符串转换为python数据,因此通过分别使用pickle.loads和exec,两种方法都很容易实现。

那么应该首选哪个?为什么?是因为exec比pickle慢得多吗?或其他一些原因?

感谢。

9 个答案:

答案 0 :(得分:10)

  

或者我们可以存储dict字符串   直接到DB而不进行序列化。

没有“dict字符串”这样的东西。有很多方法可以将字典序列化为字符串;你可能会想到repr,可能是eval作为获得词典的方法(你提到exec,但这简直荒谬:你会执行什么声明......?!我想你可能意味着eval)。它们是不同的序列化方法及其权衡,在许多情况下,权衡往往倾向于酸洗(cPickle,速度,协议-1意味着“你能做的最好”,通常)。 / p>

性能肯定是一个问题,例如,在您存储的大小方面......:

$ python -c 'import cPickle; d=dict.fromkeys(range(99), "banana"); print len(repr(d))'
1376
$ python -c 'import cPickle; d=dict.fromkeys(range(99), "banana"); print len(cPickle.dumps(d,-1))'
412

...为什么每次序列化像这样的dict时,你想要存储1.4 KB而不是0.4 KB? - )

编辑:因为有些人建议使用Json,所以值得指出json在这里需要1574个字节 - 比笨重的repr更笨重!

速度......

$ python -mtimeit -s'import cPickle; d=dict.fromkeys(range(99), "chocolate")' 'eval(repr(d))'
1000 loops, best of 3: 706 usec per loop
$ python -mtimeit -s'import cPickle; d=dict.fromkeys(range(99), "chocolate")' 'cPickle.loads(cPickle.dumps(d, -1))'
10000 loops, best of 3: 70.2 usec per loop

...为什么要花10倍的时间?支付这么高的价格有什么好处呢?

编辑:json需要2.7 毫秒秒 - 比cPickle慢几乎四十倍。

然后有一般性 - 并非每个可序列化对象都可以使用repr和eval正确地进行往返,而酸洗则更为通用。 E.g:

$ python -c'def f(): pass
d={23:f}
print d == eval(repr(d))'
Traceback (most recent call last):
  File "<string>", line 3, in <module>
  File "<string>", line 1
    {23: <function f at 0x241970>}
         ^
SyntaxError: invalid syntax

VS

$ python -c'import cPickle
def f(): pass
d={"x":f}
print d == cPickle.loads(cPickle.dumps(d, -1))'
True

编辑:就往返而言,json甚至不如repr。

因此,比较两种序列化方法(酸洗与repr / eval),我们看到:酸洗方式更为通用,它可以是例如快10倍,并采取例如数据库空间减少3倍。

您为repr / eval设想了哪些补偿优势??

顺便说一下,我看到一些答案提到安全性,但这不是一个真正的观点:酸洗也是不安全的(eval不可信任字符串的安全问题可能更明显,但是对不受信任的字符串进行取消也是不安全的,尽管在更微妙的情况下和更黑暗的方式)。

修改:json更安全。这是否值得在规模,速度和普遍性方面付出巨大代价,这是一个值得深思的权衡。在大多数情况下,它不会。

答案 1 :(得分:3)

存储为字符串和使用pickle都是序列化策略。 Pickle在其可存储的内容方面更加灵活,并且可以更紧凑。两种策略,eval(在这个实例中你将使用exec)和pickle.loads都是不安全的 - 这两种策略都可以运行任意Python代码。

更好的方法是使用像JSON这样的序列化格式(2.6中的json模块,2.6之前的simplejson第三方模块),它不是专门用于Python读取的,如果最终结果不会执行任意代码是您在数据库中不期望的数据。此外,虽然pickle格式可能会发生变化(以及您丢失的数据!),但像JSON这样的标准不会以向后不兼容的方式改变您。

答案 2 :(得分:3)

我更喜欢使用像JSON这样的标准序列化格式来将这种数据存储在数据库中。这使得数据的消费者可以使用除python以外的其他语言编写,它基本上是人类可读的,并且使用SQL比使用pickle对象更容易查询。

答案 3 :(得分:3)

如果我必须选择将数据序列化为JSON或存储pickle数据结构,我每次都会选择JSON选项。除了其他人提到的安全问题之外,可移植性是不在数据库中存储本机python对象的最大原因。将来可能需要将系统移植到其他语言,并且存储pickle python对象会使其变得相当困难。此外,其他应用程序可能需要访问您存储的数据,但由于我不了解您的情况,因此无法与特定实例通信。

此外,如果您的系统需要进行任何类型的过滤,那么将数据存储在JSON字符串中仍然不是您的最佳选择。如果可以的话,并且有一定数量的字段,我很想把它们分成原子元素。这将使搜索和过滤更容易和有效。

答案 4 :(得分:2)

问题是序列化会给你带来什么? 我敢打赌那些推荐你存储序列化数据的人认为你会节省时间,因为你不需要乱用SQL查询来构造Python对象。但是将数据存储为序列化blob存在一些重大的权衡,例如:

  • 您失去了参照完整性检查
  • 您选择的数据格式可能无法适用于不同的访问模式。如果所有数据都存储在序列化对象中,你将如何有效地获得价格超过20,000美元的所有汽车?
  • 如果您明显反对模型更改,您会怎么做?
  • 如果使用本机Python序列化格式,则会失去与其他语言的互操作性
  • 您必须编写代码支持代码以使用非本机Python序列化格式加载数据
  • 您无法使用第三方工具进行数据报告

列表一直在继续,请确保您可以通过这些权衡取舍。

答案 5 :(得分:1)

exec总是存在一种危险,即有人会以某种方式传递带有一些讨厌代码的字符串。在您的应用程序中可能永远不会出现这种情况,但总的来说,这是一个大问题,使用内置序列化可以避免它。

使用内置序列化的另一个原因是它使您在代码中尝试做的事情变得明显。如果您只是获取和执行,有人可能无法理解您的实际意图。

答案 6 :(得分:0)

首先,fetch和exec会使您的应用程序容易受到代码注入的影响。 如果有人输入“System(rm -r /);”在您的名字字段中,当您读入数据时,您将丢失* nix系统上的大部分文件。

第二个原因是可移植性和可升级性。 “pickled”对象可以在任何python平台上运行任何python版本 - Guido承诺!

第三,“pikling”将自动处理特殊字符和奇怪的代码页。因此,如果您的用户输入换行符或半冒号,则不会出现问题。

答案 7 :(得分:0)

序列化意味着更少担心。当您使用一些已知的序列化(Pickle,JSON,Google的协议缓冲区)对数据进行编组时,您可以相信以后检索的数据就是您之前存储的数据。 / p>

限制功能。如果您要存储静态数据,为什么要开放让代码执行的可能性?这是不必要的。想象一下,如果从现在开始,另一个程序员开始向这个“静态”数据添加函数和模块导入,将会出现复杂情况。

答案 8 :(得分:0)

如果有可能在数据库上操作此数据或从中创建报告;我会认真考虑将它拆包到桌子上。包含namekeyvalue列的简单表格可为您提供关系数据库的所有功能。根据编辑,它甚至可能比fetch-&gt; modify-&gt; dump。

更好