乐趣区

关于mongodb:为什么-MongoDB-使用-B-树

概述

MongoDB 是一个通用的、面向文档的分布式数据库,这是官网对 MongoDB 介绍。区别于传统的关系型数据库 MySQL、Oracle 和 SQL Server,MongoDB 最重要的一个特点就是 『面向文档』,因为数据存储形式的不同,对外提供的接口不再是被大家熟知的 SQL,所以被划分成了 NoSQL,NoSQL 是绝对 SQL 而言的,很多咱们耳熟能详的存储系统都被划分成了 NoSQL,例如:Redis、DynamoDB 和 Elasticsearch 等。

sql-and-nosq

NoSQL 常常被了解成没有 SQL(Non-SQL)或者非关系型(Non-Relational),不过也有人将其了解成不只是 SQL(Not Only SQL),深挖这个词的含意和起源可能没有太多意义,这种二次解读很多时候都是为营销服务的,咱们只须要晓得 MongoDB 对数据的存储形式与传统的关系型数据库齐全不同。

MongoDB 的架构与 MySQL 十分相似,它们底层都应用了可插拔的存储引擎以满足用户的不同需要,用户能够依据数据特征选择不同的存储引擎,最新版本的 MongoDB 应用了 WiredTiger 作为默认的存储引擎。

mongodb-architecture

作为 MongoDB 默认的存储引擎,WiredTiger 应用 B 树作为索引底层的数据结构,然而除了 B 树之外,它还反对 LSM 树作为可选的底层存储构造,LSM 树的全称是 Log-structured merge-tree,你能够在 MongoDB 中应用如下所示的命令创立一个基于 LSM 树的汇合(Collection):

db.createCollection(    

      "posts",  
      
      {storageEngine: { wiredTiger: {configString: "type=lsm"}}}
      
      )

咱们在前端培训这篇文章中不仅会介绍 MongoDB 的默认存储引擎 WiredTiger 为什么抉择应用 B 树而不是 B+ 树,还会对 B 树和 LSM 树之间的性能和利用场景进行比拟,帮忙各位读者更全面地了解明天的问题。

设计

既然要比拟两个不同数据结构与 B 树的差异,那么在这里咱们将分两个大节别离介绍 B+ 树和 LSM 树为什么没有成为 WiredTiger 默认的数据结构:

作为非关系型的数据库,MongoDB 对于遍历数据的需要没有关系型数据库那么强,它谋求的是读写单个记录的性能;

大多数 OLTP 的数据库面对的都是读多写少的场景,B 树与 LSM 树在该场景下有更大的劣势;

上述的两个场景都是 MongoDB 须要面对和解决的,所以咱们会在这两个常见场景下对不同的数据结构进行比拟。

非关系型

咱们在下面其实曾经屡次提到了 MongoDB 是非关系型的文档数据库,它齐全摈弃了关系型数据库那一套体系之后,在设计和实现上就十分自在,它不再须要遵循 SQL 和关系型数据库的体系,能够更自在对特定场景进行优化,而在 MongoDB 假如的场景中遍历数据并不是常见的需要。

mysql-innodb-b-plus-tree

MySQL 中应用 B+ 树是因为 B+ 树只有叶节点会存储数据,将树中的每一个叶节点通过指针连接起来就能实现程序遍历,而遍历数据在关系型数据库中十分常见,所以这么抉择是齐全没有问题的。

MongoDB 和 MySQL 在多个不同数据结构之间抉择的最终目标就是缩小查问须要的随机 IO 次数,MySQL 认为遍历数据的查问是常见的,所以它抉择 B+ 树作为底层数据结构,而舍弃了通过非叶节点存储数据这一个性,然而 MongoDB 面对的问题就不太一样了:

mongodb-wiredtiger-b-tree

尽管遍历数据的查问是绝对常见的,然而 MongoDB 认为查问单个数据记录远比遍历数据更加常见,因为 B 树的非叶结点也能够存储数据,所以查问一条数据所须要的均匀随机 IO 次数会比 B+ 树少,应用 B 树的 MongoDB 在相似场景中的查问速度就会比 MySQL 快。这里并不是说 MongoDB 并不能对数据进行遍历,咱们在 MongoDB 中也能够应用范畴来查问一批满足对应条件的记录,只是须要的工夫会比 MySQL 长一些。

SELECT * FROM comments WHERE created_at > '2019-01-01'

很多人看到遍历数据的查问想到的可能都是如上所示的范畴查问,然而在关系型数据库中更常见的其实是如下所示的 SQL —— 查问外键或者某字段等于某一个值的全副记录:

SELECT * FROM comments WHERE post_id = 1

