我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。

MongoDB 使用经验

最初听说 MongoDB 的时候,我总是觉得它的稳定性堪忧,后来用了差不多一年的时间,其实也没有遇到过什么问题,反而是 MySQL 出现过几次丢失数据的情况。配合 Node.js 使用 MongoDB 是一件非常舒畅的事情,从前端,到后端,再到数据库,统统全是 JSON.

本文的定位是一篇对 MongoDB 的一个概览性的介绍,告诉你 MongoDB 的特点和功能,以及如果需要了解某个功能,应当搜索什么关键词,并不直接涉及技术细节。

特点

  • 无模式

    MongoDB 中的每一条文档,都是一个 JSON 对象,因此你无需预定义一个集合的结构,集合中的每个文档也可以有不同的结构。

  • 异步写入

    MongoDB 默认所有的写操作都是『不安全』的,即当请求被 MongoDB 收到时,不等写入操作完成,就返回一个『成功』的响应。
    这是默认的行为,当然你设置一些选项,让操作等待等待写入完成后再返回响应。不过对于大多数应用,这种『不安全』已经足够安全了。

  • 简单查询

    MongoDB 只支持简单的查询,MongoDB 只储存数据,更多的逻辑应该在应用中解决。因此 MongoDB 有着简单的且在各编程语言下高度一致的 API 接口。

  • 无需运维

    MongoDB 几乎没有什么选项可以设置,集群也是设置一次之后就可以自动地解决故障,极少需要维护。

安装和备份

MongoDB 的安装很简单,直接在官网下载二进制版本,运行其中的 mongod 即可启动数据库,运行 mongo 即可启动客户端 Shell, 或者你也可以从软件源中安装。
MongoDB 被设计运行于 64bit 的操作系统,在 32bit 的情况下,数据文件最大限制为 2GiB.

MongoDB 在运行时并不会实时地将修改写入磁盘,因此在关闭服务器时需要给 MongoDB 时间将所有数据写入磁盘。当出现服务器突然掉电的情况时,MongoDB 的数据库文件会损坏,需要进行修复才能重新运行,这个过程中会丢失掉电时正在进行的写入操作。在对运行中的 MongoDB 进行备份时,需要使用自带的 mongodump 工具,不能直接复制其数据库文件。

设计文档结构

  • ObjectID

    MongoDB 会为每一个文档默认添加一个名为 _id, 类型为 Object 的字段,这个字段用来唯一地标识每一个文档。这个 ID 通过时间,服务器,进程号被生成,甚至可以认为是全世界唯一的。
    除了 MongoDB 默认为 _id 添加 ObjectID, 你也可以自己在文档中创建 ObjectID, 来起到唯一地标识某个对象的功能。

引用关系

例如我们有一个 topic 集合,topic 都是 account 创建的,所以 topics 中要引用来自 accounts 中的 account.

// accounts
{
    _id: <ObjectID 1>,
    name: "jysperm"
}

// topics
{
    _id: <ObjectID 2>,
    account_id: <ObjectID 1>
}

嵌入关系

每个 topic 会有一些 reply, 所以在 topic 中可以嵌入 reply.

// topics
{
    _id: <ObjectID 2>,
    account_id: <ObjectID 1>

    replies: [
        {
            _id: <ObjectID 3>,
            content: "xxoo"
        }
    ]
}

引用 Vs 嵌入

在上面第一个例子中,topic 其实也可以嵌入 account 中;而第二个例子中,reply 也可以使用一个新的集合,然后来引用 topic, 那么应该如何选择这两种关系呢。

我们主要从几个出发点来考虑:

  • 查询

    我们可以考虑查询 account 时,是否需要他所有的 topic, 或者查询 topic 时,是否需要它所有的 reply.
    从查询的角度来考虑,如果需要一同获得这两种数据,那么就应该嵌入,如果不需要,就应该引用。

  • 数据的增长性

    我们还可以考虑 account 的 topic 数量在今后会有怎样的增长,topic 的 reply 数量会有怎样的增长。
    如果增长是没有限度的,那么就应该引用,如果增长是有限的,那么就可以嵌入。

  • 对应关系

    如果是一对一关系,或者一对多关系,那么可以考虑嵌入,如果是多对多关系,那么应该引用。

  • 原子性

    MongoDB 仅在文档层面提供原子性,如果有两个非常敏感的数据需要同时被更新,那么他们有必要存在于同一个文档中。

