IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> ElasticSearch简介及ElasticSearch部署、原理和使用介绍 -> 正文阅读

[大数据]ElasticSearch简介及ElasticSearch部署、原理和使用介绍

ElasticSearch简介及ElasticSearch部署、原理和使用介绍

第一章:elasticsearch简介

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。

第一节 elasticSearch的使用场景

1、 为用户提供按关键字查询的全文搜索功能。

2、 实现企业海量数据的处理分析的解决方案。大数据领域的重要一份子,如著名的ELK框架(ElasticSearch,Logstash,Kibana)。

第二节 常用数据库存储对比

redismysqlelasticsearchhbasehadoop/hive
容量/容量扩展较大海量海量
查询时效性极高中等较高中等
查询灵活性较差 k-v模式非常好,支持sql较好,关联查询较弱,但是可以全文检索,DSL语言可以处理过滤、匹配、排序、聚合等各种操作较差,主要靠rowkey,scan的话性能不行,或者建立二级索引非常好,支持sql
写入速度极快中等较快较快
一致性、事务

第三节 elasticsearch的特点

1.3.1 天然分片,天然集群

es 把数据分成多个shard,下图中的P0-P2,多个shard可以组成一份完整的数据,这些shard可以分布在集群中的各个机器节点中。随着数据的不断增加,集群可以增加多个分片,把多个分片放到多个机子上,已达到负载均衡,横向扩展。

在这里插入图片描述

在实际运算过程中,每个查询任务提交到某一个节点,该节点必须负责将数据进行整理汇聚,再返回给客户端,也就是一个简单的节点上进行Map计算,在一个固定的节点上进行Reduces得到最终结果向客户端返回。

这种集群分片的机制造就了elasticsearch强大的数据容量及运算扩展性。

在这里插入图片描述

1.3.2 天然索引

ES 所有数据都是默认进行索引的,这点和mysql正好相反,mysql是默认不加索引,要加索引必须特别说明,ES只有不加索引才需要说明。

而ES使用的是倒排索引和Mysql的B+Tree索引不同。

第四节 lucene与elasticsearch的关系

lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来的应用。

好比lucene是类似于发动机,而搜索引擎软件(ES,Solr)就是汽车。

目前市面上流行的搜索引擎软件,主流的就两款,elasticsearch和solr,这两款都是基于lucene的搭建的,可以独立部署启动的搜索引擎服务软件。由于内核相同,所以两者除了服务器安装、部署、管理、集群以外,对于数据的操作,修改、添加、保存、查询等等都十分类似。就好像都是支持sql语言的两种数据库软件。只要学会其中一个另一个很容易上手。

从实际企业使用情况来看,elasticSearch的市场份额逐步在取代solr,国内百度、京东、新浪都是基于elasticSearch实现的搜索功能。国外就更多了 像维基百科、GitHub、Stack Overflow等等也都是基于ES的。

第二章:elasticSearch的安装部署(含kibana)

2.1 下载地址

https://www.elastic.co/cn/downloads/past-releases#elasticsearch

https://www.elastic.co/cn/downloads/past-releases#kibana

【注意】:

  1. es和kibana版本下载需一致
  2. 目前生产环境大多采用大版本6.x.x;7.x.x版本相对较新,但部署流程都一样

2.2 机器规划

3台机器:

11.8.37.50 ops01
11.8.36.63 ops02
11.8.36.76 ops03

wangting@ops01:/home/wangting >cat /etc/hosts
127.0.0.1 ydt-cisp-ops01
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

# elasticsearch
11.8.37.50 ops01
11.8.36.63 ops02
11.8.36.76 ops03

【注意】:如果在各节点的/etc/hosts中都配置了节点的ip解析,那后续在配置文件中,相关的ip配置都可以用解析名代替;

例如:network.host: 11.8.37.50 等同于 network.host: ops01

2.3 下载安装包

wangting@ops01:/opt/software >ll | grep 6.6.0
-rw-r--r-- 1 wangting wangting     114106988 Aug  4 14:40 elasticsearch-6.6.0.tar.gz
-rw-r--r-- 1 wangting wangting     180704352 Aug  4 14:40 kibana-6.6.0-linux-x86_64.tar.gz

2.4 环境优化

2.4.1 优化1

系统允许 Elasticsearch 打开的最大文件数需要修改成65536

wangting@ops01:/opt/software >sudo vim /etc/security/limits.conf
# End of file
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 65536

# 断开重连会话
wangting@ops01:/home/wangting >ulimit -n
65536

这个配置不优化启动服务会出现:
[error] max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536] elasticsearch

2.4.2 优化2

允许最大进程数配置修该成4096;不是4096则需要修改优化

wangting@ops01:/home/wangting >sudo vim /etc/security/limits.d/20-nproc.conf
# Default limit for number of user's processes to prevent
# accidental fork bombs.
# See rhbz #432903 for reasoning.

*          soft    nproc     4096
root       soft    nproc     unlimited

这个配置不优化启动服务会出现:
[error]max number of threads [1024] for user [judy2] likely too low, increase to at least [4096]

2.4.3 优化3

设置一个进程可以拥有的虚拟内存区域的数量

wangting@ops01:/home/wangting >sudo vim /etc/sysctl.conf 
vm.max_map_count=262144
# 重载配置
wangting@ops01:/home/wangting >sudo sysctl -p

这个配置不优化启动服务会出现:
[error]max virtual memory areas vm.max_map_count [65530] likely too low, increase to at least [262144]

2.5 解压安装

