Elasticsearch05:ES的基本操作
一、ES的基本操作
针对ES的操作,官方提供了很多种操作方式。
https://www.elastic.co/guide/index.html
在实际工作中使用ES的时候,如果想屏蔽语言的差异,建议使用REST API,这种兼容性比较好,但是个人感觉有的操作使用起来比较麻烦,需要拼接组装各种数据字符串。
针对Java程序员而言,还有一种选择是使用Java API,这种方式相对于REST API而言,代码量会大一些,但是代码层面看起来是比较清晰的。
下面在操作ES的时候,分别使用一下这两种方式:
使用REST API的方式操作ES
使用Java API的方式操作ES
二、使用REST API的方式操作ES
如果想要在Linux命令行中使用REST API操作ES,需要借助于CURL工具。
CURL是利用URL语法在命令行下工作的开源文件传输工具,使用CURL可以简单实现常见的get/post请求。
curl后面通过-X参数指定请求类型,通过-d指定要传递的参数。
1、索引库的操作(创建、删除)
(1)创建索引库:
curl -XPUT ‘http://bigdata01:9200/test/’
这里使用PUT或者POST都可以。
[root@bigdata01 soft]# curl -XPUT 'http://bigdata01:9200/test/'
{"acknowledged":true,"shards_acknowledged":true,"index":"test"}
注意:索引库名称必须要全部小写,不能以_、 -、 +开头,也不能包含逗号。
错误示例:
[root@bigdata01 soft]# curl -XPUT 'http://bigdata01:9200/_test/' {"error":{"root_cause":[{"type":"invalid_index_name_exception","reason":"Invalid index name [_test], must not start with '_', '-', or '+'","index_uuid":"_na_","index":"_test"}],"type":"invalid_index_name_exception","reason":"Invalid index name [_test], must not start with '_', '-', or '+'","index_uuid":"_na_","index":"_test"},"status":400}
[root@bigdata01 soft]# curl -XPUT 'http://bigdata01:9200/Test/'
{"error":{"root_cause":[{"type":"invalid_index_name_exception","reason":"Invalid index name [Test], must be lowercase","index_uuid":"_na_","index":"Test"}],"type":"invalid_index_name_exception","reason":"Invalid index name [Test], must be lowercase","index_uuid":"_na_","index":"Test"},"status":400}
(2)删除索引库:
[root@bigdata01 soft]# curl -XDELETE 'http://bigdata01:9200/test/'
{"acknowledged":true}注意:索引库可以提前创建,也可以在后期添加数据的时候直接指定一个不存在的索引库,ES默认会自动创建这个索引库。
手工创建索引库和自动创建索引库的区别就是,手工创建可以自定义索引库的分片数量。
(3)创建一个具有3个分片的索引库
下面创建一个具有3个分片的索引库。
[root@bigdata01 soft]# curl -H "Content-Type: application/json" -XPUT 'http://bigdata01:9200/test/' -d'{"settings":{"index.number_of_shards":3}}'
{"acknowledged":true,"shards_acknowledged":true,"index":"test"}
其中实线的框表示是主分片,虚线框是副本分片。
索引分片编号是从0开始的,并且索引分片在物理层面是存在的,可以到集群中查看一下,从界面中也看到test索引库的1号和2号分片是在bigdata01节点上的。
到bigdata01节点中看一下,ES中的所有数据都在ES的数据存储目录中,默认是在ES_HOME下的data目录里面:
[root@bigdata01 1IQ2r-vqRxSsicd8BzWPtg]# pwd
/data/soft/elasticsearch-7.13.4/data/nodes/0/indices/1IQ2r-vqRxSsicd8BzWPtg
[root@bigdata01 1IQ2r-vqRxSsicd8BzWPtg]# ll
total 0
drwxrwxr-x. 5 es es 49 Feb 26 18:01 1
drwxrwxr-x. 5 es es 49 Feb 26 18:01 2
drwxrwxr-x. 2 es es 24 Feb 26 18:01 _state
这里面的1IQ2r-vqRxSsicd8BzWPtg表示的是索引库的UUID。
4、索引的操作(增、删、改、查、Bulk批量操作)
(1)添加索引:
[root@bigdata01 soft]# curl -H "Content-Type: application/json" -XPOST 'http://bigdata01:9200/emp/_doc/1' -d '{"name":"tom","age":20}'
{"_index":"emp","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}
注意:
1、这里emp索引库是不存在的,在使用的时候ES会自动创建,只不过索引分片数量默认是1。
为了兼容之前的API,虽然ES现在取消了Type,但是API中Type的位置还是预留出来了,官方建议统一使用_doc 。
注意:在添加索引的时候,如果没有指定数据的ID,那么ES会自动生成一个随机的唯一ID。
[root@bigdata01 soft]# curl -H "Content-Type: application/json" -XPOST 'http://bigdata01:9200/emp/_doc' -d '{"name":"jack","age":30}'
{"_index":"emp","_type":"_doc","_id":"EFND8aMBpApLBooiIWda","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":1,"_primary_term":1}
(2)查询索引:
查看id=1的索引数据。
[root@bigdata01 soft]# curl -XGET 'http://bigdata01:9200/emp/_doc/1?pretty'
{"_index" : "emp","_type" : "_doc","_id" : "1","_version" : 1,"_seq_no" : 0,"_primary_term" : 1,"found" : true,"_source" : {"name" : "tom","age" : 20}
}
只获取部分字段内容。
[root@bigdata01 soft]# curl -XGET 'http://bigdata01:9200/emp/_doc/1?_source=name&pretty'
{"_index" : "emp","_type" : "_doc","_id" : "1","_version" : 1,"_seq_no" : 0,"_primary_term" : 1,"found" : true,"_source" : {"name" : "tom"}
}
[root@bigdata01 soft]# curl -XGET 'http://bigdata01:9200/emp/_doc/1?_source=name,age&pretty'
{"_index" : "emp","_type" : "_doc","_id" : "1","_version" : 1,"_seq_no" : 0,"_primary_term" : 1,"found" : true,"_source" : {"name" : "tom","age" : 20}
}
(3)查询指定索引库中所有数据
[root@bigdata01 soft]# curl -XGET 'http://bigdata01:9200/emp/_search?pretty'
{"took" : 2,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "emp","_type" : "_doc","_id" : "1","_score" : 1.0,"_source" : {"name" : "tom","age" : 20}},{"_index" : "emp","_type" : "_doc","_id" : "EVPO8aMBpApLBooib2e7","_score" : 1.0,"_source" : {"name" : "jack","age" : 30}}]}
}
注意:针对这种查询操作,可以在浏览器里面执行,或者在cerebo中查询都是可以的,看起来更加清晰。
(4)更新索引
可以分为全部更新和局部更新
全部更新:同添加索引,如果指定id的索引数据(文档)已经存在,则执行更新操作。
注意:执行更新操作的时候,ES首先将旧的文标记为删除状态,然后添加新的文档
旧的文档不会立即消失,但是你也无法访问,ES会在你继续添加更多数据的时候在后台清理已经标记为删除状态的文档。
局部更新:可以添加新字段或者更新已有字段,必须使用POST请求。
[root@bigdata01 soft]# curl -H "Content-Type: application/json" -XPOST 'http://bigdata01:9200/emp/_doc/1/_update' -d '{"doc":{"age":25}}'
{"_index":"emp","_type":"_doc","_id":"1","_version":2,"result":"updated","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":2,"_primary_term":1}
(5)删除索引:
删除id=1的索引数据。
[root@bigdata01 soft]# curl -XDELETE 'http://bigdata01:9200/emp/_doc/1'
{"_index":"emp","_type":"_doc","_id":"1","_version":3,"result":"deleted","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":3,"_primary_term":1}[root@bigdata01 soft]# curl -XDELETE 'http://bigdata01:9200/emp/_doc/1'
{"_index":"emp","_type":"_doc","_id":"1","_version":4,"result":"not_found","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":4,"_primary_term":1}
如果索引数据(文档)存在,ES返回的数据中,result属性值为deleted,_version(版本)属性的值+1。
如果索引数据不存在,ES返回的数据中,result属性值为not_found,但是_version属性的值依然会+1,这属于ES的版本控制系统,它保证了我们在多个节点间的不同操作的顺序都被正确标记了。
对于索引数据的每次写操作,无论是 index,update 还是 delete,ES都会将_version增加 1。该增加是原子的,并且保证在操作成功返回时会发生。
注意:删除一条索引数据(文档)也不会立即生效,它只是被标记成已删除状态。ES将会在你之后添加更多索引数据的时候才会在后台清理标记为删除状态的内容。
5、Bulk批量操作
Bulk API可以帮助我们同时执行多个请求,提高效率。
格式:
{ action: { metadata }}
{ request body }
解释:
action:index/create/update/delete
metadata:_index,_type,_id
request body:_source(删除操作不需要)
create 和index的区别:如果数据存在,使用create操作失败,会提示文档已经存在,使用index则可以成功执行。
下面来看一个案例,假设在MySQL中有一批数据,首先需要从MySQL中把数据读取出来,然后将数据转化为Bulk需要的数据格式。
在这直接手工生成Bulk需要的数据格式。
[root@bigdata01 elasticsearch-7.13.4]# vi request
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "1" } }
{ "field1" : "value1" }
{ "index" : { "_index" : "test", "_type" : "_doc", "_id" : "2" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_type" : "_doc", "_id" : "2" } }
{ "create" : { "_index" : "test", "_type" : "_doc", "_id" : "3" } }
{ "field1" : "value1" }
{ "update" : {"_index" : "test", "_type" : "_doc","_id" : "1" } }
{ "doc" : {"field2" : "value2"} }
6、执行Bulk API
[root@bigdata01 elasticsearch-7.13.4]# curl -H "Content-Type: application/json" -XPUT 'http://bigdata01:9200/test/_doc/_bulk' --data-binary @request
{"took":167,"errors":false,"items":[{"index":{"_index":"test","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":0,"_primary_term":1,"status":201}},{"index":{"_index":"test","_type":"_doc","_id":"2","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":0,"_primary_term":1,"status":201}},{"delete":{"_index":"test","_type":"_doc","_id":"2","_version":2,"result":"deleted","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":1,"_primary_term":1,"status":200}},{"create":{"_index":"test","_type":"_doc","_id":"3","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":2,"_primary_term":1,"status":201}},{"update":{"_index":"test","_type":"_doc","_id":"1","_version":2,"result":"updated","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":1,"_primary_term":1,"status":200}}]}
查看结果:
[root@bigdata01 elasticsearch-7.13.4]# curl -XGET 'http://bigdata01:9200/test/_search?pretty'
{"took" : 6,"timed_out" : false,"_shards" : {"total" : 3,"successful" : 3,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 2,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "test","_type" : "_doc","_id" : "3","_score" : 1.0,"_source" : {"field1" : "value1"}},{"_index" : "test","_type" : "_doc","_id" : "1","_score" : 1.0,"_source" : {"field1" : "value1","field2" : "value2"}}]}
}
Bulk一次最大可以处理多少数据量?
Bulk会把将要处理的数据加载到内存中,所以数据量是有限制的,最佳的数据量不是一个确定的数值,它取决于集群硬件,文档大小、文档复杂性,索引以及ES集群的负载。
一般建议是1000-5000个文档,如果文档很大,可以适当减少,文档总大小建议是5-15MB,默认不能超过100M。
如果想要修改最大限制大小,可以在ES的配置文件中修改http.max_content_length: 100mb,但是不建议,因为太大的话Bulk操作也会慢。
三、使用Java API的方式操作ES
针对Java API,目前ES提供了两个Java REST Client版本:
1、Java Low Level REST Client:
低级别的REST客户端,通过HTTP与集群交互,用户需自己组装请求JSON串,以及解析响应JSON串。兼容所有Elasticsearch版本。
这种方式其实就相当于使用Java对前面讲的REST API做了一层简单的封装,前面我们是使用的CURL这个工具执行的,现在是使用Java代码模拟执行HTTP请求了。
2、Java High Level REST Client:
高级别的REST客户端,基于低级别的REST客户端进行了封装,增加了组装请求JSON串、解析响应JSON串等相关API,开发代码使用的ES版本需要和集群中的ES版本一致,否则会有版本冲突问题。
这种方式是从ES 6.0版本开始加入的,目的是以Java面向对象的方式进行请求、响应处理。
高级别的REST客户端会兼容高版本的ES集群,例如:使用ES7.0版本开发的代码可以和任何7.x版本的ES集群交互。
如果ES集群后期升级到了8.x版本,那么也要升级之前基于ES 7.0版本开发的代码。
如果考虑到代码后期的兼容性,建议使用Java Low Level REST Client。
如果考虑到易用性,建议使用Java High Level REST Client。
在这我们使用Java High Level REST Client。
创建maven项目:db_elasticsearch
创建包:com.imooc.es
在pom.xml文件中添加ES的依赖和日志的依赖。
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.13.4</version>
</dependency>
<dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.14.1</version>
</dependency>
在resources目录下添加log4j2.properties。
appender.console.type = Console
appender.console.name = console
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %marker%m%nrootLogger.level = info
rootLogger.appenderRef.console.ref = console
1、索引库的操作(创建、删除)
package com.imooc.es;import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.settings.Settings;import java.io.IOException;/*** 针对ES中索引库的操作* 1:创建索引库* 2:删除索引库* */
public class EsIndexOp {public static void main(String[] args) throws Exception{//获取RestClient连接RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("bigdata01", 9200, "http"),new HttpHost("bigdata02", 9200, "http"),new HttpHost("bigdata03", 9200, "http")));//创建索引库//createIndex(client);//删除索引库//deleteIndex(client);//关闭连接client.close();}private static void deleteIndex(RestHighLevelClient client) throws IOException {DeleteIndexRequest deleteRequest = new DeleteIndexRequest("java_test");//执行client.indices().delete(deleteRequest, RequestOptions.DEFAULT);}private static void createIndex(RestHighLevelClient client) throws IOException {CreateIndexRequest createRequest = new CreateIndexRequest("java_test");//指定索引库的配置信息createRequest.settings(Settings.builder().put("index.number_of_shards", 3)//指定分片个数);//执行client.indices().create(createRequest, RequestOptions.DEFAULT);}}
执行代码的时候会有一个警告信息,提示ES集群没有开启权限校验机制,其实在企业中只要在运维层面控制好了ES集群IP和端口的访问其实就足够了。
2、索引的操作(增、删、改、查、Bulk批量操作)
package com.imooc.es;import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHost;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** 针对ES中索引数据的操作* 增删改查* */
public class EsDataOp {private static Logger logger = LogManager.getLogger(EsDataOp.class);public static void main(String[] args) throws Exception{//获取RestClient连接RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("bigdata01", 9200, "http"),new HttpHost("bigdata02", 9200, "http"),new HttpHost("bigdata03", 9200, "http")));//创建索引//addIndexByJson(client);//addIndexByMap(client);//查询索引//getIndex(client);//getIndexByFiled(client);//更新索引//注意:可以使用创建索引直接完整更新已存在的数据//updateIndexByPart(client);//局部更新//删除索引//deleteIndex(client);//Bulk批量操作//bulkIndex(client);//关闭连接client.close();}private static void bulkIndex(RestHighLevelClient client) throws IOException {BulkRequest request = new BulkRequest();request.add(new IndexRequest("emp").id("20").source(XContentType.JSON,"field1", "value1","field2","value2"));request.add(new DeleteRequest("emp", "10"));//id为10的数据不存在,但是执行删除是不会报错的request.add(new UpdateRequest("emp", "11").doc(XContentType.JSON,"age", 19));request.add(new UpdateRequest("emp", "12")//id为12的数据不存在,这一条命令在执行的时候会失败.doc(XContentType.JSON,"age", 19));//执行BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);//如果Bulk中的个别语句出错不会导致整个Bulk执行失败,所以可以在这里判断一下是否有返回执行失败的信息for (BulkItemResponse bulkItemResponse : bulkResponse) {if (bulkItemResponse.isFailed()) {BulkItemResponse.Failure failure = bulkItemResponse.getFailure();logger.error("Bulk中出现了异常:"+failure);}}}private static void deleteIndex(RestHighLevelClient client) throws IOException {DeleteRequest request = new DeleteRequest("emp", "10");//执行client.delete(request, RequestOptions.DEFAULT);}private static void updateIndexByPart(RestHighLevelClient client) throws IOException {UpdateRequest request = new UpdateRequest("emp", "10");String jsonString = "{\"age\":23}";request.doc(jsonString, XContentType.JSON);//执行client.update(request, RequestOptions.DEFAULT);}private static void getIndexByFiled(RestHighLevelClient client) throws IOException {GetRequest request = new GetRequest("emp", "10");//只查询部分字段String[] includes = new String[]{"name"};//指定包含哪些字段String[] excludes = Strings.EMPTY_ARRAY;//指定多滤掉哪些字段FetchSourceContext fetchSourceContext = new FetchSourceContext(true, includes, excludes);request.fetchSourceContext(fetchSourceContext);//执行GetResponse response = client.get(request, RequestOptions.DEFAULT);//通过response获取index、id、文档详细内容(source)String index = response.getIndex();String id = response.getId();if(response.isExists()){//如果没有查询到文档数据,则isExists返回false//获取json字符串格式的文档结果String sourceAsString = response.getSourceAsString();System.out.println(sourceAsString);//获取map格式的文档结果Map<String, Object> sourceAsMap = response.getSourceAsMap();System.out.println(sourceAsMap);}else{logger.warn("没有查询到索引库{}中id为{}的文档!",index,id);}}private static void getIndex(RestHighLevelClient client) throws IOException {GetRequest request = new GetRequest("emp", "10");//执行GetResponse response = client.get(request, RequestOptions.DEFAULT);//通过response获取index、id、文档详细内容(source)String index = response.getIndex();String id = response.getId();if(response.isExists()){//如果没有查询到文档数据,则isExists返回false//获取json字符串格式的文档结果String sourceAsString = response.getSourceAsString();System.out.println(sourceAsString);//获取map格式的文档结果Map<String, Object> sourceAsMap = response.getSourceAsMap();System.out.println(sourceAsMap);}else{logger.warn("没有查询到索引库{}中id为{}的文档!",index,id);}}private static void addIndexByMap(RestHighLevelClient client) throws IOException {IndexRequest request = new IndexRequest("emp");request.id("11");HashMap<String, Object> jsonMap = new HashMap<String, Object>();jsonMap.put("name", "tom");jsonMap.put("age", 17);request.source(jsonMap);//执行client.index(request, RequestOptions.DEFAULT);}private static void addIndexByJson(RestHighLevelClient client) throws IOException {IndexRequest request = new IndexRequest("emp");request.id("10");String jsonString = "{" +"\"name\":\"jessic\"," +"\"age\":20" +"}";request.source(jsonString, XContentType.JSON);//执行client.index(request, RequestOptions.DEFAULT);}
}
Elasticsearch05:ES的基本操作相关推荐
- ElasticSearch8.x.x 【一篇文章精通系列】【ES的基本操作,ES安装,ES head + Kibana】
ElasticSearch8.x.x [一篇文章精通系列][ES的基本操作,ES安装,ES head + Kibana] 一.ElasticSearch的安装 1.解压安装ES 2.熟悉目录 3.启动 ...
- es的基本操作(创建索引,添加数据,删除数据,判断索引是否存在)
1.创建索引+ik分词器 /*** 创建es索引* $indexName 索引名称*/public function createEsIndex($indexName){$esClient = $th ...
- Elasticsearch基本操作 1:ES 基本操作、批处理
一:ES的基本操作 1.索引操作 1.1.建立索引 语法:PUT /索引名 在没有特殊设置的情况下,默认有5个分片,1个备份,也可以通过请求参数的方式来指定.. 参数格式: PUT /user { & ...
- Elasticsearch 实战 - 第三讲:ES 基本操作、批处理
Elasticsearch 实战 - 第三讲:ES 基本操作.批处理 Elasticsearch 实战系列文章: 一:ES的基本操作 1.索引操作 1.1.建立索引 1.2.删除索引 1.3.查询索引 ...
- Opengl ES系列学习--顶点属性、顶点数组和缓冲区对象
本节我们继续来看一下<OPENGL ES 3.0编程指南 原书第2版(中文版)>书中第6章的内容,PDF下载地址:OPENGL ES 3.0编程指南 原书第2版(中文版),代码下载地址:O ...
- hdfs、MapReduce、yarn、hive、sqoop、HBASE、Redis、kafka、ES
目录 大数据开发复习课程 1.Hadoop 1.1.介绍Hadoop 1.2.Hadoop特性优点 1.3.hadoop集群中hadoop都需要启动哪些进程,他们的作用分别是什么? 1.4.Hadoo ...
- ElasticSearch - SpringBoot集成ES
文章目录 ElasticSearch - SpringBoot集成ES 1.整体设计思路(仿NBA中国官网) 2.项目搭建 3.ES API的基本使用 3.1 新增球员信息 3.2 查看球员信息 3. ...
- Elasticsearch-06 Spring Boot 2.0.9整合ElasticSearch5.6.16
文章目录 概述 官方JAVA API文档 工程 pom.xml es配置文件 Es配置类 控制层 简单查询 新增数据 删除数据 更新数据 复合查询 其他 新建索引 删除索引 判断index中某个typ ...
- springboot 单测加入参数_Spring Boot集成Elasticsearch实战分享
作者|java梦想口服液|简书 最近有读者问我能不能写下如何使用 Spring Boot 开发 Elasticsearch(以下简称 ES) 相关应用,今天就讲解下如何使用 Spring Boot 结 ...
最新文章
- VS2015不显示工程文本信息
- mongodb高可用性架构---Replica Set
- tftp ubuntu安装
- VMware安装MikroTik RouterOS chr
- springboot项目中pom.xml文件的颜色变成灰色,图标变成蜘蛛图形
- Union-Find 算法应用
- oracle包写入程序失败_Oracle内核技术揭秘第一篇
- intellij远程调试_IntelliJ中的远程调试Wildfly应用程序
- LwIP之ARP协议
- LeetCode 454. 4Sum II
- 简单粗暴地理解 JS 原型链
- linux安装yum报错Unable to locate package yum
- 【FPGA】 Altera FPGA 入门篇(1)
- WinAppDriver UI自动化测试环境搭建
- 一文搞懂步进电机特性、原理及驱动器设计
- Ubuntu录制gif动态图
- cac会议投稿难度大吗_英文(会议)论文投稿准备与流程
- WPF窗体更换背景颜色
- JavaScript的ES3, ES5, ES6实现继承方式
- 企业微信邮箱可以移动办公吗?