一些基本名词
名词 | 解释 | 备注 |
cluster | 集群,一个ES集群可以由1个或者多个node节点组成 | cluster是由一组包含相同的cluster.name的节点构成。在不修改默认配置的情况下,各个node节点会自动组成一个叫elasticsearch的集群 |
node | 每个集群实例中的一个节点 | 当节点新加入或剔除时,集群会自动均分数据到各个节点上。 |
index | 虽然index翻译过来是索引的意思,这里是一个指向一个或多个物理shard的逻辑命名空间 | 可以把数据结构基本一致的文档放在同一个index里 |
type | 类型,低版本的ES可以在一个index里面多个type,目前已在ES7舍弃, 用一个_doc当做每个index的默认type | ES8将完全移除type |
document | ES存储的数据是文档型的,一个index里面可以包含多个文档 文档以json格式保存 | 我们往ES里PUT一条数据,就相当于生成一个文档,每个文档有一个唯一ID(_id) |
field | 字段,每个document由多个字段组成 | |
mapping | 各个字段的数据类型描述 我们在插入一条document的时候如果不指定mapping,ES会自动给我们生成mapping | ES支持的字段类型还是挺多,比如日期类型,数值类型,字符串等等,还支持搞附近的人geohash |
shard | 分片(index里的数据可以分片的形式存在多个shard里,方便横向扩容),每个index有1个或多个shard,如果你是多节点的集群的话,shard分布在不同的机器上 | shard还分为replica shard(备份分片 可调整)和primary shard(主分片 不可调整) |
replica | 副本,可以备份数据用,多个replica还可以提高查询的吞吐量 | |
query dsl | ES的复杂查询语句简称 | 就像我们查mysql叫查询语句叫sql一样 |
很多ES的教程都会跟你类比index=数据库 type=表 document=一行数据 field=字段 mapping=字段类型描述。其实这个不完全对,你往index里面put一条数据的时候就生成了一个document,每个文档有个唯一ID(_id),每个文档都有一个版本号,每次修改或删除文档时,_version就会自增。PUT一个document到一个index后,ES会自动给我们生成mapping,当然,你也可以自定义mapping。默认情况下,每个字段会被analyzed,就是会被自动分词掉,所以,有时候你存进去的字段里面类似xxx_xx_xxxx的字符串时候,你用xxx_xx_xxxx精准匹配竟然搜不到,就是因为被自动analyzed的原因。
倒排索引&&正排索引
假如说你去面试,你说用过ES,面试官肯定会问你倒排索引。那什么是倒排索引,是不是听着很niu bi。我就借着官网上了例子讲下:
例如,假设我们有两个文档的内容是
The quick brown fox jumped over the lazy dogQuick brown foxes leap over lazy dogs in summer
PUT完这两个之后,ES会按标准模式(大写转小写,单复数按单数,同义词存一个单词)生成索引,查询输入内容也会被同样的做标准化处理。
这时候存的索引可以简化为就像下面这样:
Term Doc_1 Doc_2-------------------------brown | X | Xdog | X | Xfox | X | Xin | | Xjump | X | Xlazy | X | Xover | X | Xquick | X | Xsummer | | Xthe | X | X------------------------
这就把内容分词后,拿分词的各个字段对应文档id,所以也形象的称之为倒排索引。类比传统像mysql这样的数据库,通常的做法是按id维度建立索引,查询想要快也是通过id查具体的内容。而ES按分词来建立与文档ID的做法,大大的提升全文搜索的速度。
当然 倒排索引还存了单词频率TF(即这个词在某个文档中的出现次数),还记录词文档出现的位置信息。所以我们每次查询的时候,查询结果都会返回一个_score,默认的查询结果按分值从高到低返回,词在文档中出现次数越多,词越相似,分值越高,这也符合我们想要的搜索结果。
ES默认的分词器对于中文内容只会单纯的拆分每个中文字,没法像英文文档,用默认的就能得到强大的效果,所以需要自行去找合适的中文分词器。
那我们再简单来讲讲正排索引,其实ES对于我们插入的内容,除了会分词生成倒排索引之外,它也会存每个字段的一些值(doc_value)。假如我们要根据查询结果按某个字段的的数值进行排序,前面我们讲到ES的field是支持很多数据类型的,所谓的正排索引就是单存的存了每个字段的原始值,所以,假如我们要对一个字符串类型的字段做排序,那么我们要手动把它设为not_analyzed,不然字符串类型的field是没办法排序的。
基本的增删改查
ES支持RESTful API 方便让我们执行很多操作。ES也有批量操作的Bulk API,其实就相当于一次性发送多个语句过去。让我们在上一篇文章的基础上,开启ES、Kibana,然后打开Kibana的DevTools
创建/删除一个index
PUT lib3DELETE lib3/
查看index的信息
GET /lib3/_settings
创建index的时候我们还可以手动配置setting设置分片数量,我单机ES默认就一个分片,一个备份
插入/更新一个document
POST /lib3/_doc/{_id}{"name":"cxk","age":18,"interests":["chang", "tiao", "rap", "lanqiu"]}
我们插入文档的时候可以手动指定文档id (一般插入的时候都还是不指定id的),当然如果你不指定ES也会自动生成一个,每个文档插入后也会生成一个自增的_version(插入的时候也可以指定version,默认的version是1到2^63-1 没修改一次自增1)。ES就是根据_version(版本控制),采用乐观锁来保证文档的更新。如果你指定了文档id,并且这个文档存在,那么ES就会覆盖更新这个文档。ES的实际上内部的更新策略就是先删除再插入,所以ES的更新效率并不高。李老之前在搞附近的人项目中提到ES也是支持geohash数据类型的,但是ES这样的架构,更适合搞附近的店,搞附近的人其实也能搞,就是效率不是很高。
按id精准查询document
GET /lib1/_doc/{_id}
我们可以在查询的时候指定返回的字段只要在后面加上?_source=字段1,字段2 如果要按某字段(有doc_value)排序,可以在后面加上sort=字段:desc。默认情况下查询结果是不返回_version的,如果你需要 可以在后面带上version=true
按field匹配
GET /lib3/_search?q=name:cxk
这时候我们发现查询结果回来的字段好多,因为基本上我们使用搜索引擎的时候,我们肯定事先是不知道内容到底在哪个document里的,不然还用搜么-,所以基本上是不可能按document id来查的。而基本上我们都要用到复杂查询,对于一些简单的需求,我们可以简单的用一个简单的语法?q=字段:查询输入关键词 再复杂一点,我们就要手撸更复杂的 query dsl语句了。
后话,本篇内容看似很长,其实并没有讲透很多内容,也只是对于ES做了一个简单的应用介绍。比如ES里面的倒排索引的底层数据结构到底是如何的,还有像ES的_version到底是怎么靠乐观锁来更新的,ES的各个node选master的算法,ES里的field支持的数据类型,以及它们怎么存储的,都没一一展开深入。不过这不重要,如果你不是专门从事搜索相关业务的技术开发,每个点都去深究,都可以写好几篇文章。我可能更多的想给一些业务开发,以及没用过ES或者想尝试一下ES的泥腿子们提供一个简单快速入门的ES的介绍,以及一些基本的使用。下一篇 我将花一整篇介绍query dsl!
话说这次修改路由器ssid名字我发现原来路由器的ssid有个32位长度限制,过了几天后隔壁的果然消停了好多,顺便也改了路由器的名字给我怼回来了
参考资料
https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
https://abcfy2.gitbooks.io/elasticsearch_the_definitive_guide/