wangting@ops01:/opt/software >tar -xf elasticsearch-6.6.0.tar.gz -C /opt/module/
wangting@ops01:/opt/software >cd /opt/module/elasticsearch-6.6.0/
wangting@ops01:/opt/module/elasticsearch-6.6.0 >ll
total 448
drwxr-xr-x  3 wangting wangting   4096 Aug  4 15:13 bin
drwxr-xr-x  2 wangting wangting   4096 Jan 24  2019 config
drwxr-xr-x  3 wangting wangting   4096 Jan 24  2019 lib
-rw-r--r--  1 wangting wangting  13675 Jan 24  2019 LICENSE.txt
drwxr-xr-x  2 wangting wangting   4096 Jan 24  2019 logs
drwxr-xr-x 29 wangting wangting   4096 Jan 24  2019 modules
-rw-r--r--  1 wangting wangting 403816 Jan 24  2019 NOTICE.txt
drwxr-xr-x  2 wangting wangting   4096 Jan 24  2019 plugins
-rw-r--r--  1 wangting wangting   8519 Jan 24  2019 README.textile

2.6 修改配置文件

wangting@ops01:/opt/module/elasticsearch-6.6.0 >cd config/
wangting@ops01:/opt/module/elasticsearch-6.6.0/config >cat elasticsearch.yml | grep -vE "^#|^$"
cluster.name: my-es
node.name: node-ops01
bootstrap.memory_lock: false
network.host: 11.8.37.50
http.port: 9200
discovery.zen.ping.unicast.hosts: ["11.8.37.50", "11.8.36.63", "11.8.36.76"]

配置项说明:

cluster.name: my-es # 集群名称;同一集群各节点名称必须相同
node.name: node-ops01 # 当前节点名称;可理解成在集群中各自用这个名称区别
bootstrap.memory_lock: false # bootstrap自检程序
network.host: 11.8.37.50 # 当前节点host主机
http.port: 9200 # es启动端口
discovery.zen.ping.unicast.hosts: [“11.8.37.50”, “11.8.36.63”, “11.8.36.76”] # 自发现配置:新节点向集群报到的主机名

【注意】: 这些是常规配置,其它例如data存储路径,log存储路径等等都可以自定义根据情况去配置

2.7 分发安装目录

wangting@ops01:/opt/module >scp -r elasticsearch-6.6.0 ops02:/opt/module/
wangting@ops01:/opt/module >scp -r elasticsearch-6.6.0 ops03:/opt/module/

2.8 修改其它节点配置文件

# 节点ops02
wangting@ops02:/opt/module/elasticsearch-6.6.0/config >cat elasticsearch.yml | grep -vE "^#|^$"
cluster.name: my-es
node.name: node-ops02
bootstrap.memory_lock: false
network.host: 11.8.36.63
http.port: 9200
discovery.zen.ping.unicast.hosts: ["11.8.37.50", "11.8.36.63", "11.8.36.76"]

# 节点ops03
wangting@ops03:/opt/module/elasticsearch-6.6.0/config >cat elasticsearch.yml | grep -vE "^#|^$"
cluster.name: my-es
node.name: node-ops03
bootstrap.memory_lock: false
network.host: 11.8.36.76
http.port: 9200
discovery.zen.ping.unicast.hosts: ["11.8.37.50", "11.8.36.63", "11.8.36.76"]

2.9 服务启动

# 依次启动3台节点es,命令相同
wangting@ops01:/opt/module >cd /opt/module/elasticsearch-6.6.0/bin/
wangting@ops01:/opt/module/elasticsearch-6.6.0/bin >./elasticsearch -d
wangting@ops01:/opt/module/elasticsearch-6.6.0/bin >

【注意】:

  1. -d 为后台运行,不加-d只能前台运行,关了会话窗口服务也会同时终止
  2. 3台机器都需要启动elasticsearch
  3. 运行日志没有配置定义,默认在服务目录下:elasticsearch-6.6.0/logs/ ,有异常可以先查看日志

2.10 命令验证es

wangting@ops01:/opt/module/elasticsearch-6.6.0/logs >curl http://11.8.37.50:9200/_cat/nodes?v
ip         heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
11.8.36.63           26          88   0    0.05    0.13     0.10 mdi       -      node-ops02
11.8.37.50           28          87   0    0.06    0.10     0.08 mdi       *      node-ops01
11.8.36.76           26          53   0    0.06    0.07     0.06 mdi       -      node-ops03

【注意】:正常运行的es集群,curl各个节点nodes状态都是可以返回结果

2.11 安装kibana

# 安装kibana kibana只是一个工具 挑一台服务器安装即可
wangting@ops01:/opt/software >scp kibana-6.6.0-linux-x86_64.tar.gz ops03:/opt/software/
wangting@ops03:/opt/module/elasticsearch-6.6.0/bin >cd /opt/software/
wangting@ops03:/opt/software >tar -xf kibana-6.6.0-linux-x86_64.tar.gz -C /opt/module/
wangting@ops03:/opt/software >cd /opt/module/kibana-6.6.0-linux-x86_64/config/
wangting@ops03:/opt/module/kibana-6.6.0-linux-x86_64/config >cat kibana.yml | grep -vE "^$|^#"
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://11.8.37.50:9200"]

wangting@ops03:/opt/module/kibana-6.6.0-linux-x86_64/config >cd ..
wangting@ops03:/opt/module/kibana-6.6.0-linux-x86_64 >nohup bin/kibana &

2.12 界面验证kibana

在哪个节点部署,则使用对应地址+5601端口访问

http://11.8.36.76:5601/

在这里插入图片描述

第三章:elasticsearch的基本概念

