一万小时 / 将静态数据存储在数组中还是数据库中

远没到拼智商的时候

将静态数据存储在数组中还是数据库中

2009-11-05 posted in [点滴技术] with tags: []

将静态数据存储在数组中还是数据库中?

本博客所有内容采用 Creative Commons Licenses 许可使用. 引用本内容时,请保留 朱涛出处 ,并且 非商业 .

点击 订阅 来订阅本博客.(推荐使用 google reader, 如果你的浏览器不支持直接订阅,请直接在 google reader 中手动添加).

下载pdf 阅读.

摘要

在web开发中你可能会遇到一些静态数据的处理问题, 如何存放这些静态数据这个问题你可能从来没有思考过, 但是它确实是个比较重要的问题.

本文就究竟要存放在文件中的数组还是应该存放在数据库表中进行一些讨论.

引入

最近在做一个游戏的后端架构, 很常见的一个任务系统需求的场景如下:

用户只有达到一定的要求才能开始某个任务,完成这个任务后,会有一些奖励.
这里的要求和奖励比较复杂.

如果用数组表示(下文不区分数组和python中的字典类型,统称为数组),可以有如下的代码:

Task = {
    "1" : {
        "name" : "Task name",
        "description" : "some description",
        "requirement" : {
            "level" : 5,
            "money" : 100,
            ...
        },
        "reward" : {
            "money" : 250,
            "experience" : 50,
            ...
        }
        ...
    }
    ...
}

其中可以看到, requirement和reward是2个比较复杂的数据结构,而非简单的字符串. 那么这时你会想,这个静态数据究竟应该存储在文件中的数组(如上面的结构)还是存储在 数据库的表中?各有什么优劣呢?

请继续阅读.

使用数据库表

这是我们的第一个方案, 我们可以存储在数据库表中.

那么这里会出现一个问题, 就是类似于上例中requirement这样比较复杂的数据结构该如何存储呢?

当然,你会说, 这难不倒我, 我们可以使用序列化后的对象, 如python中的pickle来对对象 进行序列化,然后存储在表项中, 在读取时直接可以转换为对象来使用.

这种方案是没有问题的, 但是随之而来会有一个比较难处理的问题, 因为我们这里提到的静态数据 通常是已经设定好的数据, 而在运行时是不会更改的,那么我们就必须在初始化时要构建好这些数据. 所以,会面临着把这些初始化到数据库的操作.

为了达到这个目的,你可以选择sql语句, 或者django框架中的fixture等,但是逃不脱的是你需要手动输入 序列化好的对象.

例如, 对象 a = {"name":"zhutao", "gender" : "male"} 序列化后(pickle)的结果(记为b)为:

(dp0
S'gender'
p1
S'male'
p2
sS'name'
p3
S'zhutao'
p4
s.

你会看到是一串人类无法容易识别的字符串, 那么我们手动输入就更加困难, 当然你可以,先把结构写好然后将序列化后 的字符串直接拷贝到sql中, 没有问题, 不过,相当麻烦. 想像下,如果我们后面要更改这个结构,我们需要:

更改数据结构=>将更改后的数据结构序列化=>将得到的序列化字符串拷贝到sql中

而更多的环节意味着更多的出错可能.

当然, 这样序列化后的数据也很容易转换为原始的对象, 例如:

c = pickle.loads(b)
#这时c是a的一份完全的拷贝

使用数组

我们来看解决方案二, 使用静态文件的数组.

那么对应的代码可以很方便地使用类数组的结构, 这些代码可以存储在文件中, 其它代码也可以很方便地操作这个数据结构来获得相应的数据.

这样的代码都是文本的人类易读,易更改的内容,所以即使后面要更改,也只需要:

修改静态数组 => (没有下一个操作了)

简单的步骤意味着更低的出错机会, 也为我们的代码提供了更好的健壮性保证.

优劣分析

那么从上面的分析来看,似乎我们会毫无迟疑地选择数组, 其实不然,我们需要认真地分析应用场景 及这两种方式的优劣,从而决定自己的方案.

使用数据库

显然的优势包括:

  1. 统一的持久化接口层(我们将统一地使用数据库作为数据的持久化载体)
  2. 扩展性(如果静态数据比较大, 那么使用数据库可以将表进行分割,置于不同的服务器上, 显然数组不能)
  3. 数据库已经为某些常用的查询操作提供了很大的优化(如果使用数组,我们得自己实现)
  4. 并发和性能上问题(如果静态数据可能在运行时修改,则会存在写的竞争,而有可能导致数据不一致,数据库已经很好地进行了处理,使用数组则得自己处理)

劣势包括:

  1. 修改起来麻烦(参考上面的说明)
  2. 性能(虽然数据库可以cache,不过数组可以直接载入内存,从而极大地提高效率)
  3. 表达能力有限(不能表达比较复杂的逻辑关系,如例子中的requirement, 当然如果真要表达,则会带来一定的问题,请参考例子)

使用数组

根据上面的分析, 显然的优势包括:

  1. 修改方便, 维护容易
  2. 性能上的优势(一次性载入内存)
  3. 代码处理比较方便(无需写sql查询,直接可以操作本地的数据结构)
  4. 可以容易地表达比较复杂的数据关系

劣势包括:

  1. 数组不能太大(太大会造成内存的极大压力)
  2. 扩展性(不能分割至不同的服务器)

如何选择方案

那么我们该如何选择具体的方案呢?

那我们要根据具体的应用场景来看, 如果应用具有如下的特征,我们要更倾向于数组:

  1. 运行时不会更改(也就是说只读)
  2. 逻辑关系比较复杂的数据(例如例子中的requirement和reward)
  3. 规模较小

而除此而外的其它情形, 我们都要选择数据库.

从本质而言,数据库中的表也是存储在本地的文件中(B树等),当然它在数据结构上做了 极大的优化,所以相比于数组, 它会在一些常见问题的处理上会有更好的优势,如 并发, 数据一致性, rollback等. 当然,如果你要达到同样的功能,则需要程序员来自行封装, 而这个几乎很难完成.

所以,我们在除了上面提到的场景中需要使用数组外,其它应用场景我们应该毫无迟疑地选择数据库.

后记

在引入部分提到的实际项目中, 我最终还是选择了数组的方式, 也主要是因为正好满足了上面提到的条件.

在整个决策过程中, 同事有建议统一用数据库的, 当然最终我还顺利说服了对方.

除此,你还可以参考这里的 讨论 , 这是我昨天刚在 SO 上发起的一个讨论.

欢迎留言和讨论.

本文的rst源码

本文的源码链接在 这里 .

下载pdf 阅读.

comments powered by Disqus
Top

Press q to hide the help

Key Action Key Action
Small Scroll j Scroll Down k Scroll Up
Big Scroll b Scroll to Bottom t Scroll to Top
Post Navigation n Next Post(if exists) p Previous Post(if exists)
Page Navigation h Go to Blog's Home Page a Go to Blog's Archive Page
Page Navigation c Go to Blog's Category Page ? Show this help
Misc s Go to Search Box q hide the help