NoSQL=Not Only SQL,虽然关系型数据库凭借强大的SQL和ACID的属性得到广泛应用,但并不意味着没有缺憾,比如关系型数据库无法存储数据结构存的是行记录,比如关系型数据库的schema扩展很不方便,比如关系型数据库在大数据场景下I/O较高,比如关系型数据库的全文搜索功能比较弱,因此诞生了不同的NoSQL解决方案,这些方案与关系型数据库比在某些应用场景下表现更好,但NoSQL方案带来的优势本质上是牺牲了ACID特性的某个或者某几个,因此NoSQL方案更适合作为SQL的一个有力补充
K-V 存储的全称是 Key-Value 存储,其中 Key 是数据的标识,和关系数据库中的主键含义 一样,Value 就是具体的数据,Redis是K-V存储的典型代表,它是一款开源(基于 BSD 许可)的高性能K-V缓存和存储系统,Redis的Value是具体的数据结构,包括 string、hash、list、set、sorted set、bitmap和hyperloglog ,所以常常被称为数据结构服务器
以 List 数据结构为例,Redis提供了如下的典型的操作
如果用关系数据库来实现以上这些功能,就会变得很复杂。 例如, LPOP 操作是移除井返回 key 对应的 list 的第一个元素。 如果用关系数据库来存储,为了达到同样目的,则需要进行 如下操作:
可以看出关系数据库的实现很麻烦,而且需要进行多次 SQL 操作,性能很低
Redis的缺点主要体现在并不支持完整的 ACID 事务,Redis虽然提供事务功能,但 Redis的事务和关系数据库的事务不可同日而语, Redis的事务只能保证隔离性和一致性(I和C),无法保证原子性和持久性(A和D)
具体实现原理如下 :
- RDB持久化只备份当前内存中的数据集,事务执行完毕时,其数据还在内存中,并未立即写入到磁盘,所以RDB持久化不能保证Redis事务的持久性。
- AOF持久化是先执行命令,执行成功后再将命令追加到日志文件中 。 即使 AOF 每次执行命令后立刻将日志文件刷盘,也可能丢失1条命令数据,因此AOF也不能严格保证Redis事务的持久性
举一个例子来说明Redis事务和数据库事务的差别:
例如,用户A关注了用户B, 实际上产生了两条数据操作:A 的“ 关注” 列表要增加B, B的“粉丝”列表要增加 A, 如果用数据库来存储关系数据,通过事务可以保证这两个操作要么同时成功,要么同时失败,而使用 Redis事务来处理,可能将B加入了A的关注列表 ,但没有将A加入B的粉丝列表。
虽然Redis并没有严格遵循 ACID 原则,但实际上大部分业务也不需要严格遵循 ACID原则,以上述的微博关注操作为例,即使系统没有将A加入B的粉丝列表,其实业务影响也非常小,因此我们在设计方案时,需要根据业务特性和要求来确定是否可以用 Redis,而不能因为Redis不遵循ACID原则就直接放弃
为了解决关系数据库 schema 带来的问题,文档数据库应运而生,文档数据库最大的特点就是 no-schema,可以存储和读取任意的数据,目前绝大部分文档数据库存储的数据格式是JSON(或者BSON),因为 JSON数据是自描述的,无须在使用前定义宇段,读取一个JSON中不存在的字段也不会导致SQL那样的语法错误
文档数据库的no-schema特性,给业务开发带来如下几个明显的优势:
- JSON是一种强大的描述语言 ,能够描述复杂的数据结构,例如,我们设计一个用户管理系统,用户的信息有ID 、 姓名、性别、爱好、邮箱、地址、学历信息。其中爱好是列表(因为可以有多个爱好),地址是一个结构,包括省、市、区楼盘地址,学历包括学校、专业、入学毕业年份信息等
- 如果我们用关系数据库来存储,需要设计多张表,包括基本信息(列 : D、姓 名、性别、邮箱)、爱好(列:ID、爱好)、地址(列:省、市、区、详细地址)、学历(列:入 学时间、毕业时间、学校名称、专业),而使用文档数据库,一个 JSON 就可以全部描述
{
"id":1000,
"name":"Davieyang",
"sex":"male",
"hobbies":[
"football",
"playing",
"singing"
],
"email":"[email protected]",
"address":[
"province":"Beijing",
"city":"Beijing",
"district":"Yizhuang",
"detail":"Beijing Road 10000"
],
"education":[
{
"begin":"2001-09-01",
"end":"2004-07-31",
"school":"BIM",
"major":"Computer Science&Technology"
},
{
"begin":"2004-09-01",
"end":"2007-07-01",
"school":"SCUT",
"major":"Computer Science & Technology"
}
]
}
通过这个样例我们看到,使用 JSON 来描述数据,比使用关系型数据库表来描述数据方便和容易得多而且更加容易理解
文档数据库的这个特点,特别适合电商和游戏这类的业务场景。以电商为例,不同商品的属性差异很大,例如,冰箱的属性和笔记本电脑的属性差异非常大,即使是同类商品也有不同的属性。例如,LCD和LED显示器,两者有不同的参数指标
这种业务场景如果使用关系数据库来存储数据,就会很麻烦,而使用文档数据库,简单、方便, 扩展新的属性也更加容易
顾名思义,列式数据库就是按照列来存储数据的数据库,与之对应的传统关系数据库被称为“行式数据库”,因为关系型数据库是按照行来存储数据的
关系型数据库按照行式来存储数据,主要有如下几个优势:
基于上述列式存储的优缺点,一般将列式存储应用在离线的大数据分析和统计场景中,因为这种场景主要是针对部分列进行操作,且数据写入后就无须再更新删除
传统的关系型数据库通过索引来达到快速查询的目的,但是在全文搜索的业务场景下,索引也无能为力,主要体现在如下几点:
需要考虑Not Only SQL通过引入全文搜索引擎来弥补关系型数据库的缺陷
全文搜索引擎的技术原理被称为“倒排索引”(Inverted index),也常被称为反向索引 、置入档案或反向档案,是一种索引方法,其基本原理是建立单词到文档的索引。之所以被称为“倒排"索引,是和“正排"索引相对的,“正排索引”的基本原理是建立文档到单词的索引
倒排索引和正排索引虽然是相反的两个索引技术,但实际应用的时候并不是非此即彼,而是将两者结合起来,用到倒排索引的地方几乎肯定会用到正排索引,例如,用户搜索文章时用的是倒排索引,系统根据搜索关键词搜索到文档 ID ,然后系 统根据文档 ID 去查询文档名称展示给用户:当用户单击具体的某篇文档时,系统根据文档 ID 查询文档 内容井展示给用户,此时用的是正排索引
全文搜索引擎的索引对象是单词和文档,而关系数据库的索引对象是键和行,两者的术语差异很大,不能简单地等同起来。因 此,为了让全文搜索引擎支持关系型数据的全文搜索,需要做一些转换操作,即将关系型数据转换为文档数据,目前常用的转换方式是将关系型数据按照对象的形式转换为JSON文挡,然后将JSON文档输入全文搜索引擎进行索引
全文搜索引擎能够基于JSON文档建立全文索引,然后快速进行全文搜索,以 Elasticsearch为例,其索引基本原理如下:
需要注意的是,实际应用中的转换,并不限定为只能单表到文档的转换,而可以根据搜索需要,灵活地从表转换到文档,可以单表转换到文挡,也可以多表联合起来转换为单一文档。例如, 一个学生管理系统 , 数据库表 可以设计为基础信息表(包含学号、姓名、性别、年龄、籍贯等)、专业信息表(学院、专业等)、成绩信息表(学科、成绩等)、社团信息表(是否为学生会干部、学生团体等)等多个表格,但 最终转换为 JSON 文档时, 可以一个学生有一个 JSON 文档,文档中包含学生的所有信息。