写给大忙人的Elasticsearch架构与概念(未完待续)
最新版本官方文档https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
文档增删改参考https://www.elastic.co/guide/en/elasticsearch/reference/6.2/docs.html
ES除了用于搜索尽可能满意的结果,要达到一定的SLA也是可以保证的,例如秒级别。
https://blog.csdn.net/zhangcongyi420/article/details/96508712、https://blog.csdn.net/u010454030/article/details/79586072
例如有一个上亿的表或者有数十万产品的表,每次依赖数据库的like进行模糊匹配,很快就导致系统不堪重负。一种方式是垂直切分表,将部分作为模糊搜索条件的列作为单独的表进行维护,然后将符合条件的ID作为条件传递给主查询(它的限制是排序条件有限的,难以支持按照任意字段如金额排序,更不要说多列),如果用于搜索的部分仍然太大,那么关系数据库就不那么合适了,就需要使用专门的搜索数据库例如ES,如今的ES是可以完全做到精确匹配的。第二种方式的前半部分思路和第一种一样,但是它并不返回符合条件的ID,而是将模糊搜索转为精确搜索,也就是利用补全机制,让用户多选精确条件,以此实现模糊匹配+支持任何排序。对于一个核心页面来说,通常看哪一方强势选择实现方式,平台通常选择第一种,IT供应商通常选择第二种。但是任何的NoSQL存储,都必须考虑如何关联查询,实际中很大一部分都是要关联查询的,ES官方对关联的查询。实际上无论是mongodb还是es,在关联、java的driver上做的都一般般,对于海量数据的to B而言,其实都没有MPP、分区架构来的成本低,尤其是在三范式较好的系统中。
除了ES外,还有oracle全文检索。如果不想再维护一个组件,可以考虑集成lucene。
Elasticsearch主要概念
Index,索引:一系列具有类似属性的文档集合,类似于数据库里的表,集群中可以包含的索引数不限,索引是逻辑概念(对应物理上为分片,shard),一般应提前手工创建而非自动创建便于管理,索引的名称和存储名称不相同但是有着对应关系,可通过查询索引信息的uuid得到。一个典型的索引定义如下:
{
"logstash-2018-06": {
"aliases": {},
"mappings": {
"_default_": {
"dynamic_templates": [
{
"message_field": {
"path_match": "message",
"match_mapping_type": "string",
"mapping": {
"norms": false,
"type": "text"
}
}
},
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"norms": false,
"type": "text"
}
}
}
],
"properties": {
"@timestamp": {
"type": "date"
},
"@version": {
"type": "keyword"
},
"geoip": {
"dynamic": "true",
"properties": {
"ip": {
"type": "ip"
},
"latitude": {
"type": "half_float"
},
"location": {
"type": "geo_point"
},
"longitude": {
"type": "half_float"
}
}
}
}
},
"doc": {
"dynamic_templates": [
{
"message_field": {
"path_match": "message",
"match_mapping_type": "string",
"mapping": {
"norms": false,
"type": "text"
}
}
},
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"norms": false,
"type": "text"
}
}
}
],
"properties": {
"@timestamp": {
"type": "date"
},
"@version": {
"type": "keyword"
},
"beat": {
"properties": {
"hostname": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"version": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"geoip": {
"dynamic": "true",
"properties": {
"ip": {
"type": "ip"
},
"latitude": {
"type": "half_float"
},
"location": {
"type": "geo_point"
},
"longitude": {
"type": "half_float"
}
}
},
"host": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"message": {
"type": "text",
"norms": false
},
"offset": {
"type": "long"
},
"prospector": {
"properties": {
"type": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"source": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"tags": {
"type": "text",
"norms": false,
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
},
"settings": {
"index": {
"refresh_interval": "5s",
"number_of_shards": "5",
"provided_name": "logstash-2018-06",
"creation_date": "1527913627481",
"number_of_replicas": "1",
"uuid": "0VidmJtrT1uoz-TygNCn_Q",
"version": {
"created": "6020499"
}
}
}
}
}
Type,类型:在6.0.0之前的版本中,类型用于在索引中进行二次分类/分区以便在相同索引中存储不同类型的文档,比如同时存储blog和user,这个版本开始,不能再在一个索引中创建多个类型,类型是逻辑概念。
Document,文档:文档代表可以被索引的基本单元,类似数据库里的表,文档在ES中表示为JSON,在索引/类型中,可以存储无限的文档。从物理上来说,文档存储在索引中,从逻辑上,文档实际上归属于索引中的某个类型。一个典型的文档如下:
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
其实就是一个json。文档由 _index 、 _type 和 _id 唯一标识一个文档。
Near Realtime (NRT),准实时:Elasticsearch是准实时搜索平台(官方称秒级)。
Cluster,集群:所有Elasticsearch节点都运行在集群中,即使只有一个节点,集群中可以包含的节点数不限,节点是物理概念,集群是逻辑概念。
Node,节点:简单地说,ES中的所有节点都是用来存储数据的。具体地说,从功能上来看,Elasticsearch有5种类型的节点:
- Data节点,虽然每个节点都可以作为数据节点,但是通过在elasticsearch.yml配置文件中进行如下设置,可以限定某些节点只能作为数据节点,设置专用数据节点可以使得数据节点和Master节点隔离。
node.data: true
node.master: false
node.ingest: false
- Master节点,负责管理整个集群,其持有所有节点的状态并周期性的将集群状态分发给其他所有节点,包括新加入的节点以及退出的节点。master节点的主要职责是配置管理,其包含完整的元数据以及所有索引的映射,当更新密集时,状态信息可能会非常大,每秒1w条数据时,状态信息可能有几十M。2.0版本之后,更新的集群状态信息只发diff,并且是被压缩的。如果master节点挂了,新的主节点会从剩余有资格的主节点中产生,默认情况下,每个节点都可以成为主节点。通过在elasticsearch.yml配置文件中进行如下设置,可以限定某些节点只能作为主节点。
node.data: false
node.master: true
node.ingest: false
- Ingest节点,可以在实际索引前执行预处理数据。通过在elasticsearch.yml配置文件中进行如下设置,可以限定某些节点只能作为Ingest节点。
node.data: false
node.master: false
node.ingest: true
- Tribe节点,是一种特殊的协调节点,或者成为联邦节点,用于代理执行跨集群的操作。
- Coordinating/Client节点,在ES中,每个请求的执行分为两个步骤:分发和聚合。这两个均由接收请求的协调节点管理,同时他们也是ES集群的负载均衡器。通过在elasticsearch.yml配置文件中进行如下设置,可以限定某些节点只能作为协调节点,对于较大型的生产系统,应该配置专门的协调节点,因为聚合节点比较消耗资源。
node.data: false
node.master: false
node.ingest: false
Routing,路由:路由允许我们在索引和搜索数据时选择具体的分片。
Lucene:提供了基于java的索引和搜索技术,Solr类似于ES,也是基于Lucene Core,提供了管理接口。
Shards & Replicas,分片与副本:一个索引包含的文档在存储的时候不一定是一起的,存储文档的每个物理单元称为分片(Elasticsearch 没有采用节点级别的主从复制,而是基于分片,跟couchbase一样)(类似于数据库的分区以及段Partition/Segment)。分片的作用无非两个:
- 容量扩展;
- 并行操作;
为了保证高可用,每个分片可以设置副本数量。在索引创建的时候,可以指定分片和副本的数量,副本数可以按需调整但是分片数量一经定义、无法修改。默认情况下,Elasticsearch为每个索引分配5个分片和1个副本(5个主分片,5个副本分片),这意味着双节点模式。
文档根据公式shard = hash(routing) % number_of_primary_shards计算存储的具体分片,默认是根据文档的id计算,可自定义。
每个Elasticsearch分片是一个Lucene索引/实例(需要注意,Lucene的概念和ES不完全一一对应),一个Lucene索引中文档的数量限制为Integer.MAX_VALUE - 128,这一点需要注意不要超出,分片中文档的数量可以通过API _cat/shards监控。
索引和分片的关系如下:
索引和分片在存储的组织上可以看得出关系(通过api也可以看出):
Analysis,分析:ES是一个全文搜索引擎,为了高性能,它会提前或者按需将文本转换为一系列的符号,ES在索引时执行分析。对于全文检索,ES还会对查询字符串执行搜索时分析。
Mapping(6.0.0开始准备移除,见Type),映射:映射定义了文档及其包含的字段如何打分和索引的过程,包括:哪个字段应该用于全文检索,哪些字段包含日期、数字以及地理信息等。
Mapping Type(6.0.0开始准备移除,见Type,移除的原因是因为ES早期的假设不正确,一开始是假设Index是database,type是表,而数据库里面,不同表中的列是无关的,而Lucene的内部实现则认为是一个字段,所以就懵逼了),映射类型:每个索引都有一个映射类型,定义文档如何索引。一个映射类型包含了下列信息:
- 元字段:用于自定义如何处理文档相关的元数据,元字段包括_index(文档所属的索引), _type(文档的映射类型), _id(文档ID)以及_source(代表文档主体的原始JSON),完整的元数据字段请参考https://www.elastic.co/guide/en/elasticsearch/reference/6.2/mapping-fields.html。
- 字段/属性:映射类型包含了属于文档的字段/属性列表。需要注意的是,ES文档的每个字段也有数据类型,但是只有text类型才会被全文检索,参考https://www.elastic.co/guide/en/elasticsearch/reference/6.2/text.html。
除了在创建索引的时候声明映射类型外,ES还支持动态映射,也就是创建文档的时候自动创建索引、映射类型以及字段(注:生产中应避免使用动态映射特性以最大化性能和存储利用率)。
Mapping parameter,映射参数:对于每种数据类型,我们可以为此声明一定的映射参数,有些参数可以应用于所有数据类型,有些则适用于部分数据类型。比如用于文本分析的分析器就是一个映射参数,完整的映射参数可以参考https://www.elastic.co/guide/en/elasticsearch/reference/6.2/mapping-params.html。
===================================
Elasticsearch基于Java编写,最新版本6.2.x要求Java 8。
ES提供REST API给用户用于日常管理,端口是9100,不过一般情况下,我们都会安装插件Elasticsearch Head,具体安装可以参考https://www.cnblogs.com/zhjh256/p/9126144.html。虽然Head不错,但是有时候现成的环境并没有安装Head,又需要及时排查问题,此时就需要对常见的api熟悉,可以参考https://www.elastic.co/guide/en/elasticsearch/reference/6.2/_exploring_your_cluster.html。
Elasticsearch REST APIs的规范
所有的Elasticsearch REST APIs都是JSON格式作为出入参,这意味着必备Postman,友好强大。一些通用的参数可以参考https://www.elastic.co/guide/en/elasticsearch/reference/6.2/api-conventions.html
Elastic出品的所有产品几乎都包含三个配置文件:XXX.yml,jvm.options,log4j2.properties。Elasticsearch也遵守相同的惯例。
elasticsearch.yml中最重要的包括:
- path.data和path.logs这两个配置,logs中除了记录ES本身的一些日志信息外,还记录搜索异常的日志比如模式无法解析。
- cluster.name:标识一个集群,默认为elasticsearch,一般应该更改,比如对于elk日志平台,可以为logger-dev。
- node.name:节点名,一般来说建议和hostname相同,hostname不应该随机,而是类似elk-es-1便于更好地管理。
- network.host:绑定的网卡,为了方便且网络流量管控不是特别严格的话,可以直接注释掉,也就是任何网卡,默认是本地回环地址,这无论开发还是生产环境都不适用。
- discovery.zen.ping.unicast.hosts:ES使用了一种称为Zen Discovery的自定义搜索来寻找集群节点和master选举。默认情况下,ES仅查找本机回环地址下的9300~9305端口,这在生产中是不适用的,具体可见https://www.elastic.co/guide/en/elasticsearch/reference/6.2/discovery-settings.html。
- discovery.zen.minimum_master_nodes:设置每个有资格作为master的节点集群中应至少有多少个可见的有资格作为master的节点时才组群。为了防止脑裂,一般是N/2+1,通用集群做法(oracle/mysql同)。
jvm.options最主要的是-Xms和-Xmx以及gc、oom、线程池(对于所有的java应用来说,这几方面都是必须事先预防的)。
elasticsearch.yml的所有配置参数可以没有一个统一的地方定义所有,总体在https://www.elastic.co/guide/en/elasticsearch/reference/2.3/setup-configuration.html,索引相关的在https://www.elastic.co/guide/en/elasticsearch/reference/2.3/index-modules.html,分析器相关的在https://www.elastic.co/guide/en/elasticsearch/reference/2.3/analysis.html。注:最新版本的没有去掉了analyse配置的文档,故列出了2.x版本的参考。
注意事项
JDK 8u40之前的版本G1GC有bug,会导致索引损坏。
分析(Analysis)
分析是将文本转化为符号的过程,在文档被索引的时候,内置的english分析器会执行分析。任何一个mapping中的text字段都可以声明自己的分析器,比如:
"mappings": {
"_doc": {
"properties": {
"text": {
"type": "text",
"fields": {
"english": {
"type": "text",
"analyzer": "english"
}
}
}
}
}
}
默认情况下,ES会使用索引创建时设置的default分析器,如果该分析器无效或者不存在的话,使用标准分析器(https://www.elastic.co/guide/en/elasticsearch/reference/6.2/analysis-standard-analyzer.html)。ES提供了一些内置的分析器,见https://www.elastic.co/guide/en/elasticsearch/reference/6.2/analysis-analyzers.html。对于中文而言,这并不合适,因此社区开发了elasticsearch-analysis-ik(中文词语)和https://github.com/medcl/elasticsearch-analysis-pinyin(用于汉字转拼音)。
自定义分析器的创建以及规范参考https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-custom-analyzer.html。
自定义分析器的安装需要在elasticsearch.yml中进行配置,具体可参考https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html。
索引API
ES索引和搜索机制
默认情况下,当有个文档需要索引时,ES会计算文档ID的哈希值,基于该哈希值选择存储的分片,然后这些文档被复制到副本分片。但是查询的时候,由于查询条件并非都是ID等值查询,所以不知道哪个分片中实际存储了匹配的文档,因此需要查询所有的分片,处理客户端请求的节点成为协调节点。如下:
通过API可以看到每个索引的路由信息,如下:
很多时候,在全文搜索的时候,我们会限制仅在某个用户下,比如仅搜索www.cnblogs.com/zhjh256下的文档,此时路由选择就发挥作用了。提供路由值最简单的方式就是在HTTP请求中增加routing参数。即使是最高效的哈希查找,如果可以确保每次搜索时限定具体分片,理论上默认情况下吞吐量就可以提升5倍。
ES REST API提供了一批接口用于管理索引、索引的配置、别名、映射以及索引模板,索引内部信息的监控等。从纯粹使用的角度来说,索引API使用较少,所以它更多地属于管理类API。完整的索引API可以参考https://www.elastic.co/guide/en/elasticsearch/reference/6.2/indices.html。创建一个索引,如:
PUT test
{
"settings" : {
"number_of_shards" : 1
},
"mappings" : {
"type1" : {
"properties" : {
"field1" : { "type" : "text" }
}
}
}
}
创建索引的时候映射类型不是必须的。如果没有定义映射类型,创建文档的时候会自动根据api中的类型名进行创建。
有些时候,我们发现有些文本中有的内容,我们查询的时候并不匹配,此时可以针对具体的文本执行手工分析过程,看下ES是如何解析的,这个时候可以使用_analyze API(postman怎么发送参数格式为JSON的get?),如:
GET _analyze
{
"tokenizer" : "keyword",
"filter" : ["lowercase"],
"text" : "this is a test"
}
索引模板顾名思义,就是为索引定义默认设置,在创建索引的时候自动套上去,因为索引本身的创建是不多的,所以可有可无。
cat APIs
对于程序来说,JSON很适合自动化处理,但是对人类而言,其友好性就差了很多。所以,对于管理型的查询API,ES提供了平面化的接口。例如,查看集群中节点的状态:
GET /_cat/nodes?v
返回如下:
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.1.104 56 94 1 0.23 0.33 0.24 mdi * node-1
更多cat APIs,可参考https://www.elastic.co/guide/en/elasticsearch/reference/6.2/cat.html。
文档APIs
ES的数据复制模型
https://www.elastic.co/guide/en/elasticsearch/reference/6.2/docs-replication.html
文档接口分为单文档接口和多文档接口。
https://www.elastic.co/guide/en/elasticsearch/reference/6.2/docs.html
index api,插入或者更新一个JSON文档,并使其可搜索。例如,在twitter索引的_doc类型下创建一个id为1的文档。
PUT twitter/_doc/1
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
如果_doc类型不存在,会自动创建。如果已经存在了一个叫做_type的类型,则会创建失败,提示不允许包含多个类型。
查询和搜索类接口
ES提供了两种查询接口:搜索APIs和Query DSL。准确的说,前者能做的事情,后者都能做,前者和文档APIs等一起适合于比较简单的CRUD。
搜索APIs
https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search.html
Query DSL
http://www.querydsl.com/
https://www.elastic.co/guide/en/elasticsearch/reference/6.2/query-dsl.html
Elasticsearch与Solr的适用场景比较可参考:https://www.cnblogs.com/simplelovecs/articles/5129276.html
ES底层原理
动态更新索引
倒排索引(Lucene中的段)被写入磁盘后是 不可改变 的:它永远不会修改
es增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到—从最早的开始–查询完后再对结果进行合并
近实时搜索
按段(per-segment)搜索的发展
新段会被先写入到文件系统缓存,稍后再被刷新到磁盘,只要文件已经在缓存中, 就可以像其它文件一样被打开和读取了。
持久化变更
每一次对 Elasticsearch 进行操作时均记录事务日志,当 Elasticsearch 启动的时候,并且会重放 translog 中所有在最后一次提交后发生的变更操作。
段合并
为节省资源,提高检索效率,Elasticsearch通过在后台进行段合并,小的段被合并到大的段,然后这些大的段再被合并到更大的段。
通过optimize API可以将一个分片强制合并到指定的段数目。 (通常减少到一个)。例如在日志这种用例下,每天、每周、每月的日志被存储在一个索引中。 老的索引实质上是只读的;它们也并不太可能会发生变化。
集群扩容
按集群节点来均衡分配这些分片,从而对索引和搜索过程进行负载均衡,复制每个分片以支持数据冗余,从而防止硬件故障导致的数据丢失
当集群只有一个节点,到变成2个节点,3个节点时的 shard 变换图如下:
ES的节点加入与退出机制
- 集群中的某个节点丢失网络连接
- master提升该节点上的所有主分片的在其他节点上的副本为主分片
- cluster集群状态变为 yellow ,因为副本数不够
- 等待一个超时设置的时间,如果丢失节点回来就可以立即恢复(默认为1分钟,通过 index.unassigned.node_left.delayed_timeout 设置)。如果该分片已经有写入,则通过translog进行增量同步数据。
- 否则将副本分配给其他节点,开始同步数据。
- 但如果该节点上的分片没有副本,则无法恢复,集群状态会变为red,表示可能要丢失该分片的数据了。
除了官方文档外,还有一些资料对于学习ES是有帮助的,这里列举如下:
- Elasticsearch in Action
- Elasticsearch可扩展的开源弹性搜索解决防范
- Mastering Elasticsearch 5.x - Third Edition
- 深入理解ElasticSearch
- Elasticsearch The Definitive Guide(中文Elasticsearch 权威指南),可以作为较全的参考,没有很好的组织结构
- https://www.jianshu.com/p/17ba43cbc3ee
Anatomy of an Elasticsearch Cluster: Part I
写给大忙人的Elasticsearch架构与概念(未完待续)的更多相关文章
- Unity3D架构设计NavMesh寻路(未完待续)
国庆闲来没事把NavMesh巩固一下.以Unity3D引擎为例写一个底层c# NavMesh寻路.由于Unity3D中本身自带的NavMesh寻路不能非常好的融入到游戏项目其中,所以重写一个NavMe ...
- ElasticSearch 基础概念学习(未完)
1.基本定义 摘自百度百科 elasticseaElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elastic ...
- python 面向对象的基本概念(未完待续)
面向对象编程简称OOP(Object-oriented-programming),是一种程序设计思想. 面向过程编程(如C语言)指一件事该怎么做,面向对象编程(如Java.python)指一件事该让谁 ...
- [Python]从哪里开始学习写代码(未完待续)
预警:这只是我在学习中的一点感受,可能并不完全准确,也不包括面向对象编程的思想(我还不太懂),也有水文的嫌疑,大佬请温和批评指正或者绕道. 计算机语言 语言,是用来交流的.计算机是不能直接听懂人的语言 ...
- Day4-内置函数--未完待续,慢慢写
内置函数:https://docs.python.org/3/library/functions.html?highlight=built#ascii 未完待续....
- 写给大忙人的spring cloud 1.x学习指南
这几天抽空搞了下spring cloud 1.x(2.0目前应该来说还不成熟),因为之前项目中使用dubbo以及自研的rpc框架,所以总体下来还是比较顺利,加上spring boot,不算笔记整理,三 ...
- 理解MySQL——架构与概念
写在前面:最早接触的MySQL是在三年前,那时候MySQL还是4.x版本,很多功能都不支持,比如,存储过程,视图,触发器,更别说分布式事务等复杂特性了.但从5.0(2005年10月)开始,MySQL渐 ...
- (二)ROS系统架构及概念 ROS Architecture and Concepts 以Kinetic为主更新 附课件PPT
第2章 ROS系统架构及概念 ROS Architecture and Concepts PPT说明: 正文用白色,命令或代码用黄色,右下角为对应中文译著页码. 这一章需要掌握ROS文件系统,运行图级 ...
- ROS机器人程序设计(原书第2版)补充资料 (贰) 第二章 ROS系统架构及概念
ROS机器人程序设计(原书第2版)补充资料 (贰) 第二章 ROS系统架构及概念 书中,大部分出现hydro的地方,直接替换为indigo或jade或kinetic,即可在对应版本中使用. 由于工作事 ...
随机推荐
- [LeetCode] 130. Surrounded Regions_Medium tag: DFS/BFS
Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'. A reg ...
- 解放你生产力的10款效率工具,赶紧mark起来
聪明的人总是善用工具 社会的快度发展赋予我们的一个幸运之处在于:你可以通过读书或使用工具去改善自身的处境甚至是阶层. 在父母那个时代,通过熟练使用办公软件,可以使一个在水泥地做帮工的青年走进格子间.而 ...
- 配置opensips经验总结
主要参考https://www.cnblogs.com/Forever-Kenlen-Ja/p/7741776.html (ubuntu),还有https://blog.csdn.net/sunyun ...
- idc函数大全
A80_addcA80_addcixA80_addciyA80_addixA80_addiyA80_cmpdA80_cmpiA80_im0A80_im1A80_im2A80_jrcA80_jrncA8 ...
- 40.SEO----前端该懂的seo技巧
SEO要点:1.语义化html标签,用合适的标签嵌套合适的内容,不可过分依赖div,对浏览器更友善就能更容易被抓取.2.重要的内容html代码放在前面,放在左边.搜索引擎爬虫是从左往右,从上到下进行抓 ...
- python+appium+PyCharm==自动化测试APP环境
1.点击SDK下面的uiautomatorviewer 2.启动夜神3.启动adb--->在cmd adb -version adb connect 127.0.0.1:62001 这里的配置环 ...
- node.js初识01
1.对于node.js的安装在这里就不做过多的介绍了,安装成功后,可以通过cmd 输入node -v查看node的版本号,如图所示 2.开始我们的hello world,通过cmd进入所属文件夹,输入 ...
- ****************VS编码操作实践******************
下面是今天主要练习的内容: 运用到的内容有 {运算符.强制转换.数据类型的运用.转义字符.变量与常量.基本类型的转换等} 1) 首先我们来看 下列的编码是由三大类组成的 ① 定制变量与常量 蓝色部 ...
- LeetCode160.相交链表
编写一个程序,找到两个单链表相交的起始节点. 例如,下面的两个链表: A: a1 → a2 ↘ c1 → c2 → c3 ↗ B: b1 → b2 → b3 在节点 c1 开始相交. 注意: 如果两个 ...
- Java基础(basis)-----异常与错误处理
1.编译型异常和运行时异常 编译时异常是指程序正确 而由外界条件不满足而产生的异常 java 中要求必须去捕捉住这类异常 不然无法通过编译 运行时异常是指程序存在着bug 如空指针异常 数 ...