查询和更新

所谓『增删查改』在 MongoDB 里对应:

  • find/findOne: 查询
  • update/save: 修改
  • insert/save: 新增
  • remove: 删除

在 MongoDB 中,操作符以 $ 开头,主要分为三类:查询操作符,更新操作符,聚合查询操作符。

  • 查询操作符

    用于 find 和 update 中的查询器,如 $gt(大于), $ne(不等于), $in(匹配几个值之一), 逻辑运算:$and, $not.

  • 更新操作符

    用于 update 中的更新器,如 $inc(对数字进行增量), $set(修改文档的一部分), $unset(删除一个字段).
    MongoDB 对嵌入式的文档和数组有非常好的支持:$addToSet(向集合中添加元素), $push(向数组添加元素), $pull(从数组移除元素).

  • 聚合查询(Aggregation)

    类似于 SQL 数据库中的 GROUP, 提供统计和计算的功能,要多强大有多强大,毕竟可以直接在数据库中运行 JavaScript 代码,不过因为性能的关系,不适合在应用中频繁调用。

查询命令还有几个选项:

  • limit: 限制返回的结果数
  • skip: 跳过一些结果
  • sort: 对结果进行排序
  • fields: 只返回指定的字段

副本集(Replica Set)和分片(Sharding)

MongoDB 的副本集采用一主多从的的集群方式。副本集中只有主节点可以写入,其他节点从主节点同步。当主节点故障时,从节点中会自动推选出一个新的主节点。当故障的节点恢复后,会向其他节点同步到最新的数据,然后成为一个从节点。

MongoDB 的分片是指,按照某个字段的值,将数据分为多份,储存在不同的服务器上。客户端会运行一个 mongos 代替 mongod, mongos 相当于一个路由,会根据请求和分片的设置,将请求拆分后发给不同的服务器,得到结果后再将结果组合起来发给客户端。

分片可以与副本集同时使用,此时,每个分片都是一个副本级,这样可以提供非常高的可用率和扩展性。

东软游记

对于听成『东莞』的还情有可原,对于看成『东莞』的,我表示你已经无可救药了。

半个月前受邀去东软参观参观,分享分享经验。之前我只是听说过东软而已,只知道是一个很大规模的软件开发方面的企业,但不知道具体是做什么业务的。这次受到一个认识不久的朋友的邀请,所以稍微了解了一下东软,根据我了解到的情况,东软应该是一个非常大规模,比较传统的软件开发公司,主要是做外包相关的业务。

之前很多次听长辈说,你看你好好考个大学,你看东北大学就不错,我认识一个某某某,就是东北大学计算机专业毕业的,毕业之后就去了东软,一个月多少多少钱,混得相当不错啊。我收到邀请后的第一反应就是,以后再有人跟我这么讲,我就可以用非常装 B 的腔调说,东软啊,我去过,还给他们讲过课呢,不过如此嘛。

到东软的园区,感觉非常气派,面积非常大,花花草草,还有湖,还有几栋精致的楼,就好像一所大学一样,比我想象中的要好很多。这位朋友还给我介绍,东软的创始人是东北大学的学生,算是沈阳软件方面的龙头级企业,在全国几个城市都有园区,还有几所学校。除了外包也有很多其他方面的业务,很多都是走在全国前列的,而且目前也在做一些业务方面的转型。

到了以后,才知道居然有几十个人来听我讲。我之前准备不是很多,只是列了个提纲,考虑了一下都讲哪些话题,但对于具体讲什么,从哪开始,如何起承转合,完全没有准备。站在几十个人前面,真的有点小紧张,不知道该说什么,但说出第一句话之后,顿时就感觉好多了,一点点,想到什么就讲什么,竟然也很流畅,一共讲了一个半小时,房间挺大的,我也没用那个麦克风(也不知道好不好使), 嗓子都快冒烟儿了。

内容嘛,主要就是两个话题:『去中心化』和『Node.js』,没什么好说的,我天天闲得无聊都会在 QQ 群里就这些话题和群友们打口水仗,这次其实也没什么特别嘛,不过讲得一点都不过瘾——因为互动的人太少。

我本以为是会只有几个人,然后大家比较放松的聊一聊,会比较开心。

但是没想到只有我一个人在讲,就好像和平时学校上课一样,人数也差不多,都是四五十人,有一半人若有所思的样子,但完全不知道有没有听懂,还有少数能在关键处点点头,只有那么几个人,会提出一些问题。