第一节:关键词解释

关键词释义
cluster整个elasticsearch 默认就是集群状态,整个集群是一份完整、互备的数据。
node集群中的一个节点,一般只一个进程就是一个node
shard分片,即使是一个节点中的数据也会通过hash算法,分成多个片存放,默认是5片。(7.0默认改为1片)
index相当于rdbms的database(5.x), 对于用户来说是一个逻辑数据库,虽然物理上会被分多个shard存放,也可能存放在多个node中。 6.x 7.x index相当于table
type类似于rdbms的table,但是与其说像table,其实更像面向对象中的class , 同一Json的格式的数据集合。(6.x只允许建一个,7.0被废弃,造成index实际相当于table级)
document类似于rdbms的 row、面向对象里的object
field相当于字段、属性

第二节:语法简单示例:

GET /_cat/nodes?v # 查询各个节点状态

GET /_cat/indices?v # 查询各个索引状态

GET /_cat/shards/xxxx # 查询某个索引的分片情况

第三节:elasticsearch 9200 9300端口区别

es正常启动会有9200和9300两个端口

1、9200作为Http协议,主要用于外部通讯;是 HTTP 协议的 RESTful 接口

2、9300作为Tcp协议,jar之间就是通过tcp协议通讯;集群间和 TCPClient 都走得它

第四章:elasticsearch restful api [DSL]

DSL: 全称 Domain Specific language,即特定领域专用语言

第一节 es中保存的数据结构

一般在java代码中,两个对象如果放在关系型数据库保存,例如存储在MySQL中,则会被拆成2张表,Movie对应一张MySQL表,Actor对应另一张MySQL表;

package com.wangting.elasticsearch.test;

import java.util.List;

public interface Test {
    public static void main(String[] args) {
        public class  Movie<Actor> {
            String id;
            String name;
            Double doubanScore;
            List<Actor> actorList;
        }

        public class Actor{
            String id;
            String name;
        }
    }
}

但是elasticsearch是用一个json来表示一个document。

{
    "id": "1", 
    "name": "operation red sea", 
    "doubanScore": "8.5", 
    "actorList": [
        {
            "id": "1", 
            "name": "zhangyi"
        }, 
        {
            "id": "2", 
            "name": "haiqing"
        }, 
        {
            "id": "3", 
            "name": "zhanghanyu"
        }
    ]
}

为便于理解,简单和MySQL作一个对比,关键词只是类似,并不是完全一个概念,功能差不多

MySQLelasticsearch
databasesindex
tabletype
rowdocument
columnfield

第二节 对数据常用操作

4.2.1 查看es中有哪些索引

比较类似MySQL中的show tables;

GET /_cat/indices?v

在这里插入图片描述

【注意】:

  1. es 中会默认存在一个名为.kibana的索引
  2. GET /_cat/indices?v 等同于命令行中curl http://11.8.37.50:9200/_cat/indices?v
  3. 最后的?v可以不加,但是不建议,因为这样结果没有表头,查询的结果相对较乱,不便于查阅

查询索引表头各关键词释义

关键词释义
healthgreen(集群完整) yellow(单点正常、集群不完整) red(单点不正常)
status是否能使用
index索引名
uuid索引统一编号
pri主节点几个
rep从节点几个
docs.count文档数
docs.deleted文档被删了多少
store.size整体占空间大小
pri.store.size主节点占空间大小

4.2.2 添加索引

比较类似MySQL中的create table;

PUT /indexname

在这里插入图片描述

【注意】:

#! Deprecation: the default number of shards will change from [5] to [1] in 7.0.0; if you wish to continue using the default of [5] shards, you must manage this on the create index request or with an index template

意思大致是:提示在大版本7.0.0以后,默认分片数将从5更改变为1,以后如想使用默认的5,就必须新建带有索引模板的索引请求

再次查询索引信息:

在这里插入图片描述

4.2.3 删除索引

比较类似MySQL中的drop table;

DELETE /indexname

在这里插入图片描述

4.2.4 新增文档

比较类似MySQL中某张表的insert into ;

语法:格式 PUT /index/type/id

如果上面示例的 movie_index索引删除了,重新建一下:PUT /movie_index

新增:

PUT /movie_index/movie/1
{
    "id": 1, 
    "name": "operation red sea", 
    "doubanScore": 8.5, 
    "actorList": [
        {
            "id": 1, 
            "name": "zhang yi"
        }, 
        {
            "id": 2, 
            "name": "hai qing"
        }, 
        {
            "id": 3, 
            "name": "zhang han yu"
        }
    ]
}

PUT /movie_index/movie/2
{
    "id": 2, 
    "name": "operation meigong river", 
    "doubanScore": 8, 
    "actorList": [
        {
            "id": 3, 
            "name": "zhang han yu"
        }
    ]
}

PUT /movie_index/movie/3
{
    "id": 3, 
    "name": "incident red sea", 
    "doubanScore": 5, 
    "actorList": [
        {
            "id": 4, 
            "name": "zhang chen"
        }
    ]
}

在这里插入图片描述

4.2.5 直接用id查找数据

比较类似MySQL中某张表的select where条件 ;

GET movie_index/movie/1

在这里插入图片描述

4.2.6 修改 - ( 修改整体替换数据 )

比较类似MySQL中某张表的alter table ;

【注意】: 和新增没有区别 要求:必须包括全部字段

PUT /movie_index/movie/3
{
    "id": "3", 
    "name": "incident red sea", 
    "doubanScore": "5.0", 
    "actorList": [
        {
            "id": "1", 
            "name": "zhang chen"
        }
    ]
}

