引入
单机mysql的美好时代
在90年代,一个网站的访问量一般都不大,用单个数据库完全可以轻松应付。
那个时候,更多的是静态网页,动态交互类型的网站不多
上述架构上,我们来看看数据存储的瓶颈是什么?
DAL:Data Access Layer(数据库访问层-- Hibernate,MyBatis)
- 数据量的总大小一个机器放不下时
- 数据的索引(B+ Tree)一个机器的内存放不下时
- 访问量(读写混合)一个实例不能承受
如果满足了上面的1 or 3 个时,只能对数据库的整体架构进行重构
memcached + mysql + 垂直拆分
后来,随着访问量的上升,几乎大部分使用mysql架构的网站在数据库上都出现了性能问题,web程序不再仅仅专注于功能,同时也在追求性能。程序员们开始大量使用缓存技术来缓解数据库的压力,优化数据库的结果和索引。开始比较流行的是通过文件缓存来缓解数据库的压力,但是当访问量继续增大的时候,动态web机器通过文件缓存不能共享,大量的小文件缓存也带来了比较高的IO压力。此时,memcached自然就成为一个非常时尚的技术产品
Memcached作为一个独立的分布式的缓存服务器,为多个web服务器提供了一个共享的高性能缓存服务,在Memcached服务器上,又发展了根据hash算法来进行多台Memcached缓存服务的扩展,然后又出现了一致性hash来解决增加或减少缓存服务器导致重新hash带来的大量缓存失效的弊端。
mysql主从读写分离
由于数据库的写入压力增加,Memcached只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术来达到读写分离,以提高读写性能和读库的可扩展性。Mysql的master-slave模式成为这个时候的网站标配了。
分库分表+水平拆分+mysql集群
在Memcached的高速缓存,MySQL的主从复制,读写分离的基础之上,这时MySQL主库的写压力开始出现瓶颈,而数据量的持续猛增,由于MyISAM在写数据的时候会使用表锁,在高并发写数据的情况下会出现严重的锁问题,大量的高并发MySQL应用开始使用InnoDB引擎代替MyISAM。
ps:这就是为什么 MySQL 在 5.6 版本之后使用 InnoDB 做为默认存储引擎的原因 – MyISAM 写会锁表,InnoDB 有行锁,发生冲突的几率低,并发性能高。
同时,开始流行使用分表分库来缓解写压力和数据增长的扩展问题。这个时候,分表分库成了一个热门技术,是面试的热门问题也是业界讨论的热门技术问题。也就在这个时候,MySQL推出了还不太稳定的表分区,这也给技术实力一般的公司带来了希望。虽然MySQL推出了MySQL Cluster集群,但性能也不能很好满足互联网的要求,只是在高可靠性上提供了非常大的保证。
MySQL的扩展性瓶颈
MySQL数据库也经常存储一些大文本字段,导致数据库表非常的大,在做数据库恢复的时候就导致非常的慢,不容易快速恢复数据库。比如1000万4KB大小的文本就接近40GB的大小,如果能把这些数据从MySQL省去,MySQL将变得非常的小。关系数据库很强大,但是它并不能很好的应付所有的应用场景。MySQL的扩展性差(需要复杂的技术来实现),大数据下IO压力大,表结构更改困难,正是当前使用MySQL的开发人员面临的问题。
今天是什么样子
最前面的是企业级防火墙,后面通过负载均衡主机(软负载:Nginx,硬负载:F5)在 web 服务器集群之间进行调度,再由具体的 web 服务器(Tomcat)去访问缓存,访问数据库。
为什么用nosql
今天我们可以通过第三方平台(如:Google,Facebook等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL数据库的发展也却能很好的处理这些大的数据。
什么是nosql
概述
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,
泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题,包括超大规模数据的存储。
(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。
nosql代表
MongDB、 Redis、Memcache
关系数据库和nosql的区别
RDBMS
- 高度组织化的结构化数据
- 结构化查询语言(SQL)
- 数据和关系都存储在单独的表中
- 数据操作语言,数据定义语言
- 严格的一致性
- 基础事务
- ACID原则(事务的特性)
- A (Atomicity) 原子性:原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。比如银行转账,从A账户转100元至B账户,分为两个步骤:1)从A账户取100元;2)存入100元至B账户。这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元。
- C (Consistency) 一致性:一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务的运行不会改变数据库原本的一致性约束。
- I (Isolation) 独立性:所谓的独立性是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响。比如现有有个交易是从A账户转100元至B账户,在这个交易还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的
- D (Durability) 持久性:持久性是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。
nosql
- 没有声明性查询语言
- 没有预定义的模式
- 键 - 值对存储,列存储,文档存储,图形数据库
- 最终一致性,而非ACID属性
- 非结构化和不可预知的数据
- 高性能,高可用性和可伸缩性
- CAP定理(说明:C:强一致性 A:高可用性 P:分布式容忍性)
- Consistency(一致性), 数据一致更新,所有数据变动都是同步的
- Availability(可用性), 好的响应性能
- Partition tolerance(分区容错性) 可靠性
定理:任何分布式系统只可同时满足二点,没法三者兼顾。
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
- CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
- CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
- AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。
CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。
而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。
所以我们只能在一致性和可用性之间进行权衡,没有NoSQL系统能同时保证这三点。
举例:
- CA:传统Oracle数据库
- AP:大多数网站架构的选择
- CP:Redis、Mongodb
注意:分布式架构的时候必须做出取舍。
一致性和可用性之间取一个平衡。多余大多数web应用,其实并不需要强一致性。
因此牺牲C换取P,这是目前分布式数据库产品的方向。
当下NoSQL的经典应用
去 IOE 化。
ps:I 是指 IBM 的小型机,很贵的,好像好几万一台;O 是指 Oracle 数据库,也很贵的,好几万呢;M 是指 EMC 的存储设备,也很贵的。
NoSQL数据库是如何做到和关系型数据库互补的
提升写入性能
问题:
-
数据库系统大都使用的是传统的机械硬盘,对于机械磁盘的访问方式有两种:一种是随机IO,一种是顺序IO。随机IO就需要花费时间做磁盘寻道,一般来说,它的读写效率比顺序IO小两到三个数量级,所以我们想要提升写入性能就要尽量减少随机ID
-
以MySQL的InnoDB存储引擎来说,更新binlog、redolog、undolog都是在做顺序IO,而更新datafile和索引文件则是在做随机IO。为了减少随机IO的发生,关系数据库已经做了很多优化,比如说写入时先写入内存,然后批量刷新到磁盘上,但是随机IO还是会发生。
-
索引在InnoDB引擎中是以B+树方式来组织的,而MySQL主键是聚簇索引(一种索引类型,数据和索引数据放在一起),既然数据和索引数据放在一起,那么在数据插入或者更新的时候,我们需要找到要插入的位置,在把数据写到特定的位置上,这就产生了随机IO。而且一旦发生了页分裂,就不可避免的会出现数据的移动,也会极大的损耗写入性能
那么,NoSQL数据库是怎么解决这个问题的呢?
- 最常见的解决方式是,NoSQL数据库使用的基于LSM树的存储引擎
- LSM(Log-Structured Mege Tree)牺牲了一定的读性能来换取写入数据的高性能HBase、Cassandra、LevelDB都是用这种算法作为存储的引擎
- 它的思想很简单,数据首先会写入到一个叫做MemTable的内存结构中,在MemTable中的数据是按照写入的Key来排序的。为了防止MemTable里面的数据因为机器掉电或者重启而丢失,一般会通过Write Ahead Log的方式将数据备份到磁盘上
- MemTable在累积到一定规模时,它会被刷新生成一个新的文件,我们把这个文件叫做SSTable(Sorted String Table)。当SSTable达到一定数量时,我们会将这些SSTable合并,减少文件的数量,因为SSTable都是有序的,所以合并的速度也很快
- 当从LSM属里面读数据时,我们首先会从MemTable中查询数据,如果数据没有找到,再从SSTable中查询数据。因为存储的数据都是有序的,所以查找的效率很高,只是因为数据被拆分成多个 SSTable,所以读取的效率会低于 B+ 树索引。
和 LSM 树类似的算法有很多,比如说 TokuDB 使用的名为 Fractal tree 的索引结构,它们的核心思想就是将随机 IO 变成顺序的 IO,从而提升写入的性能。
提升扩展性
很多NoSQL在设计之初就考虑到了分布式和大数据存储的场景,比如MongoDB就有三个扩展性方面的特性。
- 其一是Replica,也叫做副本集,
- 可以理解为主从分离,就是通过将数据库拷贝成多份来保证当主挂掉后数据不会丢失。
- 同时,Replica还可以分担读请求。Replica中有主节点来承担写请求,并且把对数据变动记录到oplog中(类似binlog);从节点接收到oplog后就会修改自身的数据以保持和主节点的一致;
- 一旦主节点挂掉,MongoDB就会从从节点中选取一个节点称为主节点,可以继续提供写数据服务
- 其二是Shard,也叫做分片。
- 可以理解为分库分表,即将数据按照某种规则拆分成多份,存储在不同的机器上
- MongoDB 的 Sharding 特性一般需要三个角色来支持,
- 一个是 Shard Server,它是实际存储数据的节点,是一个独立的 Mongod 进程;
- 二是Config Server,也是一组 Mongod 进程,主要存储一些元信息,比如说哪些分片存储了哪些数据等;
- 最后是 Route Server,它不实际存储数据,仅仅作为路由使用,它从Config Server 中获取元信息后,将请求路由到正确的 Shard Server 中。
- 其三是负载均衡
- 就是当MongoDB发现Shard之间数据分布不均匀,会启动Blancer进程对数据做重新的分配,最终让不同Shard Server的数据尽量均衡
- 当我们的Shared Server存储空间不足需要扩容时,数据会自动被移动到新的Shard Server上,减少了数据迁移和验证的成本。
可以看到,NoSQL 数据库中内置的扩展性方面的特性可以让我们不再需要对数据库做分库分表和主从分离,也是对传统数据库一个良好的补充。
场景补充
除了可以提升性能之外,NoSQL 数据库还可以在某些场景下作为传统关系型数据库的补充,来看一个具体的例子。
如果有一个需求,是按照商品的名称模糊搜索到对应的商品。怎么做?
- 一开始,你认为这非常的简单,不就是在数据库里面执行一条类似:
select * fromproduct where name like ‘%***%’
的语句吗?可是在实际执行的过程中,却发现了问题。 - 比如这类语句并不是都能使用到索引,只有后模糊匹配的语句才能使用索引。比如
select * from product where name like ‘% 电冰箱
就没有使用到字段“name”上的索引,而select * from product where name like ‘索尼 %’
就使用了"name’"上的索引。而一旦没有使用索引就会扫描全表的数据,在性能上是无法接受的
解决方案:使用开源组件 Elasticsearch 来支持搜索的请求,它本身是基于“倒排索引”来实现的,那么什么是倒排索引呢?
倒排索引是指将记录中的某些列做分词,然后形成的分词和记录ID之间的映射关系。比如,当前项目中有如下记录:
那么,我们将商品名称做简单的分词,然后建立起分词和商品ID的对应关系,就像下面展示的这样:
这样,如果用户搜索电冰箱,就可以给他展示商品 ID 为 1 和 3 的两件商品了。
小结
- 在性能方面,NoSQL数据库使用一些算法将对磁盘的随机写转换成顺序写,提升了写的性能
- 在扩展性方面,NoSQL数据库天生支持分布式,支持数据冗余和数据分片的特性
- 在某些场景下,比如全文搜索功能,关系型数据库并不能高效的支持,需要NoSQL数据库的支持