NoSQL在近几年发展得如火如荼,在经过一番研究选型之后,其中的代表产品MongoDB就被成功引入到我们新一版本的销售数据分析系统中了,它的使用,化解了我们在使用MySQL时需要自己想办法去解决的一些难题,在学习和使用的过程当中,有一些欣喜和心得,希望分享给大家。

先说我们系统的背景,我们的系统内部代号为“棱镜”,并非附庸风雅,在斯诺登爆出美国的“棱镜门”之前我们就开始使用了;就如其名称所暗示的:就像棱镜分解白光一样,我们的系统尝试从旅客订票信息,销售采样数据,火车余票数据中挖掘出对航空公司生产经营有用处的信息。一部分是对历史数据的分析用来更好的改善我们的产品和销售策略,或者发掘高端客户;一部分是要对目前销售情况,客流情况,历史同期情况,对未来航班安排进行部署和调整以及应对市场情况做出销售舱位和票价的调整,使得营销决策有数据作为依托,尽力使收益最大化。虽然我们只是一家只有22架飞机的分公司,我们也只保留了从我们属地机场关联的近几年的数据,但是存储了大概有5000万旅客信息;多种类型的销售采样数据差不多5亿条,数据库文件占用了60多G的磁盘空间。对全国航线的销售数据每隔几分钟一次的快照监测,数据的增量不可小看;第一版很简单的就是使用MySQL的主从复制,读写分离,随着数据量的增加,除了响应有所延迟之外,其实还是可以接受的。这点数据量,跟别人真正的海量数据相比,还不算多,然而小产品有自己的大梦想,作为中国最大的航空公司,如果我们的产品有一天可以在整个公司用起来,面对着20倍以上的数据增量,我们的系统还能否承受?所以注意到查询性能的延迟之后,在新版本中,我们决定引入当前发展很快的NoSQL数据库,考虑到MongoDB对SQL查询如聚合查询等一些特性的支持,它成为了我们的首选。

话说任何一个成功的产品要么是解决了用户的痛点,要么是触动了用户的G点,那么作为NoSQL优秀产品之一的 MongoDB与传统的关系型数据库相比它有哪些优势呢?

第一,    动态数据模型

传统的开发模式一般是从数据库表设计开始的,这种开发模式有一定的弊端,其中最大的矛盾就是应对快速变化的需求时那种束缚感。为了性能考虑,根据业务查询数据的需要,对索引和冗余数据字段的设计也不一样,而先设计数据表的这种方法无法预计到将来业务的变化;特别是对于我们在开发中遇到的情况,四个人的小型开发团队,不断变化的业务需求,有些甚至是实验性的探索性的功能,则对于产品的快速迭代带来了很多的问题,增加一个索引动辄几十分钟的情况经常遇到,使敏捷开发变得不敏捷。而MongoDB带来的几乎是随时,无成本的数据表结构的修改能力,可以根据数据查询的需要调整到最佳的存储结构,无需瞻前顾后。无需过多的在数据冗余和性能之间权衡取舍,无需在各种范式之间纠结踌躇,Just do it,快速迭代,快速交付,快速迁移。它还给我们在设计中带来了更加直观的思维模式,以文档存储为单位,不要过多去想结构化的数据如何变成行列化,如何处理各个表之间的关联关系,是否会造成1+N查询,是否需要使用JOIN来避免,这在数据存储方面带来的思维模式的变化就如同面向对象编程和面向过程编程对比的感觉一般。另外一个值得一提的地方,它的这种基于JSON文档格式存储的数据模型,让我们在项目进行过程中有一个很取巧的地方,我们有一些数据源接口,返回的是JSON格式的数据,直接把返回结果存储到MongoDB中即可,再也无需像以前那样要么组织成SQL语句,要么组织成实体对象再存储了,这让开发过程变得流程而自然。

第二,    类SQL的查询特性和支持Map-Reduce功能

