互联网运营商(NSP)的数据中心是数据最集中的地方,也正是因为海量的数据存储与访问,传统的存储架构已经无法满足了现有的需求。
比如每秒几十万次的随机IOPS、每秒10GB的流量,一般都需要使用高端存储,当然价格将不便宜。而且扩展性不好,扩容成本高。
业务的不断增加,导致互联网运营商逐步使用分布式系统来构建底层文件系统及数据库,比如Google的GFS+Bigtable
我们先来看一下为了应对大并发、大流量下,架构的逐步的变化,最后引入NoSQL。
传统数据库架构的演进
使用缓存技术
随着访问量的上升,流量的增加,使用mysql的架构的网站在数据库上出现了性能问题。
要应对传统的MySQL性能不足的问题,最容易想到的就是加缓存。如果只是使用文件缓存,由于多台Web服务器通过文件缓存不能共享,所以性能并没有那么显著的提升。
那么,我们能不能把缓存单独抽取出来,形成一个缓存服务器,使用这台服务器的大内存来进行缓存,而且这个缓存服务器还可以分布式部署,既保证了共享,又保证了性能。
最典型的代表就是Memcached缓存服务器,我们可以使用一致性Hash算法来进行多台服务器的扩展。
详细可见另一篇文章Memcached集群
MySQL的主从读写分离
使用缓存服务器只能算是权宜之计,访问量继续上涨的话,数据库还是会有瓶颈。因为互联网应用主要还在于读,既然读压力这么大,我们能不能使用集群来负载分担呢。
但是数据库因为需要保证它的原子性、一致性等,不能简单的使用多台服务器并发访问这种架构。
我们可以进行读写分离,将数据库分为Master和Slave两种角色,Master主要用于写,Slave主要用于读,而且Slave可以多台来负载均衡。
这样就可以提高读写性能和读库的可扩展性。具体可以参考数据库(七),读写分离到CQRS。
分表分库
使用 Memcached高速缓存和MySQL主从复制,可以缓解读压力,但是写压力怎么办?
最开始MySQL使用MyISAM引擎,它是使用表锁的,高并发有严重的锁问题。于是MySQL使用InnoDB引擎代替它。
同时我们还可以进行分表分库来缓解写压力:
有两种方式:
-
水平切:一张表,水平切分为多张表,每个节点存储一张子表,同时保留有另外两个副本。所以每张子表都包含原表所有的列。
-
垂直切分:也就是把一张表竖直的切分,原来的列拆分开,相当于把数据打散,可以获得超高的随机查询性能。
水平切分不需要人参与,盲切分即可。
而垂直切分相当于基于业务层面的切分,具有一定的人为介入度。
MySQL的问题
上面讲到了MySQL在应对大流量大并发的情况下做的努力,底层架构可谓是越来越复杂,相应的,上层的应用也会越来越复杂。虽然有些公司开发了中间件层来屏蔽开发的复杂性,但是避免不了架构的复杂。
那么之前的方法主要的缺点在于:
-
不够灵活:表结构更改困难,关系型数据库的表结构在一开始的时候就确定好了,很难更改。
-
扩展性的问题还是没有解决,MySQL推出的集群解决方案,只能保证可靠性,无法保证性能。
-
MySQL经常需要存放大文本字段,导致数据库恢复的时候特别慢。
所以关系型数据库很强大,但是不代表它能应付所有的应用环境,如果我们想要保证良好的扩展性、灵活性,可能需要对原有的功能做一些取舍。
下面我们将介绍一下CAP理论,它会从理论的角度告诉我们鱼和熊掌不可兼得
CAP理论
所谓CAP(Consistency , Availability , Partition Tolerance)理论,指的是一致性、性能、扩展性无法完全兼顾。也就是,我们想要良好的扩展性,则就无法像传统关系型数据那样能保证强一致性。
到底要不要保证强一致性,主要看客户的业务是否需要强一致性呢?
比如DNS解析,更换主机之后暂时不能全网刷新,这种短暂的不一致是可以接受的。
不过也不是完全不能保证一致性,我们有个模型叫NWR模型,可以在一致性和性能上实现平衡。
-
N
表示数据块的副本数, -
W
表示写入几份就返回成功。 -
R
表示为了保证读一致,客户端需要从保存副本的N
个节点中的几个同时读出数据。
比如N=3,W=1,也就是保存3副本,写入1份数据就返回成功。
-
如果R=1,当写入一份数据并返回成功以后开始读,因为同时只读一份数据,则既可能从未更新的两份数据中读,又可能从刚刚更新的数据中读,所以有可能读到未更新的版本。
-
如果R=2,同样也不能保证一定读到最新更新的版本。
-
如果R=3,则一定可以保证读到的3份数据有一份是最新的,只需要看谁的时间戳更晚就行。
如果W=2,就必须同步写成功2个副本才返回后成功,读的时候,只需要读2份就能保证最新了。
总结一下,只要$W+R>N$,就可以保证读一致。
NoSQL
弱一致性的数据库集群一般都不支持事务,同时也不支持关联查询,为什么呢?因为关联查询需要其他节点提供数据分片,也就是说一次查询需要调动其他的节点,这样并发性能将会非常差。
这种去掉关联性,只能保证弱一致性的轻量级的分布式数据库系统,就属于 NoSQL系统
NoSQL被我们用得最多的当数key-value存储,当然还有其他的文档型的、列存储、图型数据库、xml数据库等。
NoSQL分类
我们对现有的NoSQL进行简单的分类。
类型 | 部分代表 | 特点 |
---|---|---|
列存储 | Hbase | 方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。 |
文档存储 | MongoDB | JSON。因为存储的内容是一个文档,所以可以对某些字段建立索引 |
key-value存储 | Redis/Memcache | 可以通过key快速查询到其value |
图存储 | Neo4J | 如要存储的是图形关系,使用这种方式最好。 |
对象存储 | db4o/Versant | 通过对象的方式存取数据。 |
xml数据库 | Berkeley DB XML | 支持XML的内部查询语法,比如Xpath。 |
优势
-
更加的灵活
关系型数据库需要事先设定要数据字段,而NoSQL存储方式是键值对、列存储等,更加的灵活,特别适合处理非结构化、半结构化得的数据。
-
易扩展:去掉了关系数据库的关系型特性,数据无关系,更易于扩展。
-
高性能:因为NoSQL去掉了关系性,数据库结构简单,性能自然更高。
-
高可用:大多数NoSQL数据支持自动复制,可以可以在不太影响性能的情况下,方便的实现高可用架构。
-
自动分片:
关系型数据因为是结构化的,所以一般来说都是垂直扩展,也就意味着单台服务器需要持有整个数据库,扩展性自然不好。
NoSQL一般都支持自动分片,也就是会自动向多台服务器上分发数据,这一切是对应用透明的。
缺点
了解了优点我们也需要了解NoSQL的缺点。
NoSQL相对于关系型数据库,放弃了很多功能,比如事务、一致性等,而且目前来说还不够成熟,同时很多NoSQL不提供SQL支持。
所以最好的方法是了解那些场景适合NoSQL,然后与万能的关系型数据库结合来使用
一些涉及到K-V的存储、用户密码存储、Session会话存储等,可以使用NoSQL