从MySQL和MongoDB的对比,看SQL与NoSQL的较量
张家江,网易乐得高级工程师。
贵金属(注:贵金属为笔者部门业务)的行情系统提供的接口通过Redis获取数据,目前使用Redis最多只存储了大概8000条左右的分钟k的行情数据,考虑到将来可能会有更大数据量的查询需求,需要查询几月甚至几年的行情数据,要求数据库在提供功能的同时又能保证性能和稳定性。Redis通常只用做较小数据量的内存数据库,而传统关系数据库又有一定的查询性能瓶颈,所以考虑调研一下其它的NoSQL数据库。
一、为什么调研MongoDB?
图1-1是DB-Engines2017年11月数据库的排名统计,可以看到MongoDB总排名在第5,在NoSQL数据库中排名第1。
图1-1 DB-Engines2017年11月数据库的排名统计
优点:
社区活跃,用户较多,应用广泛
MongoDB在内存充足的情况下数据都放入内存且有完整的索引支持,查询效率较高
MongoDB的分片机制,支持海量数据的存储和扩展
缺点:
不支持事务
不支持join、复杂查询
初步调研下来,MongoDB具备我们需要的特性,而缺点不影响应用场景,故接下来我们就开始做实际的性能压测。
二、压测性能对比
1、准备条件
(1)MySQL 、MongoDB数据库所在服务器硬件环境
表2-1 服务器硬件环境主要参数
(2)最新的数据库版本
MongoDB server version: 3.4.5MongoDB client version: mongo-java-driver-2.14.3MySQL server version:5.6.34MySQL connector version: MySQL-connector-java-6.0.6MongoDB使用的储存引擎wiredTigerMySQL使用的储存引擎InnoDB(3)数据库表结构及索引
MongoDB索引为dateTime 且是唯一索引。我们实际测试使用的MongoDB数据结构及字段如图2-1所示。
图2-1 MongoDB数据表记录示例
MySQL索引为DATETIME,PARTNER_ID,GOODS_ID,SCOPE且是唯一索引。我们实际测试使用的MySQL数据结构及字段如图2-2所示。
图2-2 MySQL数据表记录示例
SQL语句根据datetime字段进行时间范围的查询
(4)连接池最大连接数都设置为200个,SQL语句调到最优
2、百万、千万级别的下不同查询量不同并发量的压测结果
数据库表中记录数总量在百万、千万级别的压测数据及结果如表2-2所示。
表2-2 百万、千万级别的压测数据及结果
3、亿级别的下不同查询量不同并发量的压测结果
数据库表中记录数总量在亿级别的压测数据及结果如表2-3所示。
表2-3 亿级别的压测数据及结果
压测结果分析:
当每次查询数据量在500条时,无论表中数据总量千万或者亿级别,MySQL和MongoDB在100线程并发的情况下查询性能相当,表现良好,平均响应时间在500ms以内,TPS在230左右。
当每次查询数据量在5000条时,表中数据总量为千万级别时,MongoDB在50线程并发情况下查询性能不及MySQL 的一半,100线程并发情况查询性能都很差,平均响应时间在4500ms左右,表中数据总量为亿级别时,在50个及以上的并发情况下,MongoDB和MySQL性能都较差。
在本案例简单数据模型下时间范围内的等值查询应用场景下,MongoDB在高并发条件下的大数据量查询性能并没有比MySQL更好。另外还有一点需要注意的是,在本案例中,数据总量由百万级别到千万级别再到亿级别的变化过程中,对于查询性能的影响都不是很大,但对于查询数据量的数倍增长却十分敏感,所以在考量数据库查询性能时,也要重点考量应用的单次查询量的需求。
尽管MongoDB在我们的这种应用场景下并没有达到预期的性能,我们也简单地的调研了下MySQL和MongoDB对于内存的使用机制以及一些可能影响查询效率的内部配置。
三、MySQL和MongoDB内存结构
1、InnoDB内存使用机制
InnoDB体系结构如图3-1所示。
图3-1 InnoDB体系结构
压测MySQL使用的是InnoDB存储引擎,InnoDB关于查询效率有影响的两个比较重要的参数分别是innodb_buffer_pool_size,innodb_read_ahead_threshold。
innodb_buffer_pool_size指的是InnoDB缓冲池的大小,本例中InnoDB缓冲池大小为20G,该参数的大小可通过命令指定innodb_buffer_pool_size 20G。缓冲池使用改进的LRU算法进行管理,维护一个LRU列表、一个FREE列表,FREE列表存放空闲页,数据库启动时LRU列表是空的,当需要从缓冲池分页时,首先从FREE列表查找空闲页,有则放入LRU列表,否则LRU执行淘汰,淘汰尾部的页分配给新页。
innodb_read_ahead_threshold相对应的是数据预加载机制,innodb_read_ahead_threshold 30表示的是如果一个extent中的被顺序读取的page超过或者等于该参数变量的,InnoDB将会异步的将下一个extent读取到buffer pool中,比如该参数的值为30,那么当该extent中有30个pages被sequentially的读取,则会触发InnoDB linear预读,将下一个extent读到内存中;在没有该变量之前,当访问到extent的最后一个page的时候,InnoDB会决定是否将下一个extent放入到buffer pool中;可以在MySQL服务端通过show InnoDB status中的Pages read ahead和evicted without access两个值来观察预读的情况:
Innodb_buffer_pool_read_ahead:表示通过预读请求到buffer pool的pages;
Innodb_buffer_pool_read_ahead_evicted:表示由于请求到buffer pool中没有被访问,而驱逐出内存的页数。
可以看出来,MySQL的缓冲池机制是能充分利用内存且有预加载机制,在某些条件下目标数据完全在内存中,也能够具备非常好的查询性能。
2、MongoDB的存储结构及数据模型
(1)本例中MongoDB使用的储存引擎是WiredTiger,WiredTiger的结构如图3-2所示。
图3-2 WiredTiger Engine的结构
WiredTiger Cache的实现原理图如图3-3所示。
图3-3 WiredTiger Cache的实现原理图
Wiredtiger的Cache采用Btree的方式组织,每个Btree节点为一个page,root page是btree的根节点,internal page是btree的中间索引节点,leaf page是真正存储数据的叶子节点;btree的数据以page为单位按需从磁盘加载或写入磁盘。
可以通过在配置文件中指定storage.wiredTiger.engineConfig.cacheSizeGB参数设定引擎使用的内存量。此内存用于缓存工作集数据(索引、namespace,未提交的write,query缓冲等)。
(2)数据模型
内嵌
MongoDB的文档是无模式的,所以可以支持各种数据结构,内嵌模型也叫做非规格化模型(denormalized)。在MongoDB中,一组相关的数据可以是一个文档,也可以是组成文档的一部分。
图3-4 内嵌文档示例
内嵌类型支持一组相关的数据存储在一个文档中,这样的好处就是,应用程序可以通过比较少的的查询和更新操作来完成一些常规的数据的查询和更新工作。
当遇到以下情况的时候,我们应该考虑使用内嵌类型:
如果数据关系是一种一对一的包含关系,例如下面的文档,每个人都有一个contact字段来描述这个人的联系方式。像这种一对一的关系,使用内嵌类型可以很方便的进行数据的查询和更新。
{ ”_id”: , ”name”: “Wilber”, ”contact”: { “phone”: “12345678”, “email”: “wilber@shanghai.com” } }
如果数据的关系是一对多,那么也可以考虑使用内嵌模型。例如下面的文档,用posts字段记录所有用户发布的博客。在这中情况中,如果应用程序会经常通过用户名字段来查询改用户发布的博客信息。那么,把posts作为内嵌字段会是一个比较好的选择,这样就可以减少很多查询的操作。
{ “_id”: , “name”: “Wilber”, “contact”: { ”phone”: “12345678”, ”email”: “wilber@shanghai.com” }, ”posts”: [ { ”title”: “Indexes in MongoDB”, ”created”: “12/01/2014”, ”link”: “www.linuxidc.com” }, { ”title”: “Replication in MongoDB”, ”created”: “12/02/2014”, ”link”: “www.linuxidc.com” }, { ”title”: “Sharding in MongoDB”, ”created”: “12/03/2014”, ”link”: “www.linuxidc.com” } ] }
根据上面的描述可以看出,内嵌模型可以给应用程序提供很好的数据查询性能,因为基于内嵌模型,可以通过一次数据库操作得到所有相关的数据。同时,内嵌模型可以使数据更新操作变成一个原子写操作。然而,内嵌模型也可能引入一些问题,比如说文档会越来越大,这样就可能会影响数据库写操作的性能,还可能会产生数据碎片(data fragmentation)。
引用
相对于嵌入模型,引用模型又称规格化模型(Normalized data models),通过引用的方式来表示数据之间的关系。这里同样使用来自MongoDB文档中的图片,在这个模型中,把contact和access从user中移出,并通过user_id作为索引来表示它们之间的联系。
图3-5 引用文档示例
当我们遇到以下情况的时候,就可以考虑使用引用模型了:
使用内嵌模型往往会带来数据的冗余,却可以提升数据查询的效率。但是,当应用程序基本上不通过内嵌模型查询,或者说查询效率的提升不足以弥补数据冗余带来的问题时,我们就应该考虑引用模型了。
当需要实现复杂的多对多关系时,可以考虑引用模型。比如我们熟知的例子,学生-课程-老师关系,如果用引用模型来实现三者的关系,可能会比内嵌模型更清晰直观,同时会减少很多冗余数据。
当需要实现复杂的树形关系时,可以考虑引用模型。
四、应用场景分析
1、MongoDB的应用场景
(1)表结构不明确且数据不断变大
MongoDB是非结构化文档数据库,扩展字段很容易且不会影响原有数据。内容管理或者博客平台等,例如圈子系统、存储用户评论之类的。
(2)更高的写入负载
MongoDB侧重高数据写入的性能,而非事务安全,适合业务系统中有大量“低价值”数据的场景。本身存的就是json格式数据。例如做日志系统。
(3)数据量很大或者将来会变得很大
MySQL单表数据量达到5-10G时会出现明细的性能降级,需要做数据的水平和垂直拆分、库的拆分完成扩展,MongoDB内建了sharding、很多数据分片的特性,容易水平扩展,比较好的适应大数据量增长的需求。
(4)高可用性
自带高可用,自动主从切换(副本集)
不适用的场景
(1)MongoDB不支持事务操作,需要用到事务的应用建议不用MongoDB。(2)MongoDB目前不支持join操作,需要复杂查询的应用也不建议使用MongoDB。
2、关系型数据库和非关系型数据库的应用场景对比
关系型数据库适合存储结构化数据,如用户的帐号、地址:
这些数据通常需要做结构化查询,比如join。这时候,关系型数据库就要胜出一筹
这些数据的规模、增长的速度通常是可以预期的
事务性、一致性
NoSQL适合存储非结构化数据,如文章、评论:
这些数据通常用于模糊处理,如全文搜索、机器学习
这些数据是海量的,而且增长的速度是难以预期的
根据数据的特点,NoSQL数据库通常具有无限(至少接近)伸缩性
按key获取数据效率很高,但是对join或其它结构化查询的支持就比较差
从MySQL和MongoDB的对比,看SQL与NoSQL的较量的更多相关文章
- Redis/Mysql/SQLite/MongoDB 数据库对比
一.Redis: redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(so ...
- mysql与mongodb命令对比
连接:mysql: mysql -h localhost -u username -pmongodb:con = pymongo.Connection(‘localhost’,27017)显示数据库m ...
- MySQL与MongoDB的操作对比,以及区别
MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoDB则是非关系型数据库,也叫文档型数据库,是一种NoSQL的数据库.它们各有各的优点,关键是看用在什么地方 ...
- 1.MySQL与MongoDB的操作对比,以及区别
转自:https://www.cnblogs.com/chris-oil/p/4982490.html MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoD ...
- [转]MySQL与MongoDB的操作对比
MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoDB则是非关系型数据库,也叫文档型数据库,是一种NoSQL的数据库.它们各有各的优点,关键是看用在什么地方 ...
- MySQL与MongoDB的操作对比
MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoDB则是非关系型数据库,也叫文档型数据库,是一种NoSQL的数据库.它们各有各的优点,关键是看用在什么地方 ...
- MySQL 与 MongoDB的操作对比
MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoDB则是非关系型数据库,也叫文档型数据库,是一种NoSQL的数据库.它们各有各的优点,关键是看用在什么地方 ...
- 技术分享|SQL和 NoSQL数据库之间的差异:MySQL(VS)MongoDB
在当今市场上,存在各种类型的数据库,选择适合你业务类型的数据库对应用的开发和维护有着重要意义.本篇文章,将为大家分享SQL和NoSQL语言之间的区别,同时还将比较这两种类型的数据库,以帮助小伙伴们选择 ...
- MySQL和Mongodb的区别与应用场景对比
MySQL是关系型数据库 优势: 在不同的引擎上有不同 的存储方式. 查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高. 开源数据库的份额在不断增加,mysql的份额页在持续增长. 缺点 ...
随机推荐
- Java操作FTP工具类(实例详解)
这里使用Apache的FTP jar 包 没有使用Java自带的FTPjar包 工具类 package com.zit.ftp; import java.io.File; import java.i ...
- 前端基础之JavaScript进阶
一.流程控制 if - else var a = 10; if (a >5){ console.log("yes"); }else { console.log("n ...
- 《软件调试 Windows概要》
操作系统是计算机系统中的基本软件.它负责管理系统中的软硬件资源.通常都包括文件管理.内存管理.进程管理.打印管理.网络管理等基本功能.除此之外,支持调试也是操作系统设计的一项根本任务. 0x01 进 ...
- DevExpress ASP.NET Core Controls v18.2新功能详解
行业领先的.NET界面控件2018年第二次重大更新——DevExpress v18.2日前正式发布,本站将以连载的形式为大家介绍新版本新功能.本文将介绍了DevExpress ASP.NET Core ...
- DevExpress WinForms使用教程:Data Grid - Find Panel模式
[DevExpress WinForms v18.2下载] DevExpress WinForms用户都熟知,Data Grid是整个产品线的主要产品.在v18.2中添加了一些新的功能,例如之前教程中 ...
- Docker(4):Dockerfile命令一览
1.FROM 指定基础镜像 FROM 指令用于指定其后构建新镜像所使用的基础镜像.FROM 指令必是 Dockerfile 文件中的首条命令,启动构建流程后,Docker 将会基于该镜像构建新镜像,F ...
- Kubernetes资源监控探索
搭建kubernetes集群,有一个默认的dashboard,但是这个dashboard比较简陋,不能将自定义展示.所以打算使用Grafana+Heapster+Influxdb构建一个一体化监控平台 ...
- 了解下webpack的几个命令
[ webpack3.0.0刚刚出来 所以文章是跟着低版本 教程 操作熟悉 结果好多对不上喔] 六:了解下webpack的几个命令 webpack // 最基本的启动webpack ...
- L257 论述型作文模板
一引出相同观点: 1.With the development of science and technology, more and more people believe that...随着科技的 ...
- python实现图的遍历(递归和非递归)
class graph: def __init__(self,value): self.value=value self.neighbors=None # 图的广度优先遍历 # 1.利用队列实现 # ...