关于文档型数据库的一点理解

本贴最后更新于 2759 天前,其中的信息可能已经时移世改

最近用 Node+MongoDB 写了一个论坛,期间遇到一些麻烦,也做了一些思考。在此记录一下。

问题

首先表结构是这样的:

User: {
  username: String,
  pass: String
}

Topic: {
  creator: ObjectId,
  tab: String,
  title: String,
  content: String
}

Reply: {
  creator: ObjectId,
  replyId: ObjectId,
  topicId: ObjectId,
  content: String
}

为了便于说明,这里做了一些简化

点开一个帖子,里面应该显示帖子的创建者以及所有回复。这时后台要做的事就很麻烦:

  • 查出帖子
  • 查出帖子的创建者姓名
  • 查帖子的所有回复
  • 查回复的创建者姓名

这里的逻辑写起来非常别扭。为了前台使用的方便,需要把 topic.creator 替换成 user 文档。而根据 mongoose 的设计,查出的数据和数据库里保持一致,如果改变就要 save,否则不能读取改变后的数据。所以这里都要调用 toObject 方法,才能将 topic.creator 替换成 user 文档,这样的话 mongoose 的封装就没有意义了。

再者,前台发出一个请求,后台就要和数据库交流这么多次,这是不合理的。这种拼接对象的逻辑也不是 control 层该干的事。

思考

这个论坛的代码结构很大程度上参考了 nodeclub 这个项目,而这个项目是很久之前的,以现在的角度看,它的设计未必没有问题。MongoDB 进阶模式设计这篇文章给了我很大的启示。

mongoDB 是文档型 nosql 数据库,选择了 mongoDB 就应该改变关系型数据的思维。数据不是一个个的有关系的实体,而是一些互相之间关联较少,内部结构复杂的文档。在 MongoDB 中,应该优先考虑内嵌,比如话题的创建者可以作为一个文档内嵌在话题中。


Topic: {
  creator: {
    _id: ObjectId,
    username: String
  }
  ...
}

这样做就能一次读取需要的信息,大大提高了读取性能,造成的后果是,改变用户名的代价变高了。而改变用户名的机会是比较少的,或者干脆不允许修改用户名问题也不大。所以这样做是比较划算的。

需要注意的是,mongoDB 对单个文档的大小是有限制的:16M。有的时候为了保持可扩展性,就不能使用内嵌。比如将回复看作话题的属性,那回复的内容和数量就会受到这个 16M 的限制,不能无限扩展了。

另外,帖子的回复数量和最后回复时间这样的信息可以从回复表中获取,但也可以看作帖子的一个属性,内嵌在 Topic 表中。这就有一个数据冗余的问题:话题上记录的回复数量和回复表中该话题的回复数量不一致怎么办。有两种办法:一是每次添加或删除回复都去更新 topic 表,二是认为帖子的回复数在一定时间内不会改变太多。比如将这个时间定为三个小时。

Topic: {
  replyCount: {
    timeStamp: Date,
    value: number
  }
  ...
}

读取回复数时,如果数据是三个小时之内的就认为数据是大致准确的,否则从回复表中读取数量。这样其实相当于一个缓存。

  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 269 回帖 • 28 关注
  • MongoDB

    MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

    90 引用 • 59 回帖 • 1 关注
  • 论坛
    31 引用 • 280 回帖 • 1 关注

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • 88250

    MySQL 加了 JSON 支持后也可以按照内嵌思路来做,不过我还是习惯传统的关联查询,感觉更可控和准确些。

  • jy02201949

    😬原本老是按关系数据库去考虑,难怪这么难搞

推荐标签 标签

  • 导航

    各种网址链接、内容导航。

    42 引用 • 175 回帖
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    85 引用 • 139 回帖
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    30 引用 • 96 回帖
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    230 引用 • 1454 回帖
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    728 引用 • 1273 回帖 • 2 关注
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    156 引用 • 3792 回帖
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖
  • 博客

    记录并分享人生的经历。

    273 引用 • 2388 回帖
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 612 关注
  • BookxNote

    BookxNote 是一款全新的电子书学习工具,助力您的学习与思考,让您的大脑更高效的记忆。

    笔记整理交给我,一心只读圣贤书。

    1 引用 • 1 回帖
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖 • 5 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    288 引用 • 734 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4601 回帖 • 700 关注
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 394 关注
  • Flutter

    Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作,它正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

    39 引用 • 92 回帖 • 7 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    11 引用 • 5 回帖 • 612 关注
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 3 关注
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 653 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 51 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 2 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    286 引用 • 248 回帖 • 44 关注
  • sts
    2 引用 • 2 回帖 • 197 关注
  • OnlyOffice
    4 引用 • 2 关注
  • danl
    146 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 17 关注