最后我也留了联系方式,有几个人加了我的 QQ, 不过似乎没有人找我交朋友,我看到还有很多来听我讲的妹纸呢。

然后这位朋友跟我讲了他最近招聘的事情,为了找到更称职的人才,对招聘流程做的改进什么的。

我觉得面试最好的方式还是找技术人员来聊天,聊这个东西你听说过没有,那个东西你用过没有,曾经做过哪些项目,遇到过哪些困难。

当然,这样的成本实在太高了。笔试的成本低,但是我觉得起不到什么效果,包括上机也是一样。就比如我自己,我离开文档,离开网络是完全没办法写代码的,略微不太常用的 API, 我都会去文档确认一下参数顺序和注意事项什么的,有些自己写过的代码片段,会直接从以前的项目里复制过来,出了错误也是第一时间拖到 Google 上搜一下。

这位朋友提到,他所认识的大牛,是那种可以不借助 IDE, 不借助文档,完全没停顿的来写代码的人。但我概念中的大牛绝对不是这样子的,大牛应该是近乎全能的,他说他从未写过某种语言,但是因为工作需要,他第二天就能用这种语言写出看上去不错的代码,他绝对不会说『我没学过某种语言/技术』,而是在需要的时候,『不动声色』地就突然掌握了这种语言/技术。至于用不用 IDE, 看不看文档,whatever, 只要能快速地解决问题就是大牛。大概这就是我所谓的『传统编程』和『创业团队』的区别。

我以前也曾表示过,我更喜欢小的创业团队,而不是大公司。首先我是个挺自由散漫的人,受不了条条框框的规矩,所以结果导向的,对过程规矩很少的创业团队更适合我。同时我也觉得在小的创业团队中,我个人会显得非常重要,会让我更有斗志和危机感,不想去做规模化的企业中,可以被随意替代的积木。

很多位置稍微高一点的人,总是在强调,技术要为商业来服务。这句话说起来也没什么错误,但是你不能对所有人都这么讲,现代社会是有分工的,有些人专门负责商业,有些人专门负责技术,还有些人专门负责沟通前两类人。我只对技术方面感兴趣,如果可能的话,我希望能够将非技术的方面都交给专人来负责,比如运营,运维,甚至包括测试,我消消停停,只管写代码。

计算机方面经典书籍推荐

只推荐我看过的书,一共 13 本。

越基础,越通用的书顺序越靠前。

疯狂的程序员

小说,非常不错的小说。讲了主角在学校,公司,创业的故事,很真实,也很励志。

(Code) 编码——隐匿在计算机软硬件背后的语言

这是一本科普书,向非计算机行业的人解释计算机是如何工作的。

自底向上地描述了一台计算机的构造过程,从最基本的电路开始,逐步构造起一台真正可以使用的计算机。

黑客与画家

散文集,很多人推荐这本书,输出了有关设计,创业,财富,编程的价值观。

(CSAPP) 深入理解计算机系统

这本书讲述了在汇编的角度,C 语言是如何工作的,如何与操作系统交互的。

读完这本书,对计算机的大致结构,程序的执行过程会有更深入的了解。

计算机网络——自顶向下方法

虽然不如『TCP/IP 详解』那么权威,但是更通俗易懂一些,自顶向下来描述计算机网络的结构。

JavaScript 语言精粹

因为这本书只介绍 JavaScript 的精华部分,因此厚度是『JavaScript 权威指南』的六分之一。

编程珠玑

算法,告诉你如何写出性能更好,逻辑更清晰的代码,如何对算法进行优化,如何认识算法的复杂度。

重构

重构,将复杂的代码重构为更易读,更具可维护性的代码。

这本书还会为每个重构方法起一个名字,起到了一个『标准化』的作用。

(SICP) 计算机程序的构造和解释

通过 Scheme Lisp 语言来介绍编程语言所要实现的,抽象的本质。

C 和指针

深入讨论了 C 语言的指针和内存管理。

C++ 语言的设计和演化

从历史的角度介绍 C++ 中每一个特征的由来,和它的必要性。

(CPPPP6E) C++ Primer Plus

算是 C++ 的一本工具书,对 C++ 的几乎所有特征都有介绍,第六版中还有对 C++11 的介绍。

程序员的自我修养——链接装载与库

介绍在具体的操作系统实现中,程序如何被编译,装载,链接,以及标准库的实现。

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2025-02-10.