上述查问其实并不是范畴查问,它没有应用 >、< 等表达式,然而它却会在 comments 表中查问一系列的记录,如果 comments 表上有索引 post_id,那么这个查问可能就会在索引中遍历相应索引,找到满足条件的 comment,这种查问也会受害于 MySQL B+ 树相互连接的叶节点,因为它能缩小磁盘的随机 IO 次数。

MongoDB 作为非关系型的数据库,它从汇合的设计上就应用了齐全不同的办法,如果咱们依然应用传统的关系型数据库的表设计思路来思考 MongoDB 中汇合的设计,写出相似如上所示的查问会带来绝对比拟差的性能:

db.comments.find({ post_id: 1} )

因为 B 树的所有节点都能存储数据,各个间断的节点之间没有很好的方法通过指针相连,所以上述查问在 B 树中性能会比 B+ 树差很多,然而这并不是一个 MongoDB 中举荐的设计办法,更适合的做法其实是应用嵌入文档,将 post 和属于它的所有 comments 都存储到一起:

{    
     "_id": "...",    
     "title": "为什么 MongoDB 应用 B 树",    
     "author": "draven",    
     "comments": [        
         {            
              "_id": "...",            
              "content": "你这写的不行"        
         },        
         {           
             "_id": "...",            
             "content": "一楼说的对"        
           }    
        ]
     }

应用上述形式对数据进行存储时就不会遇到 db.comments.find({ post_id: 1} ) 这样的查问了,咱们只须要将 post 取出来就会取得相干的全副评论,这种区别于传统关系型数据库的设计形式是须要所有应用 MongoDB 的开发者从新思考的,这也是很多人应用 MongoDB 后却发现性能不如 MySQL 的最大起因 —— 应用的姿态不对。

有些读者到这里可能会有疑难了,既然 MongoDB 认为查问单个数据记录远比遍历数据的查问更加常见,那为什么不应用哈希作为底层的数据结构呢?

datastructures-and-query

如果咱们应用哈希,那么对于所有单条记录查问的复杂度都会是 O(1),然而遍历数据的复杂度就是 O(n);如果应用 B+ 树,那么单条记录查问的复杂度是 O(log n),遍历数据的复杂度就是 O(log n) + X,这两种不同的数据结构一种提供了最好的单记录查问性能,一种提供了最好的遍历数据的性能,然而这都不能满足 MongoDB 面对的场景 —— 单记录查问十分常见,然而对于遍历数据也须要有绝对较好的性能反对,哈希这种性能体现较为极其的数据结构往往只能在简略、极其的场景下应用。

读多写少

LSM 树是一个基于磁盘的数据结构,它设计的次要目标是为长期须要高频率写入操作的文件提供低成本的索引机制。无论是 B 树还是 B+ 树,向这些数据结构组成的索引文件中写入记录都须要执行的磁盘随机写,LSM 树的优化逻辑就是就义局部的读性能,将随机写转换成程序写以优化数据的写入。

咱们在这篇文章不会具体介绍为什么 LSM 树有着较好的写入性能,咱们只是来剖析为什么 WiredTiger 应用 B 树作为默认的数据结构。WiredTiger 对 LSM 树和 B 树的性能进行了读写吞吐量的基准测试,通过基准测试失去了如下图所示的后果,从图中的后果咱们能发现:

LSM_btree_Throughput

在不限度写入的状况下;

LSM 树的写入性能是 B 树的 1.5 ~ 2 倍;

LSM 树的读取性能是 B 树的 1/6 ~ 1/3;

在限度写入的状况下;

LSM 树的写入性能与 B 树的性能根本持平;

LSM 树的读取性能是 B 树的 1/4 ~ 1/2;

在限度写入的状况下,每秒会写入 30,000 条数据,从这里的剖析后果来看,无论那种状况下 B 树的读取性能是远好于 LSM 树的。对于大多数的 OLTP 零碎来说,零碎的查问会是写的很多倍,所以 LSM 树在写入方面的优异体现也没有方法让它成为 MongoDB 默认的数据格式。

总结

利用场景永远都是零碎设计时首先须要思考的问题,作为 NoSQL 的 MongoDB,其指标场景就与更早的数据库就有着比拟大的差别,咱们来简略总结一下 MongoDB 最终抉择应用 B 树的两个起因:

MySQL 应用 B+ 树是因为数据的遍历在关系型数据库中十分常见,它常常须要解决各个表之间的关系并通过范畴查问一些数据;然而 MongoDB 作为面向文档的数据库,与数据之间的关系相比,它更看重以文档为核心的组织形式,所以抉择了查问单个文档性能较好的 B 树,这个抉择对遍历数据的查问也能够保障能够承受的时延。

LSM 树是一种专门用来优化写入的数据结构,它将随机写变成了程序写显著地进步了写入性能,然而却就义了读的效率,这与大多数场景须要的特点是不匹配的,所以 MongoDB 最终还是抉择读取性能更好的 B 树作为默认的数据结构。

退出移动版