(1)思考:大规模数据如何检索?
如:当系统数据量上了10亿、100亿条的时候,我们在做系统架构的时候通常会从以下角度去考虑问题:
1)用什么数据库好?(mysql、sybase、oracle、达梦、神通、mongodb、hbase…)
2)如何解决单点故障;(lvs、F5、A10、Zookeep、MQ)
3)如何保证数据安全性;(热备、冷备、异地多活)
4)如何解决检索难题;(数据库代理中间件:mysql-proxy、Cobar、MaxScale等;)
5)如何解决统计分析问题;(离线、近实时)
(2)传统数据库的应对解决方案
对于关系型数据,我们通常采用以下或类似架构去解决查询瓶颈和写入瓶颈:
解决要点:
1)通过主从备份解决数据安全性问题;
2)通过数据库代理中间件心跳监测,解决单点故障问题;
3)通过代理中间件将查询语句分发到各个slave节点进行查询,并汇总结果
(3)非关系型数据库的解决方案
对于Nosql数据库,以mongodb为例,其它原理类似:
解决要点:
1)通过副本备份保证数据安全性;
2)通过节点竞选机制解决单点问题;
3)先从配置库检索分片信息,然后将请求分发到各个节点,最后由路由节点合并汇总结果
另辟蹊径——完全把数据放入内存怎么样?
我们知道,完全把数据放在内存中是不可靠的,实际上也不太现实,当我们的数据达到PB级别时,按照每个节点96G内存计算,在内存完全装满的数据情况下,我们需要的机器是:1PB=1024T=1048576G
节点数=1048576/96=10922个
实际上,考虑到数据备份,节点数往往在2.5万台左右。成本巨大决定了其不现实!
从前面讨论我们了解到,把数据放在内存也好,不放在内存也好,都不能完完全全解决问题。
全部放在内存速度问题是解决了,但成本问题上来了。
为解决以上问题,从源头着手分析,通常会从以下方式来寻找方法:
1、存储数据时按有序存储;
2、将数据和索引分离;
3、压缩数据;
这就引出了Elasticsearch。
1.2 Lucene与ES关系?
1)Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
2)Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
1.3 ES主要解决问题:
1)检索相关数据;
2)返回统计结果;
3)速度要快。
1.4 ES工作原理
当ElasticSearch的节点启动后,它会利用多播(multicast)(或者单播,如果用户更改了配置)寻找集群中的其它节点,并与之建立连接。这个过程如下图所示:
1.5 ES核心概念
1)Cluster:集群。
ES可以作为一个独立的单个搜索服务器。不过,为了处理大型数据集,实现容错和高可用性,ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。
2)Node:节点。
形成集群的每个服务器称为节点。
3)Shard:分片。
当有大量的文档时,由于内存的限制、磁盘处理能力不足、无法足够快的响应客户端的请求等,一个节点可能不够。这种情况下,数据可以分为较小的分片。每个分片放到不同的服务器上。
当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。即:这个过程对用户来说是透明的。
4)Replia:副本。
为提高查询吞吐量或实现高可用性,可以使用分片副本。
副本是一个分片的精确复制,每个分片可以有零个或多个副本。ES中可以有许多相同的分片,其中之一被选择更改索引操作,这种特殊的分片称为主分片。
当主分片丢失时,如:该分片所在的数据不可用时,集群将副本提升为新的主分片。
5)全文检索。
全文检索就是对一篇文章进行索引,可以根据关键字搜索,类似于mysql里的like语句。
全文索引就是把内容根据词的意义进行分词,然后分别创建索引,例如”你们的激情是因为什么事情来的” 可能会被分词成:“你们“,”激情“,“什么事情“,”来“ 等token,这样当你搜索“你们” 或者 “激情” 都会把这句搜出来。
1.6 ES数据架构的主要概念(与关系数据库Mysql对比)
关系数据库 ⇒ 数据库 ⇒ 表 ⇒ 行 ⇒ 列(Columns)
Elasticsearch ⇒ 索引(Index) ⇒ 类型(type) ⇒ 文档(Docments) ⇒ 字段(Fields)
(1)关系型数据库中的数据库(DataBase),等价于ES中的索引(Index)
(2)一个数据库下面有N张表(Table),等价于1个索引Index下面有N多类型(Type),
(3)一个数据库表(Table)下的数据由多行(ROW)多列(column,属性)组成,等价于1个Type由多个文档(Document)和多Field组成。
(4)在一个关系型数据库里面,schema定义了表、每个表的字段,还有表和字段之间的关系。 与之对应的,在ES中:Mapping定义索引下的Type的字段处理规则,即索引如何建立、索引类型、是否保存原始索引JSON文档、是否压缩原始JSON文档、是否需要分词处理、如何进行分词处理等。
(5)在数据库中的增insert、删delete、改update、查search操作等价于ES中的增PUT/POST、删Delete、改_update、查GET.
3.索引
Elasticsearch最关键的就是提供强大的索引能力,一切设计都是为了提高搜索的性能
为了提高搜索的性能,难免会牺牲某些其他方面,比如插入/更新,否则其他数据库不用混了。前面看到往Elasticsearch里插入一条记录,其实就是直接PUT一个json的对象,这个对象有多个fields,那么在插入这些数据到Elasticsearch的同时,Elasticsearch还默默1的为这些字段建立索引–倒排索引,因为Elasticsearch最核心功能是搜索。
Elasticsearch是如何做到快速索引的
Elasticsearch使用的倒排索引比关系型数据库的B-Tree索引快,为什么呢?
我们知道,二叉树查找效率是logN,同时插入新的节点不必移动全部节点,所以用树型结构存储索引,能同时兼顾插入和查询的性能。因此在这个基础上,再结合磁盘的读取特性(顺序读/随机读),传统关系型数据库采用了B-Tree/B+Tree这样的数据结构:
为了提高查询的效率,减少磁盘寻道的次数,将多个值作为一个数组通过连续区间存放,一次寻道读取多个数据,同时也降低树的高度。
什么是倒排索引?
倒排索引是单词-文档举证的一种存储方式。通过倒排索引可以快速根据单词找到包含这个单词的所有文档。
如上图所示,倒排索引主要由单词词典和倒排文件组成,单词词典存放在内存中,是组成所有文档的单词的集合,单词词典内的每条索引项记载了单词本身的一些信息和指向倒排列表的指针,通过这个指针就可以找到对应的倒排列表,而倒排列表记载了出现过某个单词的所有文档的文档列表和单词在文档中出现的位置信息,每条记录称为倒排向项。而倒排文件是倒排列表在磁盘上的物理存储。
以下是三种倒排索引
记录单词频率,文档频率和单词在文档中出现的位置将作为搜索结果排序的一个重要因子,可以利用倒排索引的其他信息计算文档得分,优化排序。
单词词典
如何快速的在单词词典中定位到某个单词,通过指针获得倒排索引项对于搜索的相应速度非常重要。随着网络新词的出现,单词词典需要自身维护,如何高效的构建和查找,对于单词词典非常重要。常用的数据结构有哈希加链表和树形词典结构。
主体部分是哈希表,哈希表的每一项都会保存一个指针,指针指向冲突链,冲突链中保存相同哈希值的单词,不同的单词可能存在相同的哈希值,所以会形成链表结构。
建立哈希加链表结构
在建立索引的过程中,单词词典会被建立起来,在解析文档的过程中,对于文档中出现的某个单词T,首先利用哈希函数获得的哈希值,找到对应的哈希项,找到对应的冲突链表,遍历冲突链表,如果存在这个单词则说明之前出现过,则继续下一个单词。如果在冲突链表中没有这个单词,说明首次碰到,则加入到冲突链表中,当所有文档都解析完成后,单词词典就建立起来了。
在哈希加链表结构中查找某个单词
对单词T哈希,定位哈希表,通过指针找到冲突链表,遍历相应的哈希链表找到这个单词,进而获得这个单词的倒排列表,如果没有找到这个单词则返回空,说明没有文档包含这个单词。
树形结构
主要利用B树高效查找的特点。B树和哈希的查找方式不同,需要字典项进行排序,而哈希并不要求此过程,形成层级查找结构,先找到子树,再进行顺序遍历即可找到匹配的叶子节点。最底层的叶子节点存储单词的地址信息。
倒排列表
倒排列表主要记录那些文档包含某个单词,一个单词会被很多文档包含,这里记录的是文档编号(docId),单词在这个文档出现的TF,以及单词在文档的哪些位置出现,最终形成倒排项。
在实际存储中,为了减少存储空间,通常不会直接的存储docId,而是存储文档编号的差值,文档差值指的是倒排列表中相邻两个倒排索引项DocID的差值,这是因为在索引构建过程中,可以保证后面出现的文档编号要大于前面出现的文档编号。
以上就是我理解的倒排索引