4.2.7 修改 - ( 修改某个字段 )

某个字段的值内容修改

比较类似MySQL中某张表的alter table ;

POST movie_index/movie/3/_update
{ 
  "doc": {
    "doubanScore":"7.0"
  } 
}
某个字段关闭索引
"name":{
  "type": "keyword",
  "index": false
}

4.2.8 删除一个document

比较类似MySQL中某张表的DELETE FROM <表名> [WHERE 子句]

DELETE movie_index/movie/3

在这里插入图片描述

4.2.9 搜索type全部数据

比较类似MySQL中某张表select * from;

GET movie_index/movie/_search

4.2.10 按条件查询(全部)

GET movie_index/movie/_search
{
  "query":{
    "match_all": {}
  }
}

【注意】:按条件查询时,条件中为空,则效果等同于查询type中全部数据GET movie_index/movie/_search

4.2.11 按分词查询

比较类似MySQL中某张表select * from like %xxx%;但是仅仅像原理不同

GET movie_index/movie/_search
{
  "query":{
    "match": {"name":"red"}
  }
}

在这里插入图片描述

4.2.12 按分词子属性查询

GET movie_index/movie/_search
{
  "query":{
    "match": {"actorList.name":"zhang"}
  }
}

在这里插入图片描述

4.2.13 match phrase [ 短语查询 ]

按短语查询,不再利用分词技术,直接用短语在原始数据中匹配

GET movie_index/movie/_search
{
    "query":{
      "match_phrase": {"name":"operation red"}
    }
}

相当于operation red当成一个整体来查询,不会operation查完再查询red,并不做拆分一个个去匹配;name中单独有operation和red的词就不符合条件

在这里插入图片描述

4.2.14 fuzzy查询 [ 校正匹配 ]

fuzzy校正匹配分词,当一个单词都无法准确匹配,es通过一种算法对非常接近的单词也给与一定的评分,能够查询出来,但是消耗更多的性能。

GET movie_index/movie/_search
{
    "query":{
      "fuzzy": {"name":"rad"}
    }
}

这个机制就相当于平时搜索百度内容时,输入的内容可能输错了,主页搜内容会提示:您是否要搜索是xxx,然后把搜索xxx的内容返回

在这里插入图片描述

4.2.15 过滤–查询后过滤(先查询后过滤)

post_filter过滤为满足条件的数据保留,不满足的剔除;并不是符合post_filter给剔除。

GET movie_index/movie/_search
{
    "query":{
      "match": {"name":"red"}
    },
    "post_filter":{
      "term": {
        "actorList.id": 3
      }
    }
}

先查询满足name中有red的是

id=1 -> “name”: “operation red sea”,

id=3 -> “name”: “incident red sea”,

在过滤出id为3的结果返回

4.2.16 过滤–查询前过滤(先过滤后查询)

同样需求下,先过滤后查询优于先查询后过滤

GET movie_index/movie/_search
{ 
    "query":{
        "bool":{
          "filter":[ {"term": {  "actorList.id": "1"  }},
                     {"term": {  "actorList.id": "3"  }}
           ], 
           "must":{"match":{"name":"red"}}
         }
    }
}

4.2.17 过滤–按范围过滤

? “name”: “operation red sea”,
? “doubanScore”: 8.5,
? “name”: “operation meigong river”,
? “doubanScore”: 8,
? “name”: “incident red sea”,
? “doubanScore”: 5,

查出电影评分8分及以上的结果

GET movie_index/movie/_search
{
   "query": {
     "bool": {
       "filter": {
         "range": {
            "doubanScore": {"gte": 8}
         }
       }
     }
   }
}

结果返回id为1和2的数据

关键词功能
gt大于
lt小于
gte大于等于 great than or equals
lte小于等于 less than or equals

4.2.18 排序

GET movie_index/movie/_search
{
  "query":{
    "match": {"name":"red sea"}
  }
  , "sort": [
    {
      "doubanScore": {
        "order": "desc"
      }
    }
  ]
}

先查出名称包含red sea关键词的数据,再根据查询数据结果进行排序;asc 从小到大 | desc从大到小

在这里插入图片描述

4.2.19 分页查询

GET movie_index/movie/_search
{
  "query": { "match_all": {} },
  "from": 1,
  "size": 1
}

from定义了目标数据的偏移值

size定义当前返回的事件数目。
如果不自定义数值则默认from为0,size为10,即所有的查询默认仅仅返回前10条数据。

4.2.20 指定查询的字段

GET movie_index/movie/_search
{
  "query": { "match_all": {} },
  "_source": ["name", "doubanScore"]
}

在这里插入图片描述

4.2.21 高亮显示

GET movie_index/movie/_search
{
    "query":{
      "match": {"name":"red sea"}
    },
    "highlight": {
      "fields": {"name":{} }
    }
    
}

在这里插入图片描述

这里可以看到结果中包裹了一层em标签,就是高亮显示,加斜

可以创建个html后缀的文件,输入如下代码,保存html文件用浏览器打开,再进行观察比对

<p>operation red sea</p>
<p>operation <em>red</em> <em>sea</em> </p>
<p>incident red sea </p>
<p>incident <em>red</em> <em>sea</em> </p>

在这里插入图片描述

4.2.22 聚合查询

示例来说明1:取出每个演员共参演了多少部电影

GET movie_index/movie/_search
{ 
  "aggs": {
    "groupby_actor": {
      "terms": {
        "field": "actorList.name.keyword"  
      }
    }
  }
}

