一.海量数据的黎明
以前,因为缺乏划算的方式来存储所有信息,很多公司会忽略某些数据源,但是现在这样的处理方式会让公司丧失竞争力。存储和分析每一个数据点的需求在不断增长,这种需求的增长直接导致各公司电子商务平台产生了更多的数据。
过去,唯一的选择就是将收集到的数据删减后保存起来,例如只保存最近N天的数据。然而,这种方式只在短期内可行,它无法存储几个月或几年收集到的所有数据,因此,构建一种数学模型覆盖整个时间段或者改进一个算法,重跑以前所有的数据,以达到更好的效果。
Google和Amazon是认识到数据价值的典范,它们已经开始开发满足自己业务需求的解决方案。例如,Google在一系列的技术出版物中描述了基于商业硬件的可扩展的存储好处理系统。开源社区利用Google的这些思想实现了开源Hadoop项目的两个模块:HDFS和MapReduce。
Hadoop擅长存储任意的、半结构化的数据,甚至是非结构化的数据,可以帮助用户在分析数据的时候决定如何解释这些数据,同时允许用户随时更改数据分类的方式,一旦用户更新了算法,只需要重新分析数据。
目前Hadoop几乎是所有现有数据库系统的一种补充,它给用户提供了数据存储的无限空间,支持用户在恰当的时候存储和获取数据,并且针对大文件的存储、批量访问和流式访问做了优化。
二.列式数据库
列式数据库以列为单位聚合数据,然后将列值顺序地存入磁盘,这种存储方式不同于行式存储的传统数据库,行式存储数据库连续地存储整行。如下图:
列式存储的出现主要基于这样一种假设:对于特定的查询,不是所有的值都是必须的。尤其是在分析型数据库中,这种假设很常见,因此需要选择一种更为合适的存储模式。在这种新型的设计中,减少I/O只是总多主要因素之一,它还有其他优点:因为列的数据类型天生是相似的,即使逻辑上每一个之间有轻微的不同,但仍旧比按行存储的结构聚集在一起的数据更利于压缩,因为大多数的压缩算法只关注有限的压缩窗口。
像增量压缩或前缀压缩这类的专业算法,是基于列存储的类型定制的,因而大幅度提高了压缩比。更好的压缩比有利于在返回结果时降低带宽的消耗。
值得注意的是,从典型RDBMS的角度来看,HBase并不是一个列式存储的数据库,但是它利用了磁盘上的列存储格式,以列的格式在磁盘上存储数据。但与传统的列式数据库不同的是:传统的列式数据库比较适合实时存取数据的场景,HBase比较适合键值对的数据存取或者有序的数据存取。
三.关系型数据库系统的问题
RDBMS在设计和实现商业应用方面扮演了一个不可或缺的角色【至少在可预见的未来依旧如此】。只要用户需要保留用户、产品、会话、订单等信息,就会采用一些存储后端为前端应用服务器提供持久化数据的服务。这种结构非常适合有限的数据量,但对于数据急剧增长的情况,这种结构就显得力不从心了。
此外,还可以利用数据库内置的功能,如存储过程。当数据系统需要始终保证多张表的数据一致性时,可以利用存储过程【事务】来解决多个客户端同时更新数据的一致性问题。事务提供了原子性跨表更新数据的特性,可以让修改同时可见或同时不可见。RDBMS提供了所谓的ACID特性,这意味着用户数据是强一致性的。参照完整性负责约束不同的表结构之间的关系,利用特定域语言,即SQL能够写出任意复杂的查询语句。最终,用户不需要关系实际上数据是怎么存储的,只需要关系更高层次的概念,例如,表结构,表结构在应用程序中提供了非常固定的访问模型。
通常这种模式的设计能在较长的一段时间里满足需求。但随着用户数的增加,共享数据库服务器的压力也会越来越大。增加应用服务器的数量相对来说比较容易,因为应用服务器之间是共享中央数据库的,但随着共享中央服务器的CPU和I/O负载的上升,将很难预测能承受多久这种增长速度。
减少压力的第一步是增加用于并行读取的从服务器,将读写分离。这种方案保留了一个主数据库服务器,但是这个主数据库服务器现在只服务于写请求,这样做主要是考虑到大部分请求主要有用户浏览产生,因此写请求远小于读请求。如果这个方案也因用户数的持续增加而失败,或者降低了系统的性能,又该怎么办呢?
下一步常见的做法是增加缓存,如Memcached。现在可以将读操作接入到高速的内存中缓存数据的系统中,但是这种方案无法保证数据一致性,因为用户更新数据到数据库,而数据库并不会主动更新缓存系统中的数据,所以需要尽可能快地同步缓存和数据库视图,把更新缓存数据与更新数据库数据的时间差最小化。
虽然这种方案能够缓解读请求的压力,但是写请求压力的增加问题还是没有得到解决。一旦主数据库服务器写性能下降,可以把主服务器换成加强服务器,即垂直扩容,让加强服务器使用更多的资源。如果使用的是主从的配置方案,就必须要让从服务器的性能与主服务器一样,否则从服务器的更新速度就会跟不上主服务器。总之,与初始的情况相比要花费更多的资源。
随着项目的使用,项目会需要增加更多新功能,而这些新功能无疑都会转化为后台数据库的查询语句。以前顺利执行的SQL join语句执行突然变慢了,或者干脆无法执行,这时候就不得不采用逆范式化存储结构。如果情况越来越糟,就不得不停止使用存储过程,因为存储过程最后会慢的无法执行。本质上讲,减少数据库中的存储数据才能优化访问。
随着用户越来越多,负载会不断提高,合乎逻辑的方式就是不时地预先实现最昂贵的查询方案,从而给用户提供更快的数据服务。最终,不得不放弃辅助索引的使用,原因就是数据量增加的同时,索引量也大到了足以让数据库的性能直线下降。最后所能提供的查询模式只剩下了按照主键查询。
如果负载在未来的几个月里预期会增加一个数量级或更多又该怎么办?此时用户可以考虑将数据分区到多个数据库中,但是采用此方案会使运维操作变成噩梦,而且代价非常高昂,因此也不是最合理的解决方案。但从本质来讲,采用RDBMS也是因为没有其他可以选择的方案。
分区主要描述了逻辑上水平划分数据的方案。这个方案的特点是将数据分文件或分服务器存储,而不是连续存储。
数据的分区是在固定范围内实施的:在传入数据之前,必须提前划分好数据的存储范围,如果一个水平划分的压力超过其所能提供的容量,就需要将数据重新分区并迁移数据。重分区并迁移数据是非常消耗资源的操作,等同于数据重做,需要重新划分边界然后横向拆分。大规模的复制操作会消耗大量的I/O资源,同时还会临时性地增加存储需求。在对数据重分区的过程中,客户端应用仍然会有更新操作要执行,不过此时的更新操作受重分区的影响会执行得非常慢。
可以采用虚拟分区的方式来减少这种资源消耗,虚拟分区按照关键词定义范围较大的数据分区,每个服务器加载同等数量的数据分区。但是在新增服务器的时候,需要重新加载数据分区,并且这个过程依旧需要将数据迁移到新服务器。分区是简单的完全脱离用户操作的事后操作,如果没有数据库的支持,可能会对生产系统造成严重破坏。
四.非关系型数据库系统【Not-Only-SQL,NoSQL】
标示符号化实际上是一个不错的选择:最新的存储系统不提供通过SQL查询数据的手段,只提供一些比较简单、类似于API接口的方式来存取数据。但是,也有一些工具为NoSQL数据存储提供了SQL语言的入口,用于执行一些关系数据库中常用的复杂条件查询。因此,从查询方式上的限制来说,关系型数据库和非关系型数据库并没有严格的区分。
实际上两者在底层上是有区别的,尤其涉及到模式或者ACID事务特性时,因此这与实际的存储架构是相关的。很多这一类的新系统首先是做的是:抛弃一些限制因素以提升扩展性。例如,它们通常不支持事务或辅助索引。更重要的是,这一类系统是没有固定模式的,可以随着应用的改变而灵活变化。
一开始的一致性是保证数据库客户端操作的正确性,数据库必须保证每一步操作都是从一个一致的状态到下一个一致的状态。系统没有明确地指定如何实现这个功能,以便系统可以有多种选择。最终,系统要选择是进入下一个一致的状态,还是回退到上一个一致的状态,从而保证一致性。
一致性可以按照严格程度由强到弱分类,或者按照对客户端的保证程度分类,下面是一个非正式的分类列表。
严格一致性:数据的变化是原子的,一经改变即时生效,这是一致性的最高形式。
顺序一致性:每个客户端看到的数据依照它们操作执行的顺序而变化。
因果一致性:客户端以因果关系顺序观察到数据的改变。
最终一致性:在没有更新数据的一段时间里,系统将通过广播保证副本之间的数据一致性。
弱一致性:没有做出保证的情况下,所有的更新会通过广播的形式传递,展现给不同客户端的数据顺序可能不一样。
采用最终一致性策略的系统还可以细分为几个子类,并且这些子类策略还可以共存。
亚马逊的首席技术官Werner Vogels提出了CAP定理,其中指出,一个分布式系统只能同时实现一致性、可用性和分区容忍性【或分区容错性】中的两个。CAP定理指出,开发一套同时满足以上需求的分布式系统是比较困难的。
在一系列的研究结果里发现,在较大型的分布式系统中,由于网络分隔,一致性与可用性不能同时满足。这意味着三个要素最多只能同时实现两个,不可能三者兼顾;放宽一致性的要求会提升系统的可用性,提升一致性意味着系统需要牺牲一定的可用性。放宽一致性来提高系统可用性是一个非常有效的提议,不过这种方案会强制让应用层去解决一致性的问题,因此也会增加系统的复杂度。