在接触MongoDB之前,NoSQL类的数据库给我的模糊印象,似乎停留在只是以key-value存储的内存数据库而已,最多考虑用它提供缓冲层,从未去想有一天它可以替代传统的关系数据库出现在我们的应用中。

MongoDB提供了对传统SQL查询方式的兼容特性,支持OR,AND,GreaterThan等等一系列除连表查询之外的完整查询功能的映射;它可在任意文档属性上快速的建立索引 ,所以查询的速度很快。

对于一个数据分析应用而言,不得不对数据库的聚合查询能力提出要求,MongoDB的Aggregation功能满足了我们的所有需求,对count,distinct,group,project这些常用的数据库查询操作都支持;另外还有map-reduce功能,相比Aggregation,提供了更灵活的查询处理数据以及组织返回结果的能力。

第三,    与生俱来的高可用,数据复制和自动分片支持

传统关系型数据库诞生有几十年了,在它诞生之初根本无法预料到如今应用的需求和发展程度,但是MongoDB之类的新生代数据库就可以从一开始就考虑大数据量,大并发,高可用这些我们在平常应用架构设计中需要着力去解决的问题。在我们使用MySQL的时候,我们经常会使用主从复制来实现高可用,使用诸如mysql-proxy之类的实现读写分离;或者精心设计应用程序分表分库来缓解大数据量和高并发的压力。而在MongoDB身上,这些都成为了标配,再无需组合各种技术和方案甚至奇技淫巧来达到目的,使用MongoDB只需几个配置文件,写个脚本启动即可,其他的专注使用即可。

第四,    为现代应用特别设计的功能特性。

如果说对SQL查询的兼容方便了传统数据库用户向MongoDB的迁移,那么MongoDB中的一些特别设计的特性就是会让你欣喜的感觉中了彩蛋。在我们的应用中,用到了几个特性,我觉得对于我们来说很有用。

(1)      Capped Collection方便了日志的存储和自动清除

在我们的应用中,高频度的数据采集会产生大量的日志,这些日志信息在一定时期内是需要的,但是也会占用很多的空间,之前的解决方案是使用一个cron脚本定期对日志表进行清理。而Capped Collection直接考虑了这种需求,它是一个固定大小的集合,支持快速的插入和查询操作,容量满了之后,直接复写旧的文档实现空间循环利用,这是不是和应用程序日志的存储需求完全一致啊?是的,Capped Collection非常好的切合了这以需求。

(2)      基于地理位置的查询

在地理位置上,比如我们应用中要考虑机场和火车站的可选择性,那么如何定义“附近”呢?MongoDB提供的基于地理位置查询的功能,很好的解决了我们的问题,原本以为要考虑的通过经纬度的复杂计算,MongoDB代劳了。

(3)      GridFS满足了文件存储的高可用和实时备份需求

在遇到集群中需要文件存储的场景时,我们通常通过网络共享存储来解决,作为分公司,我们没有磁盘阵列这种高可靠性的共享存储设备,怎么解决呢?直接使用一台服务器的提供NFS访问么?那么这实际上还是一个单点故障,我们以前的做法是利用Linux内核的inotify特性,检测文件系统变化,然后近乎实时的把文件系统的变化复制到另外一个节点中备份。而MongoDB内置的GridFS功能,直接利用其数据库的高可用能力帮助我们实现了我们对文件存储实现高可用的需求,简单直接,作为开发人员,我无需再过多考虑。

第五,    快速上手

MongoDB提供了完善的文档,支持各个主流平台,无论Mac,Linux,Windows,无论是服务端还是客户端,都提供了开箱即用的特性;基于JSON的查询一看就能让人明白。所以在项目中引入时,团队成员只花一天时间就可以掌握基本的使用的方法,这些方便使用的特性,可以让学习成本降得很低。

总之,MongoDB所体现出来的特性以及它在一些生产应用充分体现了它的优越性,对于我们的架构师和开发人员而言,是时候拥抱新一代的数据库了!