MySQL Redis MongoDB 数据库一课通
核心代码,注释必读
// download:
3w ukoou com
结构化数据、非结构化数据与半结构化数据
文章的开始,聊一下结构化数据、非结构化数据与半结构化数据,因为数据特点的不同,将在技术上直接影响存储引擎的选型。
首先是结构化数据,根据定义结构化数据指的是由二维表结构来逻辑表达和实现的数据,严格遵循数据格式与长度规范,也称作为行数据,特点为:数据以行为单位,一行数据表示一个实体的信息,每一行数据的属性是相同的。例如:
因此关系型数据库完美契合结构化数据的特点,关系型数据库也是关系型数据最主要的存储与管理引擎。
非结构化数据,指的是数据结构不规则或不完整,没有任何预定义的数据模型,不方便用二维逻辑表来表现的数据,例如办公文档(Word)、文本、图片、HTML、各类报表、视频音频等。
介于结构化与非结构化数据之间的数据就是半结构化数据了,它是结构化数据的一种形式,虽然不符合二维逻辑这种数据模型结构,但是包含相关标记,用来分割语义元素以及对记录和字段进行分层。常见的半结构化数据有XML和JSON,例如:
<person>
<name>张三</name>
<age>18</age>
<phone>12345</phone>
</person>
这种结构也被成为自描述的结构。
以关系型数据库的方式做存储的架构演进
首先,我们看一下使用关系型数据库的方式,企业一个系统发展的几个阶段的架构演进(由于本文写的是Sql与NoSql,因此只以存储方式作为切入点,不会涉及类似MQ、ZK这些中间件内容):
阶段一:企业刚发展的阶段,最简单,一个应用服务器配一个关系型数据库,每次读写数据库。
阶段二:无论是使用MySQL还是Oracle还是别的关系型数据库,数据库通常不会先成为性能瓶颈,通常随着企业规模的扩大,一台应用服务器扛不住上游过来的流量且一台应用服务器会产生单点故障的问题,因此加应用服务器并且在流量入口使用Nginx做一层负载均衡,保证把流量均匀打到应用服务器上。
阶段三:随着企业规模的继续扩大,此时由于读写都在同一个数据库上,数据库性能出现一定的瓶颈,此时简单地做一层读写分离,每次写主库,读备库,主备库之间通过binlog同步数据,就能很大程度上解决这个阶段的数据库性能问题
阶段四:企业发展越来越好了,业务越来越大了,做了读写分离数据库压力还是越来越大,这时候怎么办呢,一台数据库扛不住,那我们就分几台吧,做分库分表,对表做垂直拆分,对库做水平拆分。以扩数据库为例,扩出两台数据库,以一定的单号(例如交易单号),以一定的规则(例如取模),交易单号对2取模为0的丢到数据库1去,交易单号对2取模为1的丢到数据库2去,通过这样的方式将写数据库的流量均分到两台数据库上。一般分库分表会使用Shard的方式,通过一个中间件,便于连接管理、数据监控且客户端无需感知数据库ip
关系型数据库的优点
上面的方式,看似可以解决问题(实际上确实也能解决很多问题),正常对关系型数据库做一下读写分离 + 分库分表,支撑个1W+的读写QPS还是问题不大的。但是受限于关系型数据库本身,这套架构方案依然有着明显的不足,下面对利用关系型数据库方式做存储的方案的优点先进行一下分析,后一部分再分析一下缺点,对某个技术的优缺点的充分理解是技术选型的前提。
- 易理解
因为行 + 列的二维表逻辑是非常贴近逻辑世界的一个概念,关系模型相对网状、层次等其他模型更加容易被理解
- 操作方便
通用的SQL语言使得操作关系型数据库非常方便,支持join等复杂查询,Sql + 二维关系是关系型数据库最无可比拟的优点,这种易用性非常贴近开发者
- 数据一致性
支持ACID特性,可以维护数据之间的一致性,这是使用数据库非常重要的一个理由之一,例如同银行转账,张三转给李四100元钱,张三扣100元,李四加100元,而且必须同时成功或者同时失败,否则就会造成用户的资损
- 数据稳定
数据持久化到磁盘,没有丢失数据风险,支持海量数据存储
- 服务稳定
最常用的关系型数据库产品MySql、Oracle服务器性能卓越,服务稳定,通常很少出现宕机异常
关系型数据库的缺点
紧接着的,我们看一下关系型数据库的缺点,也是比较明显的。
- 高并发下IO压力大
数据按行存储,即使只针对其中某一列进行运算,也会将整行数据从存储设备中读入内存,导致IO较高
- 为维护索引付出的代价大
为了提供丰富的查询能力,通常热点表都会有多个二级索引,一旦有了二级索引,数据的新增必然伴随着所有二级索引的新增,数据的更新也必然伴随着所有二级索引的更新,这不可避免地降低了关系型数据库的读写能力,且索引越多读写能力越差。有机会的话可以看一下自己公司的数据库,除了数据文件不可避免地占空间外,索引占的空间其实也并不少
- 为维护数据一致性付出的代价大
数据一致性是关系型数据库的核心,但是同样为了维护数据一致性的代价也是非常大的。我们都知道SQL标准为事务定义了不同的隔离级别,从低到高依次是读未提交、读已提交、可重复度、串行化,事务隔离级别越低,可能出现的并发异常越多,但是通常而言能提供的并发能力越强。那么为了保证事务一致性,数据库就需要提供并发控制与故障恢复两种技术,前者用于减少并发异常,后者可以在系统异常的时候保证事务与数据库状态不会被破坏。对于并发控制,其核心思想就是加锁,无论是乐观锁还是悲观锁,只要提供的隔离级别越高,那么读写性能必然越差
- 水平扩展后带来的种种问题难处理
前文提过,随着企业规模扩大,一种方式是对数据库做分库,做了分库之后,数据迁移(1个库的数据按照一定规则打到2个库中)、跨库join(订单数据里有用户数据,两条数据不在同一个库中)、分布式事务处理都是需要考虑的问题,尤其是分布式事务处理,业界当前都没有特别好的解决方案
- 表结构扩展不方便
由于数据库存储的是结构化数据,因此表结构schema是固定的,扩展不方便,如果需要修改表结构,需要执行DDL(data definition language)语句修改,修改期间会导致锁表,部分服务不可用
- 全文搜索功能弱
例如like "%中国真伟大%",只能搜索到"2019年中国真伟大,爱祖国",无法搜索到"中国真是太伟大了"这样的文本,即不具备分词能力,且like查询在"%中国真伟大"这样的搜索条件下,无法命中索引,将会导致查询效率大大降低
写了这么多,我的理解核心还是前三点,它反映出的一个问题是关系型数据库在高并发下的能力是有瓶颈的,尤其是写入/更新频繁的情况下,出现瓶颈的结果就是数据库CPU高、Sql执行慢、客户端报数据库连接池不够等错误,因此例如万人秒杀这种场景,我们绝对不可能通过数据库直接去扣减库存。
可能有朋友说,数据库在高并发下的能力有瓶颈,我公司有钱,加CPU、换固态硬盘、继续买服务器加数据库做分库不就好了,问题是这是一种性价比非常低的方式,花1000万达到的效果,换其他方式可能100万就达到了,不考虑人员、服务器投入产出比的Leader就是个不合格的Leader,且关系型数据库的方式,受限于它本身的特点,可能花了钱都未必能达到想要的效果。至于什么是花100万就能达到花1000万效果的方式呢?可以继续往下看,这就是我们要说的NoSql。
结合NoSql的方式做存储的架构演进
像上文分析的,数据库作为一种关系型数据的存储引擎,存储的是关系型数据,它有优点,同时也有明显的缺点,因此通常在企业规模不断扩大的情况下,不会一味指望通过增强数据库的能力来解决数据存储问题,而是会引入其他存储,也就是我们说的NoSql。
NoSql的全称为Not Only SQL,泛指非关系型数据库,是对关系型数据库的一种补充,特别注意补充这两个字,这意味着NoSql与关系型数据库并不是对立关系,二者各有优劣,取长补短,在合适的场景下选择合适的存储引擎才是正确的做法。
比较简单的NoSql就是缓存:
针对那些读远多于写的数据,引入一层缓存,每次读从缓存中读取,缓存中读取不到,再去数据库中取,取完之后再写入到缓存,对数据做好失效机制通常就没有大问题了。通常来说,缓存是性能优化的第一选择也是见效最明显的方案。
但是,缓存通常都是KV型存储且容量有限(基于内存),无法解决所有问题,于是再进一步的优化,我们继续引入其他NoSql:
数据库、缓存与其他NoSql并行工作,充分发挥每种NoSql的特点。当然NoSql在性能方面大大优于关系挺数据库的同时,往往也伴随着一些特性的缺失,比较常见的就是事务功能的缺失。
下面看一下常用的NoSql及他们的代表产品,并对每种NoSql的优缺点和适用场景做一下分析,便于熟悉每种NoSql的特点,方便技术选型。
一、MySQL
关系型数据库。
在不同的引擎上有不同 的存储方式。
查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高。
开源数据库的份额在不断增加,mysql的份额页在持续增长。
缺点就是在海量数据处理的时候效率会显著变慢。
二、Mongodb
非关系型数据库(nosql ),属于文档型数据库。先解释一下文档的数据库,即可以存放xml、json、bson类型系那个的数据。这些数据具备自述性(self-describing),呈现分层的树状数据结构。数据结构由键值(key=>value)对组成。
存储方式:虚拟内存+持久化。
查询语句:是独特的Mongodb的查询方式。
适合场景:事件的记录,内容管理或者博客平台等等。
架构特点:可以通过副本集,以及分片来实现高可用。
数据处理:数据是存储在硬盘上的,只不过需要经常读取的数据会被加载到内存中,将数据存储在物理内存中,从而达到高速读写。
成熟度与广泛度:新兴数据库,成熟度较低,Nosql数据库中最为接近关系型数据库,比较完善的DB之一,适用人群不断在增长。
优势:
- 快速!在适量级的内存的Mongodb的性能是非常迅速的,它将热数据存储在物理内存中,使得热数据的读写变得十分快,
- 高扩展!
- 自身的Failover机制!
- json的存储格式!
缺点:主要是无事物机制!
三、Redis
非关系型数据库(nosql )
Redis数据全部存在内存,定期写入磁盘,当内存不够时,可以选择指定的LRU算法删除数据。
四、MongoDB和Redis区别
MongoDB和Redis都是NoSQL,采用结构型数据存储。二者在使用场景中,存在一定的区别,这也主要由于
二者在内存映射的处理过程,持久化的处理方法不同。MongoDB建议集群部署,更多的考虑到集群方案,Redis
更偏重于进程顺序写入,虽然支持集群,也仅限于主-从模式。
| 指标 | MongoDB(v2.4.9) | Redis(v2.4.17) | 比较说明 |
|---|---|---|---|
| 实现语言 | C++ | C/C++ | - |
| 协议 | BSON、自定义二进制 | 类Telnet | - |
| 性能 | 依赖内存,TPS较高 | 依赖内存,TPS非常高 | Redis优于MongoDB |
| 可操作性 | 丰富的数据表达、索引;最类似于关系数据库,支持丰富的查询语言 | 数据丰富,较少的IO | MongoDB优于Redis |
| 内存及存储 | 适合大数据量存储,依赖系统虚拟内存管理,采用镜像文件存储;内存占有率比较高,官方建议独立部署在64位系统(32位有最大2.5G文件限制,64位没有改限制) | Redis2.0后增加虚拟内存特性,突破物理内存限制;数据可以设置时效性,类似于memcache | 不同的应用角度看,各有优势 |
| 可用性 | 支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制 | 依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制;不支持自动sharding,需要依赖程序设定一致hash机制 | MongoDB优于Redis;单点问题上,MongoDB应用简单,相对用户透明,Redis比较复杂,需要客户端主动解决。(MongoDB 一般会使用replica sets和sharding功能结合,replica sets侧重高可用性及高可靠性,而sharding侧重于性能、易扩展) |
| 可靠性 | 从1.8版本后,采用binlog方式(MySQL同样采用该方式)支持持久化,增加可靠性 | 依赖快照进行持久化;AOF增强可靠性;增强可靠性的同时,影响访问性能 | MongoDB优于Redis |
| 一致性 | 不支持事物,靠客户端自身保证 | 支持事物,比较弱,仅能保证事物中的操作按顺序执行 | Redis优于MongoDB |
| 数据分析 | 内置数据分析功能(mapreduce) | 不支持 | MongoDB优于Redis |
| 应用场景 | 海量数据的访问效率提升 | 较小数据量的性能及运算 | MongoDB优于Redis |
五、Mysql和Mongodb应用场景
MongoDB 的适用场景为:数据不是特别重要(例如通知,推送这些),数据表结构变化较为频繁,数据量特别大,数据的并发性特别高,数据结构比较特别(例如地图的位置坐标),这些情况下用 MongoDB , 其他情况就还是用 MySQL ,这样组合使用就可以达到最大的效率。
- 1.如果需要将mongodb作为后端db来代替mysql使用,即这里mysql与mongodb 属于平行级别,那么,这样的使用可能有以下几种情况的考量: (1)mongodb所负责部分以文档形式存储,能够有较好的代码亲和性,json格式的直接写入方便。(如日志之类) (2)从data models设计阶段就将原子性考虑于其中,无需事务之类的辅助。开发用如nodejs之类的语言来进行开发,对开发比较方便。 (3)mongodb本身的failover机制,无需使用如MHA之类的方式实现。
- 2.将mongodb作为类似redis ,memcache来做缓存db,为mysql提供服务,或是后端日志收集分析。 考虑到mongodb属于nosql型数据库,sql语句与数据结构不如mysql那么亲和 ,也会有很多时候将mongodb做为辅助mysql而使用的类redis memcache 之类的缓存db来使用。 亦或是仅作日志收集分析。