# 查询结果:
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "movie_index",
        "_type" : "movie",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "id" : 2,
          "name" : "operation meigong river",
          "doubanScore" : 8,
          "actorList" : [
            {
              "id" : 3,
              "name" : "zhang han yu"
            }
          ]
        }
      },
      {
        "_index" : "movie_index",
        "_type" : "movie",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "name" : "operation red sea",
          "doubanScore" : 8.5,
          "actorList" : [
            {
              "id" : 1,
              "name" : "zhang yi"
            },
            {
              "id" : 2,
              "name" : "hai qing"
            },
            {
              "id" : 3,
              "name" : "zhang han yu"
            }
          ]
        }
      },
      {
        "_index" : "movie_index",
        "_type" : "movie",
        "_id" : "3",
        "_score" : 1.0,
        "_source" : {
          "id" : 3,
          "name" : "incident red sea",
          "doubanScore" : 5,
          "actorList" : [
            {
              "id" : 4,
              "name" : "zhang chen"
            }
          ]
        }
      }
    ]
  },
  "aggregations" : {
    "groupby_actor" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "zhang han yu",
          "doc_count" : 2
        },
        {
          "key" : "hai qing",
          "doc_count" : 1
        },
        {
          "key" : "zhang chen",
          "doc_count" : 1
        },
        {
          "key" : "zhang yi",
          "doc_count" : 1
        }
      ]
    }
  }
}

示例来说明2:每个演员参演电影的平均分是多少,并按评分排序

GET movie_index/movie/_search
{ 
  "aggs": {
    "groupby_actor_id": {
      "terms": {
        "field": "actorList.name.keyword" ,
        "order": {
          "avg_score": "desc"
          }
      },
      "aggs": {
        "avg_score":{
          "avg": {
            "field": "doubanScore" 
          }
        }
       }
    } 
  }
}

# 查询结果:
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "movie_index",
        "_type" : "movie",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "id" : 2,
          "name" : "operation meigong river",
          "doubanScore" : 8,
          "actorList" : [
            {
              "id" : 3,
              "name" : "zhang han yu"
            }
          ]
        }
      },
      {
        "_index" : "movie_index",
        "_type" : "movie",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "name" : "operation red sea",
          "doubanScore" : 8.5,
          "actorList" : [
            {
              "id" : 1,
              "name" : "zhang yi"
            },
            {
              "id" : 2,
              "name" : "hai qing"
            },
            {
              "id" : 3,
              "name" : "zhang han yu"
            }
          ]
        }
      },
      {
        "_index" : "movie_index",
        "_type" : "movie",
        "_id" : "3",
        "_score" : 1.0,
        "_source" : {
          "id" : 3,
          "name" : "incident red sea",
          "doubanScore" : 5,
          "actorList" : [
            {
              "id" : 4,
              "name" : "zhang chen"
            }
          ]
        }
      }
    ]
  },
  "aggregations" : {
    "groupby_actor_id" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "hai qing",
          "doc_count" : 1,
          "avg_score" : {
            "value" : 8.5
          }
        },
        {
          "key" : "zhang yi",
          "doc_count" : 1,
          "avg_score" : {
            "value" : 8.5
          }
        },
        {
          "key" : "zhang han yu",
          "doc_count" : 2,
          "avg_score" : {
            "value" : 8.25
          }
        },
        {
          "key" : "zhang chen",
          "doc_count" : 1,
          "avg_score" : {
            "value" : 5.0
          }
        }
      ]
    }
  }
}

keyword 是某个字符串字段,专门储存不分词格式的副本 ,在某些场景中只允许只用不分词的格式,比如过滤filter 比如 聚合aggs, 所以字段要加上.keyword的后缀

keyword功能:

  1. 不进行分词,直接索引
  2. 支持模糊、精确查询
  3. 支持聚合

第三节 中文分词

elasticsearch本身自带的中文分词,就是单纯把中文一个字一个字的分开,根本没有词汇的概念。但是实际应用中,用户都是以词汇为条件,进行查询匹配的,如果能够把文章以词汇为单位切分开,那么与用户的查询条件能够更贴切的匹配上,查询速度也更加快速。

GET _analyze
{
  "text": ["wang ting niubi","今天给力"]
  
}

# 结果如下:
{
  "tokens" : [
    {
      "token" : "wang",
      "start_offset" : 0,
      "end_offset" : 4,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "ting",
      "start_offset" : 5,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "niubi",
      "start_offset" : 10,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "今",
      "start_offset" : 16,
      "end_offset" : 17,
      "type" : "<IDEOGRAPHIC>",
      "position" : 3
    },
    {
      "token" : "天",
      "start_offset" : 17,
      "end_offset" : 18,
      "type" : "<IDEOGRAPHIC>",
      "position" : 4
    },
    {
      "token" : "给",
      "start_offset" : 18,
      "end_offset" : 19,
      "type" : "<IDEOGRAPHIC>",
      "position" : 5
    },
    {
      "token" : "力",
      "start_offset" : 19,
      "end_offset" : 20,
      "type" : "<IDEOGRAPHIC>",
      "position" : 6
    }
  ]
}

通过示例可以明显看出,“wang ting niubi”,“今天给力”;英文分词根据空格分词相对合理,但中文一个个字拆开显然不合适(今天 、给力 两个词语没有被识别)

分词器下载网址:https://github.com/medcl/elasticsearch-analysis-ik

【注意】:建议找到和es版本一致的ik版本最佳;找到zip包下载

在这里插入图片描述

安装

