ElasticSearch是目前开源全文搜索引擎的首选,能够快速存储,搜索和分析海量数据。Stack Overflow,Github等都在使用。html
Elasticsearch 是使用 Java 编写的,它的内部使用 Lucene 作索引与搜索,可是它使全文检索变得简单, 经过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。java
ES提供的Client API:https://www.elastic.co/guide/en/elasticsearch/client/index.htmlnode
包含多种语言:python
注意:没有C++接口,而咱们须要基于c++操做ESlinux
设备IP:10.3.246.224c++
系统:linux-64git
磁盘空间:无 (df –h 发现磁盘没容量了)github
du -sh /* | sort –nr :找出系统中占容量最大的文件夹,系统中15scpp这个文件夹占41Gweb
du -sh /15scpp/* | sort -nr :找出15scpp中占容量最大文件夹15scpp_testbin,占31G算法
确认这个文件夹已无用,rm删除
PS:在Linux中,当咱们使用rm在linux上删除了大文件,可是若是有进程打开了这个大文件,却没有关闭这个文件的句柄,那么linux内核仍是不会释放这个文件的磁盘空间。找出文件使用进程,kill掉,便可
Elastic 须要 Java 8 环境
Java –version查看java是否安装或如今版本
官网下载jdk-8u181-linux-x64.tar.gz,解压,安装,设置环境变量,这里就不赘述。
下载https://www.elastic.co/downloads/elasticsearch :
elasticsearch-6.3.2.tar.gz
tar解压
由于安全问题elasticsearch 不让用root用户直接运行,因此要建立新用户:
adduser pwrd-es
passwd pwrd-es pwrd-es
chown -R pwrd-es:pwrd-es /home/elasticSearch/elasticsearch-6.3.2
//由于安全问题,不让root用户执行安装,可是其余用户又没有文件操做权限,故而改之
Vim config/elasticSearch.yml
cluster.name: pwrd-es //Elasticsearch会自动发如今同一网段下的Elasticsearch 节点,用这个属性来区分不一样的集群,cluster.name相同则自动组建成一个集群
node.name: node-1 //节点名,默认随机指定一个name列表中名字,不能重复
node.master: true //指定该节点是否有资格被选举成为node,默认是true,es是默认集群中的第一台机器为master,若是这台机挂了就会从新选举master
node.data: true //指定该节点是否存储索引数据,默认为true
path.data: /home/elasticSearch/log_export/data //存储数据
path.logs: /home/elasticSearch/log_export/logs //存储日志
#index.number_of_shards: 5 // 设置默认索引分片个数,默认为5片
#index.number_of_replicas: 1 //设置默认索引副本个数,默认为1个副本
network.host: 10.3.246.222 #该参数用于同时设置bind_host和publish_host
network.bind_host: 0.0.0.0 #设置绑定的IP地址,能够是IPV4或者IPV6
network.publish_host: 10.3.246.222 #设置其余节点与该节点交互的IP地址
http.port: 9200
transport.tcp.port: 9300 #节点之间交互端口
transport.tcp.compress: true #设置是否压缩tcp上交互传输的数据
http.max_content_length: 100mb #设置http内容的最大大小
http.enabled: true #是否开启http服务对外提供服务
# --------------------------------- Discovery ----------------------------------
# Pass an initial list of hosts to perform discovery when new node is started:
# The default list of hosts is ["127.0.0.1", "[::1]"]
#设置集群中的Master节点的初始列表,能够经过这些节点来自动发现其余新加入集群的节点
discovery.zen.ping.unicast.hosts: ["10.3.246.224", "10.3.246.223"]
# Prevent the "split brain" by configuring the majority of nodes (total number of master-eligible nodes / 2 + 1): 为了防止“脑裂”(master-slave分布式中核心难点),设置最低主节点数
discovery.zen.minimum_master_nodes: 2 #集群中三个节点
vim /etc/sysctl.conf
vm.max_map_count = 655360
sysctl -p
切换用户: su pwrd-es
./elasticsearch
浏览器中输入10.3.246.224:9200
或者
curl 'http://localhost:9200/?pretty'
能够看到一个json数据,即为安装成功
PS:pretty是为了json格式化,以致于返回的结果好看一些
Elastic 本质上是一个分布式数据库,容许多台服务器协同工做,每台服务器能够运行多个 Elastic 实例。
单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)
经过cluster.name 属性配置集群的名字,用于惟一标识一个集群,不一样的集群,其cluster.name 不一样,集群名字相同的全部节点自动组成一个集群。当启动一个结点时,该结点会在当前局域网内自动寻找相同集群名字的主结点;若是找到主结点,该结点加入集群中;若是未找到主结点,该结点成为主结点。
ES基本结构是 : index/type/id -> document (通常以json样式存储数据)
因此Index(索引)是Elastic 数据管理的顶层单位,它是单个数据库的同义词。
ES会索引全部字段,通过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。
PS:每一个 Index (即数据库)的名字必须是小写。
能够用以下命令,查看当前节点索引:
curl -X GET 'http://localhost:9200/_cat/indices?v'
Index里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。
Document 使用 JSON 格式表示,以下:
{
“name”:”张三”,
“age”:18,
“sex”:”male”
}
PS:对于json的编写与格式是否正确,能够借助在线json工具:http://www.bejson.com/
Type能够用来分类document,好比,china/Beijing/id-i ->doc-n
china/shanghai/id-j ->doc-m
同一个Index下不一样的 Type 应该有类似的结构。
可是,Elastic 6.x 版只容许每一个 Index 包含一个 Type,7.x 版将会完全移除 Type。
咱们部署的是当前最新6.3.2版本
当一个索引下的数据太多,超过单一节点所能提供的磁盘空间,ES提供分片功能,能够将海量数据分片存储到集群中不一样的节点中。当你查询的索引分布在多个分片上时,ES会把查询发送给每一个相关的分片,并将结果组合在一块儿,而应用程序并不知道分片的存在。即:这个过程对用户来讲是透明的。
为提升查询吞吐量或实现高可用性,可使用分片副本。
副本是一个分片的精确复制,每一个分片能够有零个或多个副本。ES中能够有许多相同的分片,其中之一被选择更改索引操做,这种特殊的分片称为主分片。
当主分片丢失时,如:该分片所在的数据不可用时,集群将副本提高为新的主分片。
可是目前type即将做废。
curl -XPUT "http://10.3.246.224:9200/tests/"
返回数据
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "testes"
}
curl -XPUT "http://10.3.246.224:9200/tests/songs/1" -d '{"name":"deck the halls","year":"2018","month":"8"}' 会报错,以下指定header就能够了
curl -H "Content-Type: application/json" -XPUT "http://10.3.246.224:9200/tests/songs/1" -d '{"name":"deck the halls","year":"2018","month":"8"}'
返回结果:
{
"_index": "testes",
"_type": "songs",
"_id": "1", //id 也能够不知道,由系统自主生成
"_version": 1,
"result": "created",//表明添加成功
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
curl -XGET http://localhost:9200/music/songs/1?pretty
返回数据:
{
"_index" : "testes",
"_type" : "songs",
"_id" : "1",
"_version" : 1,
"found" : true, //查找成功
"_source" : { //目的数据
"name" : "deck the halls",
"year" : "2018",
"month" : "8"
}
}
a) 查找更新某个key:
curl -H "Content-Type: application/json" -XPOST "http://10.3.246.224:9200/testes/songs/1/_update?pretty" -d '{"doc":{"query":{"match":{"name":"qqqddd"}}}}'
返回数据:
{
"_index" : "testes",
"_type" : "songs",
"_id" : "1",
"_version" : 2,
"result" : "updated",//更新成功
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
b) 更新整条数据:
curl -H "Content-Type: application/json" -XPOST "http://10.3.246.224:9200/testes/songs/1/_update?pretty" -d '{"doc":{"name":"qddd","year":"2018","month":"8"}}'
返回数据同上
c) 还有就是用添加数据的命令:只是将数据改了
curl -H "Content-Type: application/json" -XPUT "http://10.3.246.224:9200/tests/songs/1" -d '{"name":"deck the halls","year":"2020","month":"8"}'
curl -XDELETE "http://localhost:9200/music/songs/1"
返回数据:
{
"_index": "tests",
"_type": "songs",
"_id": "1",
"_version": 2,
"result": "deleted",//删除成功
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
注意:删除一个文档不会当即生效,它只是被标记成已删除。es将会在你以后添加更多索引的时候才会在后台进行删除内容的清理
有两个办法:
一是嵌入其余语言的开发,利用ES已提供的接口,好比,在C++中嵌入python API,这在编译上可能引入新的问题
二是须要本身构造如第四部分的http请求来得到数据,能够基于libcurl库,也能够基于系统中已有的httpproxy
为了避免破坏已有系统的一致性,基于httpproxy,来构造http请求,对外封装提供C++接口
/*es是面向文档,基于索引的弹性搜索引擎,故而
*es中的数据结构:index/type/id ->对应一条数据,因此
*es中帖子的数据结构设计:
uid/tiezi/tid -> json{uid,tid,content,timestamp}
*虽然存在uid和tid的冗余,可是这样设计的好处是:能够很方便处理某我的某条数据,由于uid就是索引,这样还利用了,索引的高处理性能
*/
/*功能:向es中添加protobuf数据,可是在es中是以json样式存在
*uid:用户id
*tid:帖子id
*msg:帖子结构,即(uid,tid,content,timestamp)
*/
bool addDocument(const std::string &uid, const std::string &tid,const google::protobuf::Message *msg);
封装后造成的http请求:
curl -H "Content-Type: application/json" -XPUT "http://10.3.246.224:9200/ uid /tiezi/tid " -d ' msg .json_str()'
/*功能:向es中删除某我的uid的全部数据
*uid:用户id
*/
bool deleteAllByUid(const std::string &uid);
封装后造成的http请求:
curl -XDELETE http://localhost:9200/uid -d ‘{“query”:{“match_all”:{}}}’
/*功能:向es中删除某我的具体的某个数据
*uid:用户id
*tid:帖子id
*/
bool deleteDocumentByUidTid(const std::string &uid, const std::string &tid);
封装后造成的http请求:
curl -XDELETE "http://localhost:9200/ uid / tiezi / tid "
/*功能:向es中删除某个用户某个时间点之前的全部帖子,也就是 小于 某个时间的全部帖子
*uid:用户id
*beforeTimes:时刻之前的数据将所有会删除
*/
bool deleteDocumentByUidBeforeTimes(const std::string &uid, const std::string &beforeTimes);
封装后造成的http请求:
curl -XPOST "http://localhost:9200/ uid /tiezi/_delete_by_query
-d‘{"query":{"range":{"timestamp.keyword":{"gte":"2016-07-09 11:18:21","lte":"2018-08-17 11:18:21","format":"yyyy-MM-dd HH:mm:ss"}}}}’
PS: timestamp是数据中一个字段,在时间段匹配中,要注意空格
/*功能:以或的逻辑关系操做查询帖子中是否存在包含词,并过滤返回结果,只返回并获得UID,tid
*注:好比查询词是词组:"running swimming",查询以后的结果是,只要帖子content中至少包含一个词汇就能够,即帖子content中包含"running","swimming","running ... swimming ..."都返回
*containsWords:查询词 或词组 ,好比 "sport","running swimming"
*uid_tid:查询成功返回的N条uid和tid数据
*/
int searchAllByContainWords_OR(const std::string &containsWords,vector<struct Uid_Tid> &uid_tid);
封装后造成的http请求:
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/_search
-d ‘{"query":{"match": {"content":{"query":"swimming running"","operator":"or"}}},"_source":["uid","tid"]}’
PS: operator 是or,表示是或逻辑操做,query中填写多个字段,匹配数据字段content中存在swimming或running,_source 中有数据字段uid和tid,用来控制返回结果的,当数据量很大的时候对结果裁剪,减小无用的数据传输
/*功能:以与的逻辑关系操做查询帖子中是否存在包含词,并过滤返回结果,只返回并获得UID,tid
*注:好比查询词是词组:"running swimming",查询以后的结果是,帖子content中包含每一个词汇,即帖子content中包含running和 swimming才返回
*containsWords:查询词 或词组 ,好比 "sport","running swimming"
*uid_tid:查询成功返回的N条uid和tid数据
*/
Int searchAllByContainWords_AND(const std::string &containsWords,vector<struct Uid_Tid> &uid_tid);
封装后造成的http请求:
curl -H "Content-Type: application/json" -XPOST "http://localhost:9200/_search
-d ‘{"query":{"match": {"content":{"query":"swimming running"","operator":"and"}}},"_source":["uid","tid"]}’
PS:同上,区别是operator是and,表示是与逻辑操做
相似c++ API接口参考:https://github.com/QHedgeTech/cpp-elasticsearch
在Elasticsearch和磁盘之间是文件系统缓存。 在内存索引缓冲区中的文档会被写入到一个新的段中,可是这里新段会被先写入到文件系统缓存(这一步代价会比较低),稍后再被刷新(refresh)到磁盘(这一步代价比较高fsync)。不过只要文件已经在缓存中,就能够像其它文件同样被打开和读取了。在 Elasticsearch 中,写入和打开一个新段的过程叫作 refresh 。 默认状况下每一个分片会每秒自动刷新一次。
在fsync以后的数据是不会有损失的,若是你设置的refresh_interval刷新间隔过大,设备忽然断电,这种状况避免不了数据丢失
当集群设置了分片(shard)和副本(replicats)时,(少部分节点挂掉)数据不会有损失。(PS:集群有3个节点,一个一个挂掉,只要有一个节点在,数据不会损失,集群可用)
同时挂了多台,致使剩余节点中副本和分片拼不齐一个完整的数据时,集群将失效。(PS:集群有3个节点,同时挂掉2个,集群不可用,当只要再恢复一个,使可以拼齐一条完整的数据,集群恢复可用)。猜想,若是此时加入的是全新节点,可能不起做用。
这部分还须要考虑主副数据一致性,参见6.15
答案同2
只要集群数据完整性没受到破坏,集群中Master 当即注意到了这个节点的离线,它决定在集群内提拔其余拥有该崩溃节点上面的主分片对应的副本分片为主分片,将找到该崩溃节点的副本
在副本被提拔为主分片之后,master 节点开始执行恢复操做来重建缺失的副本。集群中的节点之间互相拷贝分片数据,网卡压力剧增
因为目前集群处于非平衡状态,这个过程还有可能会触发小规模的分片移动。其余不相关的分片将在节点间迁移来达到一个最佳的平衡状态,集群状态变绿
该节点自动加入集群,这个节点被告知当前的数据已经没有用了, 数据已经在其余节点上从新分配了。因此 该节点 把本地的数据进行删除,而后从新开始恢复集群的其余分片,触发小规模的分片移动。其余不相关的分片将在节点间迁移来达到一个最佳的平衡状态,集群状态变绿
curl -X PUT 10.3.246.224/_all/_settings -d '{"settings": {"index.unassigned.node_left.delayed_timeout": "5m" }}'
Elasticsearch 会检查该机器磁盘上的分片数据和当前集群中的活跃主分片的数据是否是同样 — 若是二者匹配, 说明没有进来新的文档,包括删除和修改 — 那么 master 将会取消正在进行的再平衡并恢复该机器磁盘上的数据。
之因此这样作是由于本地磁盘的恢复永远要比网络间传输要快,而且咱们保证了他们的分片数据是同样的,这个过程能够说是共赢。
若是分片已经产生了分歧(好比:节点离线以后又索引了新的文档),那么恢复进程会继续按照正常流程进行。从新加入的节点会删除本地的、过期的数据,而后从新获取一份新的。
只要集群数据完整性没受到破坏,集群可用,数据没有损失
PS:什么叫数据完整性?即集群中对数据进行分片和副本,将分片副本按照必定算法分配到不一样的节点上,任何一个节点down机或一个一个down机,都不会影响其余节点的分片或副本,能够将数据拼齐,恢复完整
在集群正常使用下,加入新节点,将会使数据从新分布,以使任何一个(或部分)节点挂掉,仍能拼齐一条完整的数据。
集群健康:curl -X get http://10.3.246.224:9200/_cluster/health?pretty
集群统计:
curl -XGET 'http://10.3.246.224:9200/_cluster/stats?human&pretty'
查看每一个节点状态:
curl -XGET http://10.3.246.224:9200/_cat/nodes?v
主分片的数目在索引建立时就已经肯定了下来。实际上,这个数目定义了这个索引可以 存储 的最大数据量。(实际大小取决于你的数据、硬件和使用场景。) 可是,读操做——搜索和返回数据——能够同时被主分片 或 副本分片所处理,因此当你拥有越多的副本分片时,也将拥有越高的吞吐量。
在运行中的集群上是能够动态调整副本分片数目的,咱们能够按需伸缩集群。让咱们把副本数从默认的 1增长到 2 :
对整个index调整副本数:
curl -H "Content-Type: application/json" -X PUT http://10.3.246.222:9200/_settings -d '{"number_of_replicas" : 2}'
对某个index按需调整副本数2:
curl -H "Content-Type: application/json" -X PUT http://10.3.246.222:9200/index/_settings -d '{"number_of_replicas" : 2}'
shard = hash(routing) % number_of_primary_shards
routing:是一个可变值,默认是文档的id,也能够设置成一个自定义的值
number_of_primary_shards :主分片的数量,如6,建立索引的时候就肯定好主分片的数量而且永远不会改变这个数量:由于若是数量变化了,那么全部以前路由的值都会无效,文档也再也找不到了
这个没有要求。只有一个节点的,叫单节点集群。集群可用,是在可用节点中数据都具有完整性,即为可用。不然集群不可用。
在 Elasticsearch 中文档是 不可改变 的,不能修改它们(倒排索引被写入磁盘后是 不可改变 的:它永远不会修改)。 相反,若是想要更新现有的文档,须要 重建索引或者进行替换。
更新操做:1,从旧文档构建 JSON2,更改该 JSON 3,删除旧文档 4,索引一个新文档
如下是部分更新一个文档的步骤:
1) 客户端向 Node 1 发送更新请求。
2) 它将请求转发到主分片所在的 Node 3 。
3) Node 3 从主分片检索文档,修改 _source 字段中的 JSON ,而且尝试从新索引主分片的文档。 若是文档已经被另外一个进程修改,它会重试步骤 3 ,超过 retry_on_conflict 次后放弃。
4) 若是 Node 3 成功地更新文档,它将新版本的文档并行转发到 Node 1 和 Node 2 上的副本分片,从新创建索引。 一旦全部副本分片都返回成功, Node 3 向协调节点也返回成功,协调节点向客户端返回成功。
咱们能够发送请求到集群中的任一节点。 每一个节点都有能力处理任意请求。 每一个节点都知道集群中任一文档位置,因此能够直接将请求转发到须要的节点上。每一个节点都是这样的协调节点(coordinating node)
如下是在主副分片和任何副本分片上面 成功新建,索引和删除文档所须要的步骤顺序:
1) 客户端向 Node 1 发送新建、索引或者删除请求。
2) 节点使用文档的 _id 肯定文档属于分片 0 。请求会被转发到 Node 3`,由于分片 0 的主分片目前被分配在 `Node 3 上。
3) Node 3 在主分片上面执行请求。若是成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。一旦全部的副本分片都报告成功, Node 3 将向协调节点报告成功,协调节点向客户端报告成功。
在客户端收到成功响应时,文档变动已经在主分片和全部副本分片执行完成,变动是安全的
如下是从主分片或者副本分片检索文档的步骤顺序:
1) 客户端向 Node 1 发送获取请求。
2) 节点使用文档的 _id 来肯定文档属于分片 0 。分片 0 的副本分片存在于全部的三个节点上。 在这种状况下,它将请求转发到 Node 2 。
3) Node 2 将文档返回给 Node 1 ,而后将文档返回给客户端。
在处理读取请求时,协调结点在每次请求的时候都会经过轮询全部的副本分片来达到负载均衡
consistency参数的值能够设为
one:要主分片状态 ok 就容许执行_写_操做
all:必需要主分片和全部副本分片的状态没问题才容许执行_写_操做
quorum:默认为quorum,即大多数的分片副本状态没问题就容许执行_写_操做。
’quorum =( (primary + number_of_replicas) / 2 ) + 1
若是你的索引设置中指定了当前索引拥有三个副本分片(number_of_replicas=3),那quorum=3 (primary=1)
若是此时你只启动两个节点,那么处于活跃状态的分片副本数量就达不到规定数量,也所以您将没法索引和删除任何文档。
在Elasticsearch和磁盘之间是文件系统缓存。 在内存索引缓冲区中的文档会被写入到一个新的段中,可是这里新段会被先写入到文件系统缓存(这一步代价会比较低),稍后再被刷新到磁盘(这一步代价比较高fsync)。不过只要文件已经在缓存中,就能够像其它文件同样被打开和读取了。
在 Elasticsearch 中,写入和打开一个新段的轻量的过程叫作 refresh 。 默认状况下每一个分片会每秒自动刷新一次。
想优化索引速度而不是近实时搜索,能够经过建立索引时设置 refresh_interval
curl -H "Content-Type: application/json" -X PUT http://10.3.246.223:9200/test_refresh -d '{"settings": {"refresh_interval": "30s" }}'
中止刷新
curl -H "Content-Type: application/json" -X PUT http://10.3.246.223:9200/test_refresh/_settings -d '{"refresh_interval":-1}'
refresh_interval 能够在既存索引上进行动态更新
curl -H "Content-Type: application/json" -X PUT http://10.3.246.223:9200/test_refresh/_settings -d '{"refresh_interval":"30m"}'//-1 不设置,1一毫秒,1s一秒,30m 30分钟
一个文档被索引以后,就会被添加到内存缓冲区,而且追加到了 translog。
translog 提供全部尚未被刷到磁盘的操做的一个持久化纪录。当 Elasticsearch 启动的时候,它会从磁盘中使用最后一个提交点去恢复已知的段,而且会重放 translog 中全部在最后一次提交后发生的变动操做。
在文件被fsync到磁盘前,被写入的文件在重启以后就会丢失。默认translog 是每5秒被fsync刷新到硬盘,或者在每次写请求完成以后执行。这个过程在主分片和复制分片都会发生。最终,基本上,这意味着在整个请求被fsync到主分片和复制分片的translog以前,你的客户端不会获得一个200 OK响应。
能够设置异步fsync
curl -H "Content-Type: application/json" -X PUT http://10.3.246.223:9200/tttt/_settings -d '{"index.translog.durability": "async","index.translog.sync_interval": "5s"}'
这个选项能够针对索引单独设置,而且能够动态进行修改。若是你决定使用异步 translog 的话,你须要 保证 在发生crash时,丢失掉 sync_interval 时间段的数据也无所谓
若是你不肯定这个行为的后果,最好是使用默认的参数( "index.translog.durability": "request" )来避免数据丢失
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
https://www.elastic.co/guide/cn/elasticsearch/guide/current/intro.html
https://discuss.elastic.co/c/elasticsearch/
在chrome中直接安装的插件,要比在linux下命令安装简单
ES也提供对数据的分析,功能很是强大,还有不少像ik这样分词插件,咱们在浏览器中访问es服务,在web界面中也能够很方便的操做数据。了解的很是浅显,暂记于此,以待有机会深刻了解。对于json格式以及json中数据空格也须要很是注意。