NoSQL指南
一、数据库发展
1、早期出现的数据库包括平面文件数据管理系统、分层数据管理系统和网络数据管理系统,分别对应的数据结构是线性表、树和图。
平面文件数据管理系统是使用磁带对数据进行顺序存储的,带来的问题不言而喻,就是查找效率低下、重复数据较多(重复数据会带来修改数据时的一致性问题)、数据结构修改时存储结构和程序都可能要面临修改。
分层数据管理系统是类似树状的结构,通过根节点可以找到相关的从属信息,查找效率有所提升。
网络数据管理系统是把数据记录在节点中,通过有向图的边来标明数据之间的联系,是一种多对多的关系,无疑这是一种比较复杂的系统,其设计和维护的成本比较高,但并没有解决平面文件系统和分层数据系统的问题。
2、关系型数据库
不得不说,关系型数据库是对早期数据库系统的重大改进,解决了很多问题,它通过改善存储方式、内存管理热数据、易管理更实用的数据结构(如索引可以提升查找效率、视图可以控制数据范围、约束可以对数据进行校验)及与存储结构相分离的查询语言,使它成为替代早期数据管理系统的流行数据库系统。
3、NoSQL数据库
关系型数据库相对早期数据库而言,效率有了很大提升,在应对一般的应用时都能够从容处理。但自从web时代到来,要求数据库拥有大批量读写操作的处理能力、较低的延迟和较短的响应时间及更高的可用性。
NoSQL就应运而生了,它更容易进行分布式,通过横向扩展(指增加机器数量,纵向扩展指增加机器配置)来提升数据处理能力,而关系型数据库要实现这一点成本很高。同时也因为分布式,NoSQL的可用性也提升了,当一台机器出现故障时系统仍可正常工作。
NoSQL并不能替代关系型数据库,在不同的应用场景中,需要选择适合的数据库系统。对于服务用户数量较少的应用,如企业内部应用,仍然是关系型数据库显得更有竞争力,其设计简单明了可维护性较好;而对于面向互联网海量数据的应用而言,NoSQL能用较低的成本来提升性能、灵活性和可用性。
常见的 NoSQL数据库有键值数据库、文档数据库、列族数据库和图数据库。
二、键值数据库
它是非关系型数据库中最简单的实现,特点是设计简洁、存取效率高和易于缩放(动态增加删减机器)。
一般都设计有缓存系统,即将数据库中的数据保存在内存中,以便于更高效的获取和设置。为了可以在内存中存储更多的数据,可以将数据进行压缩,但即便这样,也无法保证将一个庞大的数据库全部保存在内存中,这时就需要一定的策略进行调整,通常所使用的方案是只保留热数据(即最近活跃的数据,而把长期不活跃的数据从内存中清除)
最常见的分布式设计方案是主从复制模型,即有一台主服务器作为可读可写服务器,其它机器作为只读服务器。任何写入操作都写入到主服务器,再由主服务器同步到其它从服务器,无疑这种方案产生了一个单点,即如果主服务器故障,系统将无法提供写功能。对于此问题的其中一种解决方案,是让所有从服务器监控主服务器的状态,一旦主服务器故障,则依据一定规则,将其中一台从服务器提升为新的主服务器。主从复制模型适用于读操作相对更频繁的应用场景。
当读写都比较频繁时,主从复制模型就会带来性能瓶颈,这时可以采用无主复制模型,顾名思义就是没有主服务器,各服务器间之间的地位是平等的,都具有读写功能,当一台服务器数据发生变化时,将向其它服务器进行同步。但这也带来一个问题,就是不同服务器同时修改某个数据时,如何协同和处理,如最常见的订票系统。对此的解决思路是,将指定数据的写入绑定到固定的指定服务器上。其中一种方案是,对要写入的键进行哈希编码(哈希函数的结果可能发生碰撞,常用的解决方案是存储一个链表,来允许碰撞的多个结果),再模服务器的数量,就可以将该键的写入绑定到第几台机器上,并且可以把负载相对平均的进行分摊。
使用集群(分布式)方案时,复制操作的配置要求在效率和安全性之间取得一个平衡。副本越多,损失数据的可能性就越小,但性能可能下降。读取操作时,部分服务器可能因尚未同步完成导致数据不同,可以设定系统在至少获得几个相同的应答之后,才认为是有效数据。
要注意不同的键值数据库对键和值的类型和长度有没有限制,有没有对基于值的查询做优化(默认键值数据库只根据键来操作数据,但有些实现也会为值编制索引,以提升该方面查询速度)。
可以用命名空间或桶来分隔同一个键值数据库中的数据,如多个应用程序使用同一个数据库时,就可以各自使用一个命令空间,而不需要开启多个数据库服务,不同的命令空间其键是可以重复的,就像完全独立的数据库一样。
在使用键值数据库时,应避免键或值过大,比如,把一个用户的所有数据组织成JOSN作为一个值存储,如果真的需要这样做,那么文档数据库可能是更好的选择,因为过于庞大的值会降低读取和写入的性能,并且在存储层面,由于不断增大的数据值,可能会导致重新分配存储区域。但这并不是说键值数据库中的值不可以使用组合数据,恰恰相反,当某几个常常一起使用的属性(比如帐号和密码)就可以存储在一个键中(以JSON、列表等形式),或者是某些因类型不同而拥有不同属性的数据(如不同渠道的的特有信息,不同支付平台的付费返回数据)都可以存放在同一个键中。
一些键值数据库支持TTL(存活时间),对某些键值对设置TTL,可以方便的实现类似 session 超时的功能。
可以将一组同类型的键使用有序列表存储起来,这样可间接实现数据的排序功能,对于搜索查找可能会有很大的效率提升。
在使用NoSQL的过程中,你可能需要在某些时候抛弃曾经在使用关系型数据库时带来的规范化思维,比如避免重复数据。在某些时候,建立一些索引、将某些数据重复存储到另一个数据结构中来提升查询效率是值得的,这也叫去规范化。
最常用的键值数据库是 Redis,它使用主从复制模型,它是单线程的,另一个常用的键值数据库是memcache,它是多线程,但是不支持自动持久化,在大多数情况下,redis 可以替代 memcache。
三、文档数据库
键值数据库常常都是以内存数据库的方式实现,存取效率很高,但复合查询比较麻烦,而文档数据库则更像是对关系数据库的改良,它本身是基于磁盘,在内存中维护索引,效率也很高,尤其是在大数据情况下,关系型数据库的效率会急剧下降,但文档数据库却不会,最常用的文档数据库是 mongdb。
关系数据库的设计侧重规范化来去除冗余数据,而文档数据库的设计可能相对更侧重去规范化来提升查询效率,而对冗余数据的校验同步等工作由程序来保证,当然规范化和去规范化是相对的,需要达到怎样的平衡,需要根据具体项目具体需求而定。
文档数据库如 mongdb 对数据的增删改查操作提供了一系列接口,使用起来会感觉很便捷,这也是跟关系数据库有类似体验的地方。与关系数据库不同的是,一般文档数据库不支持低效的 join 操作(这也是要去规范化提升查询效率的原因),也不支持事务。
对于文档数据库,可以分片,将海量数据库分割到多台服务器上。
应该避免移动过大的文档,因为文档本质上是写在磁盘中,如果容量迅速增大,可能需要重新分配一块足够大的区域去保存,这就跟使用 c++ 的 std::vector 一样,必要的时候可以考虑在一开始就分配适当的容量。
使用文档数据库,可能需要较多的使用索引,索引太少则读取数据的效率不高,但如果太多了,又会影响写入效率。
更多的使用细则,可以参考具体的某一个文档数据库,如 mongdb 的使用,会让你加深理解,而不仅仅停留在理论。
四、列族数据库
五、图数据库