# 进入es的plugins目录
wangting@ops01:/home/wangting >cd /opt/module/elasticsearch-6.6.0/plugins/
# 创建一个插件目录(一个插件对应一个plugins下的子目录)
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins >mkdir ik
# 下载ik插件zip包
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik >ls
elasticsearch-analysis-ik-6.6.0.zip
# 解压缩安装包并清理zip文件
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik >unzip elasticsearch-analysis-ik-6.6.0.zip && rm elasticsearch-analysis-ik-6.6.0.zip
# 目录结构
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik >ll
total 1432
-rw-r--r-- 1 wangting wangting 263965 May  6  2018 commons-codec-1.9.jar
-rw-r--r-- 1 wangting wangting  61829 May  6  2018 commons-logging-1.2.jar
drwxr-xr-x 2 wangting wangting   4096 Aug 26  2018 config
-rw-r--r-- 1 wangting wangting  54693 Jan 30  2019 elasticsearch-analysis-ik-6.6.0.jar
-rw-r--r-- 1 wangting wangting 736658 May  6  2018 httpclient-4.5.2.jar
-rw-r--r-- 1 wangting wangting 326724 May  6  2018 httpcore-4.4.4.jar
-rw-r--r-- 1 wangting wangting   1805 Jan 30  2019 plugin-descriptor.properties
-rw-r--r-- 1 wangting wangting    125 Jan 30  2019 plugin-security.policy
# 分发插件至其它节点
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik >cd ..
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins >scp -r ik ops02:/opt/module/elasticsearch-6.6.0/plugins/
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins >scp -r ik ops03:/opt/module/elasticsearch-6.6.0/plugins/

【注意】:插件安装需重启es才生效,否则使用不到对应功能,如下图

在这里插入图片描述

重启es:

# 节点ops01 ; 查找es对应进程号
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins >jps | grep Elasticsearch|awk -F" " '{print $1}'
95973
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins >kill -9 95973
# 节点ops02 ; 查找es对应进程号
wangting@ops02:/opt/module/elasticsearch-6.6.0/plugins >jps | grep Elasticsearch|awk -F" " '{print $1}'
109175
wangting@ops02:/opt/module/elasticsearch-6.6.0/plugins >kill -9 109175
# 节点ops03 ; 查找es对应进程号
wangting@ops03:/opt/module/elasticsearch-6.6.0/plugins >jps | grep Elasticsearch|awk -F" " '{print $1}'
41777
wangting@ops03:/opt/module/elasticsearch-6.6.0/plugins >kill -9 41777

# 各节点均使用如下命令再次启动es
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins >cd /opt/module/elasticsearch-6.6.0/bin/
wangting@ops01:/opt/module/elasticsearch-6.6.0/bin >./elasticsearch -d

测试使用ik中文分词

常用的ik分词器功能有ik_smart和ik_max_word

ik_smart

逐个去匹配,每个字使用1次

在这里插入图片描述

ik_max_word

逐个去匹配,每个字前后能连成词都会展示,相当于尽可能多的形成关系词

在这里插入图片描述

【注意】:从上面示例可以看出,不同的分词器,分词有明显的区别,所以以后定义一个type不能再使用默认的mapping,要手工建立mapping来指定分词器, 因为要根据使用场景选择适用合理的分词器

自定义中文词库

生活中,经常会出现一些新的热门词语,比如近期我接触最多的就是yyds永远的神。。。如果始终用之前的词库,那像 永远的神 就可能分成:永远、的、神,不会是我们所想的永远的神作为一个整体。

那这种情况就需要维护一套用户自定义的中文词库。

在没有自定义中文词库之前,我们先查一个示例,把结果留下,一会安装完自定义词库后作为对比:

安装前:

在这里插入图片描述

安装部署自定义词库:

wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >pwd
/opt/module/elasticsearch-6.6.0/plugins/ik/config
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >ls
extra_main.dic  extra_single_word.dic  extra_single_word_full.dic  extra_single_word_low_freq.dic  extra_stopword.dic  IKAnalyzer.cfg.xml  main.dic  preposition.dic  quantifier.dic  stopword.dic  suffix.dic  surname.dic
# 修改ik插件的config/IKAnalyzer.cfg.xml配置
# <entry key="remote_ext_dict">http://11.8.38.86/fenci/esword.txt</entry> 这行配置一个nginx代理地址
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >vim IKAnalyzer.cfg.xml 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 扩展配置</comment>
        <!--用户可以在这里配置自己的扩展字典 -->
        <entry key="ext_dict"></entry>
         <!--用户可以在这里配置自己的扩展停止词字典-->
        <entry key="ext_stopwords"></entry>
        <!--用户可以在这里配置远程扩展字典 -->
        <entry key="remote_ext_dict">http://11.8.38.86/fenci/esword.txt</entry>
        <!--用户可以在这里配置远程扩展停止词字典-->
        <!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>

# 切换至有nginx服务的机器上(没有nginx需要自行部署安装)
root@ops04:/usr/local/nginx-1.10/conf #cd /usr/local/nginx-1.10/
root@ops04:/usr/local/nginx-1.10 #mkdir ik
root@ops04:/usr/local/nginx-1.10 #cd ik
root@ops04:/usr/local/nginx-1.10/ik #mkdir fenci
root@ops04:/usr/local/nginx-1.10/ik #cd fenci
root@ops04:/usr/local/nginx-1.10/ik/fenci #echo "王亭" >> esword.txt
root@ops04:/usr/local/nginx-1.10/ik/fenci #echo "永远的神" >> esword.txt
root@ops04:/usr/local/nginx-1.10/ik/fenci #echo "神圣赞美诗" >> esword.txt
root@ops04:/usr/local/nginx-1.10/ik/fenci #cat esword.txt 
王亭
永远的神
神圣赞美诗

