当前位置: 首页 > 科技观察

从MySQL和MongoDB的对比看SQL和NoSQL的较量

时间:2023-03-14 21:09:49 科技观察

贵金属(注:贵金属是作者所在部门的业务)行情系统提供的接口通过Redis获取数据。目前Redis每分钟最多只能存储8000条左右的记录。K的行情数据,考虑到未来可能有更大数据量的查询需求,需要查询几个月甚至几年的行情数据,要求数据库在保证性能和稳定性的同时提供功能.Redis通常只作为内存数据库使用,数据量不大,传统关系型数据库存在一定的查询性能瓶颈,可以考虑研究其他NoSQL数据库。1、为什么要研究MongoDB?图1-1为2017年11月DB-Engines数据库排名统计,可以看出MongoDB综合排名第5,在NoSQL数据库中排名第1。图1-1DB-Engines数据库2017年11月排名统计优点:社区活跃,用户多,应用广泛MongoDB在内存充足且索引支持完善的情况下将数据放入内存,查询效率更高MongoDB分片机制支持海量数据的存储和扩展。缺点:不支持事务,不支持join,查询复杂经过初步调研,MongoDB具备我们需要的特性,缺点不影响应用场景,接下来开始做实际性能。压力测试。二、压测性能对比1、准备条件(1)MySQL和MongoDB数据库所在服务器的硬件环境-java-driver-2.14.3MySQL服务器版本:5.6.34MySQL连接器版本:MySQL-connector-java-6.0.6MongoDBwiredTiger使用的存储引擎MySQLInnoDB使用的存储引擎(3)数据库表结构和索引MongoDB索引是dateTime,是唯一索引。我们在实际测试中使用的MongoDB数据结构和字段如图2-1所示。图2-1MongoDB数据表记录示例MySQL的索引有DATETIME、PARTNER_ID、GOODS_ID、SCOPE,都是唯一索引。我们实际测试中使用的MySQL数据结构和字段如图2-2所示。图2-2MySQL数据表记录示例SQL语句根据datetime字段查询时间范围(4)设置连接池最大连接数为200,SQL语句调整为***2,million,***表2-2展示了百万级不同查询量、不同并发的压测结果的数据库表总记录数。其他压力测试数据及结果如表2-2所示。表2-2百万级、***级压测数据及结果3.亿级不同查询量、不同并发的压测结果如表2-3所示。表2-3亿级压测分析压测数据及结果:当每次查询的数据量为500条时,无论表总数据量为千万级还是亿级,MySQL和MongoDB在100个并发线程的情况下查询性能相当不错,平均响应时间在500ms以内,TPS在230左右。当每次查询的数据量为5000和表的总数据量时是***,50个并发线程的情况下MongoDB的查询性能还不如MySQL的一半,100个并发线程的情况下查询性能很差。平均响应时间在4500ms左右,当表数据总量在亿级别,并发50以上的情况下,MongoDB和MySQL的性能较差。在本例简单数据模型下时间范围内等价查询的应用场景中,MongoDB在高并发条件下的查询性能并不比MySQL好。还有一点需要注意的是,在这种情况下,数据总量从***到***再到billion,对查询性能影响不大,但是对于查询数据来说,对倍数增长是非常敏感的的数据量,所以在考虑数据库查询性能的时候,还要考虑应用对单次查询量的需求。虽然MongoDB在我们的应用场景中并没有达到预期的性能,但是我们也简单考察了MySQL和MongoDB的内存使用机制以及一些可能影响查询效率的内部配置。三、MySQL和MongoDB内存结构1.InnoDB内存使用机制InnoDB架构如图3-1所示。图3-1InnoDB架构压力测试MySQL使用InnoDB存储引擎。InnoDB影响查询效率的两个重要参数是innodb_buffer_pool_size和innodb_read_ahead_threshold。innodb_buffer_pool_size指的是InnoDB缓冲池的大小。本例中InnoDB缓冲池的大小为20G。该参数的大小可以通过命令innodb_buffer_pool_size20G指定。缓冲池使用改进的LRU算法进行管理,维护一个LRU列表和一个FREE列表。FREE列表存储空闲页面。数据库启动时LRU列表为空。如果有,则放入LRU链表,否则,LRU进行淘汰,淘汰结束的页面分配给新的页面。innodb_read_ahead_threshold对应数据预加载机制,innodb_read_ahead_threshold30表示如果一个extent中顺序读取的pages超过或等于这个参数变量,InnoDB会将下一个extent异步读入缓冲池,比如如果这个参数的值为30,则当顺序读取extent中的30页时,会触发InnoDB线性预读,将下一个extent读入内存;之前没有这个变量,当extent被访问到最后一页时,InnoDB会决定是否将下一个extent放入缓冲池;可以通过showInnoDBstatus中Pagesreadahead和evictedwithoutaccess这两个值来观察MySQL服务器上的预读状态:Innodb_buffer_pool_read_ahead:表示通过预读请求到bufferpool的pages;Innodb_buffer_pool_read_ahead_evicted:表示在缓冲池中因为请求未被访问而从内存中逐出的页数。可见MySQL的缓冲池机制可以充分利用内存,并且有预加载机制。在一定条件下,目标数据完全在内存中,也可以有很好的查询性能。2.MongoDB的存储结构和数据模型(1)本例中MongoDB使用的存储引擎是WiredTiger,WiredTiger的结构如图3-2所示。图3-2WiredTigerEngine结构图WiredTigerCache的结构示意图如图3-3所示。图3-3WiredTigerCache实现示意图WiredtigerCache采用Btree组织,每个Btree节点为一个页面,根页面为btree的根节点,内部页面为btree的中间索引节点,叶子页面是实际存储数据的Leaf节点;btree数据以页面为单位按需从磁盘加载或写入磁盘。引擎使用的内存量可以通过在配置文件中指定storage.wiredTiger.engineConfig.cacheSizeGB参数来设置。此内存用于缓存工作集数据(索引、名称空间、未提交的写入、查询缓冲区等)。(2)数据模型嵌入式MongoDB文档是无模式的,因此可以支持多种数据结构。嵌入式模型也称为非规范化模型。在MongoDB中,一组相关数据可以是文档或文档的一部分。图3-4嵌入式文档示例嵌入式类型支持将一组相关数据存储在一个文档中。这样做的好处是应用程序可以用相对较少的查询和更新操作来完成一些常规的数据查询和更新。工作。当遇到以下情况时,我们应该考虑使用嵌入类型:如果数据关系是一对一的包含关系,比如下面的文档,每个人都有一个contact字段来描述这个人的联系方式。对于这样的一对一关系,使用嵌入类型查询和更新数据是非常方便的。{"_id":,"name":"Wilber","contact":{"phone":"12345678","email":"wilber@shanghai.com"}}如果数据关系是一对多,然后还要考虑使用嵌入式模型。例如,以下文档使用posts字段记录用户发布的所有博客。在这种情况下,如果应用会经常通过用户名字段查询用户发布的博客信息。那么,将posts作为一个嵌入字段会是一个更好的选择,这样可以减少很多查询操作。{“_id”:,“姓名”:“魏柏”,“联系人”:{“电话”:“12345678”,”邮箱”:“wilber@shanghai.com”},“帖子”:[”:[”:“IndexesinMongoDB”,”created”:“12/01/2014”,”link”:“www.linuxidc.com”},{”title”:“ReplicationinMongoDB”,”created”:“201/02””link”:“www.linuxidc.com”},{”title”:“ShardinginMongoDB”,”created”:“12/03/2014”,”link”:“www.linuxidc.com”}]}根据上面的描述,可以看出嵌入式模型可以为应用程序提供良好的数据查询性能,因为基于嵌入式模型,数据库可以通过一次操作同时获取所有相关数据,嵌入式模型可以将数据更新操作变成原子写操作。但是,嵌入式模型也可能会引入一些问题。比如文档会越来越大,可能会影响数据库写操作的性能,也可能会产生数据碎片。引用相对于嵌入式模型,引用模型也称为归一化数据模型,以引用的方式表达数据之间的关系。这里我们也使用来自MongoDB文档的图片。在这个模型中,去掉了user的contact和access,用user_id作为索引来表示他们之间的关系。图3-5参考文档示例当我们遇到以下情况时,可以考虑使用参考模型:使用嵌入式模型往往会带来数据冗余,但可以提高数据查询的效率。但是,当应用程序基本不通过嵌入式模型进行查询,或者查询效率的提升不足以弥补数据冗余带来的问题时,就应该考虑参考模型了。在实现复杂的多对多关系时可以考虑参考模型。比如我们熟悉的例子中,学生-课程-教师关系,如果用引用模型来实现三者的关系,可能会比嵌入式模型更清晰、更直观,同时会减少很多冗余数据。当需要实现复杂的树关系时,可以考虑引用模型。四、应用场景分析1、MongoDB的应用场景(1)表结构不清晰,数据不断增长。MongoDB是一个非结构化文档数据库。在不影响原始数据的情况下很容易扩展字段。内容管理或博客平台等,如圈子系统、存储用户评论等。(2)更高的写入负载MongoDB更注重数据的高写入性能而非事务安全,适用于业务系统中存在大量“低价值”数据的场景。数据本身以json格式存储。比如做一个日志系统。(3)当数据量很大或者以后会变得很大时,当MySQL单表数据量达到5-10G时,会出现详细的性能下降。需要对数据进行横向和纵向的拆分,通过拆分库来完成扩容。MongoDBBuiltwithsharding和众多的数据分片特性,易于水平扩展,更好地适应大数据量增长的需求。(4)高可用就是高可用,自动主从切换(副本集)不适用。(1)MongoDB不支持事务操作,需要事务的应用建议不要使用MongoDB。(2)MongoDB目前不支持join操作,对于需要复杂查询的应用不建议使用MongoDB。2、关系型数据库和非关系型数据库的应用场景对比关系型数据库适合存储结构化数据,比如用户账号、地址:这些数据通常需要结构化查询,比如join。这时候,关系型数据库就会胜出。这些数据的规模和增长率通常是可预测的、事务性的和一致的。NoSQL适合存储非结构化数据,比如文章、评论:这些数据通常用于混淆处理,比如全文搜索和机器学习都是海量数据,而且增长速度不可预测。根据数据的特点,NoSQL数据库通常具有最好(至少接近)的扩展性来通过key获取数据,但是对于join或其他结构化查询支持相对较差