root@ops04:/usr/local/nginx-1.10/ik/fenci #vim /usr/local/nginx-1.10/conf/nginx.conf
listen       80;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }
		
		# 增加如下配置:
        location /fenci/ {
            root   ik;
        }

root@ops04:/usr/local/nginx-1.10/ik/fenci #/usr/local/nginx-1.10/sbin/nginx -s reload

# 地址必须和IKAnalyzer.cfg.xml配置项对应;也可以先把nginx弄好再配置IKAnalyzer.cfg.xml合理些
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >curl http://11.8.38.86/fenci/esword.txt
王亭
永远的神
神圣赞美诗

# 修改的xml配置,分发至其它节点
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >scp IKAnalyzer.cfg.xml ops02:/opt/module/elasticsearch-6.6.0/plugins/ik/config/ 
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >scp IKAnalyzer.cfg.xml ops03:/opt/module/elasticsearch-6.6.0/plugins/ik/config/ 

# 重启es(各节点)
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >jps | grep Elasticsearch|awk -F" " '{print $1}'
13077
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >kill -9 13077
wangting@ops01:/opt/module/elasticsearch-6.6.0/plugins/ik/config >cd /opt/module/elasticsearch-6.6.0/bin/
wangting@ops01:/opt/module/elasticsearch-6.6.0/bin >./elasticsearch -d

重启es后重新再测试:(已经可以成功识别出新定义的词语)

在这里插入图片描述

第四节 关于mapping

type可以类比成table,MySQL在create table时会定义每个字段的字段类型约束;那es每个字段的数据类型也是可以定义的;实际上每个type中的字段是什么数据类型,由es的mapping定义。

【注意】:如果没有设定mapping,则系统会自动根据一条数据的格式来推断出应该的数据格式

4.4.1 查看type的mapping

GET movie_index/_mapping/movie

在这里插入图片描述

常见的类型:

? true/false → boolean
? 1020 → long
? 20.1 → double
? “2018-02-01” → date
? “hello world” → text + keyword
【注意】:

  1. 默认type只有text类型会进行分词,keyword是不会分词的字符串。

  2. mapping除了自动定义,还可以手动定义,但是只能对新加的、没有数据的字段进行定义。一旦有了数据就无法再做修改了。

  3. 虽然每个Field的数据放在不同的type下,但是同一个名字的Field在一个index下只能有一种mapping定义。

4.4.2 基于中文文辞搭建索引

创建mapping

PUT movie_chn
{
  "mappings": {
    "movie":{
      "properties": {
        "id":{
          "type": "long"
        },
        "name":{
          "type": "text"
          , "analyzer": "ik_smart"
        },
        "doubanScore":{
          "type": "double"
        },
        "actorList":{
          "properties": {
            "id":{
              "type":"long"
            },
            "name":{
              "type":"keyword"
            }
          }
        }
      }
    }
  }
}

name -> 定义成text类型 使用ik中文分词ik_smart

执行结果:

在这里插入图片描述

创建完成后

PUT插入数据:

# 数据1
PUT /movie_chn/movie/1
{ "id":1,
  "name":"红海行动",
  "doubanScore":8.5,
  "actorList":[  
  {"id":1,"name":"张译"},
  {"id":2,"name":"海清"},
  {"id":3,"name":"张涵予"}
 ]
}

# 数据2
PUT /movie_chn/movie/2
{
  "id":2,
  "name":"湄公河行动",
  "doubanScore":8.0,
  "actorList":[  
{"id":3,"name":"张涵予"}
]
}

# 数据3
PUT /movie_chn/movie/3
{
  "id":3,
  "name":"红海事件",
  "doubanScore":5.0,
  "actorList":[  
{"id":4,"name":"张晨"}
]
}

name:红海行动、湄公河行动、红海事件

测试查询效果

查询电影名为红海战役的结果:

GET /movie_chn/movie/_search
{
  "query": {
    "match": {
      "name": "红海战役"
    }
  }
}

在这里插入图片描述

查询演员有张译的结果:

GET /movie_chn/movie/_search
{
  "query": {
    "term": {
      "actorList.name": "张译"
    }
  }
}

在这里插入图片描述

第五节 索引别名 _aliases

索引别名就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用。别名 带给我们极大的灵活性,允许我们做下面这些:
1.给多个索引分组 (例如, last_three_months -> 可以指向多个)
2.给索引的一个子集创建视图
3.在运行的集群中可以无缝的从一个索引切换到另一个索引

4.5.1 创建索引别名

PUT movie_chn_2
{  "aliases": {
      "movie_chn_2020-query": {}
  }, 
  "mappings": {
    "movie":{
      "properties": {
        "id":{
          "type": "long"
        },
        "name":{
          "type": "text"
          , "analyzer": "ik_smart"
        },
        "doubanScore":{
          "type": "double"
        },
        "actorList":{
          "properties": {
            "id":{
              "type":"long"
            },
            "name":{
              "type":"keyword"
            }
          }
        }
      }
    }
  }
}

定义别名:

“aliases”: {

“movie_chn_2020-query”: {}

}

已存在的索引增加别名

POST  _aliases
{
    "actions": [
        { "add":    { "index": "movie_chn_2", "alias": "movie_chn_2020-query" }}
    ]
}

# 可以继续增加
POST  _aliases
{
    "actions": [
        { "add":    { "index": "movie_chn_2", "alias": "movie_chn_2020-query" }}
    ]
}

也可以通过加过滤条件缩小查询范围,建立一个子集视图

POST  _aliases
{
    "actions": [
        { "add":    
{ "index": "movie_chn_2", 
"alias": "movie_chn0919-query-zhhy",
            "filter": {
                "term": {  "actorList.id": "3"
                 }
               }
 }
}
    ]
}

4.5.2 查询别名

GET movie_chn_2020-query/_search

4.5.3 删除某个索引别名

POST  _aliases
{
    "actions": [
        { "remove":    { "index": "movie_chn_2", "alias": "movie_chn_2020-query" }}
    ]
}

4.5.4 为别名切换

POST /_aliases
{
    "actions": [
        { "remove": { "index": "movie_chn_2", "alias": "movie_chn_2021-query" }},
        { "add":    { "index": "movie_chn", "alias": "movie_chn_2021-query" }}
    ]
}

4.5.5 查询别名列表

GET  _cat/aliases?v

在这里插入图片描述

第六节 索引模板

Index Template 索引模板,是创建索引的模具,其中可以定义一系列规则来帮助我们构建符合特定业务需求的索引的 mappings 和 settings,通过使用 Index Template 可以让我们的索引具备可预知的一致性。

索引模板可以更方便的建立索引,例如当还没有建好索引的时候,es获取到第一条数据进来需要保存时,如果数据里的索引前缀可以匹配到索引模板的index pattern,则es会根据模板直接生成该索引

4.6.1 分割索引

分割索引就是根据时间间隔把一个业务索引切分成多个索引。
举例:
把order_info变成order_info_0801,order_info_0802,order_info_0803,…
这样做的好处有两个:

  1. 结构变化的灵活性:因为elasticsearch不允许对数据结构进行修改。但是实际使用中索引的结构和配置难免变化,那么只要对下一个间隔的索引进行修改,原来的索引位置原状。这样就有了一定的灵活性。
  2. 查询范围优化: 因为一般情况并不会查询全部时间周期的数据,那么通过切分索引,物理上减少了扫描数据的范围,也是对性能的优化。

4.6.2 创建索引模板

PUT _template/template_movie
{
  "index_patterns": ["movie_test*"],                  
  "settings": {                                               
    "number_of_shards": 1
  },
  "aliases" : { 
    "{index}-query": {},
    "movie_test-query":{}
  },
  "mappings": {                                          
"_doc": {
      "properties": {
        "id": {
          "type": "keyword"
        },
        "movie_name": {
          "type": "text",
          "analyzer": "ik_smart"
        }
      }
    }
  }
}

其中 “index_patterns”: [“movie_test*”], 的含义就是凡是往movie_test开头的索引写入数据时,例如向movie_test_001索引写入数据,如果索引movie_test_001不存在,那么es会根据movie_test模板自动建立索引。

shard数量设置:

  "settings": {                                               
    "number_of_shards": 1
  },

在 “aliases” 中用{index}表示,获得真正的创建的索引名。

POST movie_test_20210801/_doc
{
  "id":"100",
  "name":"aaa"
}

POST movie_test_20210801/_doc
{
  "id":"101",
  "name":"bbb"
}

POST movie_test_20210802/_doc
{
  "id":"102",
  "name":"ccc"
}
POST movie_test_20210801/_doc
{
  "id":"103",
  "name":"ddd"
}

4.6.3 查询已有模板列表

GET  _cat/templates

4.6.4 查看某个模板详情

GET  _template/template_movie*

第一条数据进来需要保存时,如果数据里的索引前缀可以匹配到索引模板的index pattern,则es会根据模板直接生成该索引

4.6.1 分割索引

分割索引就是根据时间间隔把一个业务索引切分成多个索引。
举例:
把order_info变成order_info_0801,order_info_0802,order_info_0803,…
这样做的好处有两个:

  1. 结构变化的灵活性:因为elasticsearch不允许对数据结构进行修改。但是实际使用中索引的结构和配置难免变化,那么只要对下一个间隔的索引进行修改,原来的索引位置原状。这样就有了一定的灵活性。
  2. 查询范围优化: 因为一般情况并不会查询全部时间周期的数据,那么通过切分索引,物理上减少了扫描数据的范围,也是对性能的优化。

4.6.2 创建索引模板

PUT _template/template_movie
{
  "index_patterns": ["movie_test*"],                  
  "settings": {                                               
    "number_of_shards": 1
  },
  "aliases" : { 
    "{index}-query": {},
    "movie_test-query":{}
  },
  "mappings": {                                          
"_doc": {
      "properties": {
        "id": {
          "type": "keyword"
        },
        "movie_name": {
          "type": "text",
          "analyzer": "ik_smart"
        }
      }
    }
  }
}

其中 “index_patterns”: [“movie_test*”], 的含义就是凡是往movie_test开头的索引写入数据时,例如向movie_test_001索引写入数据,如果索引movie_test_001不存在,那么es会根据movie_test模板自动建立索引。

shard数量设置:

  "settings": {                                               
    "number_of_shards": 1
  },

在 “aliases” 中用{index}表示,获得真正的创建的索引名。

POST movie_test_20210801/_doc
{
  "id":"100",
  "name":"aaa"
}

POST movie_test_20210801/_doc
{
  "id":"101",
  "name":"bbb"
}

POST movie_test_20210802/_doc
{
  "id":"102",
  "name":"ccc"
}
POST movie_test_20210801/_doc
{
  "id":"103",
  "name":"ddd"
}

4.6.3 查询已有模板列表

GET  _cat/templates

4.6.4 查看某个模板详情

GET  _template/template_movie*
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 10:18:18  更:2021-08-09 10:19:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 19:24:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码