当前位置: 首页 > news >正文

【ES问题总结】

文章目录

  • 1、什么是ElasticSearch;
  • 2、ElasticSearch的基本概念;
  • 3、什么是倒排索引;
  • 4、DocValue的作用;
  • 5、text和keyword类型的区别;
  • 7、query和filter的区别;
  • 8、es写数据的过程;
  • 9、es的更新和删除流程;
  • 10、es的搜索流程
  • 11、es在高并发下如何保证读写一致性;
  • 12、es如何选举master节点;
  • 13、es的分布式原理;
  • 14、es是如何避免脑裂现象;
  • 15、建立索引阶段性能提升方法;
  • 16、es的深度分页与滚动搜索scroll;

1、什么是ElasticSearch;

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

2、ElasticSearch的基本概念;

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口,数据的格式使用Json。
1、Index(索引)
ES将数据存储于一个或多个索引中,索引是具有类似特性的文档的集合。类比传统的关系型数据库领域来说,索引相当于SQL中的一个数据库,或者一个数据存储方案(schema)。索引由其名称(必须为全小写字符)进行标识,并通过引用此名称完成文档的创建、搜索、更新及删除操作。一个ES集群中可以按需创建任意数目的索引。
2、Type(类型)
类型是索引内部的逻辑分区(category/partition),然而其意义完全取决于用户需求。因此,一个索引内部可定义一个或多个类型(type)。一般来说,类型就是为那些拥有相同的域的文档做的预定义。例如,在索引中,可以定义一个用于存储用户数据的类型,一个存储日志数据的类型,以及一个存储评论数据的类型。类比传统的关系型数据库领域来说,类型相当于“表”。(Elastic 6.x 版只允许每个 Index 包含一个 Type,7.x 版将会彻底移除 Type)
3、Document(文档)
文档是Lucene索引和搜索的原子单位,它是包含了一个或多个域的容器,基于JSON格式进行表示。文档由一个或多个域组成,每个域拥有一个名字及一个或多个值,有多个值的域通常称为“多值域”。每个文档可以存储不同的域集,但同一类型下的文档至少应该有某种程度上的相似之处 。Index中的单条记录就称为Document,多个Document构成一个index。
4、Node(节点)
运行了单个实例的ES主机称为节点,它是集群的一个成员,可以存储数据、参与集群索引及搜索操作。类似于集群,节点靠其名称进行标识,默认为启动时自动生成的随机Marvel字符名称。用户可以按需要自定义任何希望使用的名称,但出于管理的目的,此名称应该尽可能有较好的识别性。节点通过为其配置的ES集群名称确定其所要加入的集群。
5、Cluster(集群)
一个集群是由一个或多个节点(服务器)组成的,通过所有的节点一起保存你的全部数据并且提供联合索引和搜索功能的节点集合。每个集群有一个唯一的名称标识,默认是“elasticsearch”。这个名称非常重要,因为一个节点(Node)只有设置了这个名称才能加入集群,成为集群的一部分。
6、Shards(分片)
我们在一个索引里存储的数据,潜在的情况下可能会超过单节点硬件的存储限制。例如,单个索引有上千万个文档需要占用1TB的硬盘存储空间,但是一台机器的硬盘可能没有这么大,或者是即便有这么大,但是单个节点在提供搜索服务时会响应缓慢。解决这个问题,Elasticsearch提供了分片的能力,它可以将你的索引细分成多个部分。当你创建一个索引的时候,你可以简单的定义你想要的分片的数量。每个分片本身是一个全功能的完全独立的“索引”,它可以部署在集群中的任何节点上。 创建索引时,用户可指定其分片的数量,默认数量为5个。
1)它允许你水平切分你的内容卷
2)它允许你通过分片来分布和并行化执行操作来应对日益增长的执行量
  Shard有两种类型:primary和replica,即主shard及副本shard。Primary shard用于文档存储,每个新的索引会自动创建5个Primary schemahard,当然此数量可在索引创建之前通过配置自行定义,不过,一旦创建完成,其Primary shard的数量将不可更改。Replica shard是Primary Shard的副本,用于冗余数据及提高搜索性能。每个Primary shard默认配置了一个Replica shard,但也可以配置多个,且其数量可动态更改。ES会根据需要自动增加或减少这些Replica shard的数量。ES集群可由多个节点组成,各Shard分布式地存储于这些节点上。
7、Replicas(复制)
在一个网络环境下或者是云环境下,故障可能会随时发生,有一个故障恢复机制是非常有用并且是高度推荐的,以防一个分片或节点不明原因下线,或者因为一些原因去除没有了。为了达到这个目的,Elasticsearch允许你制作分片的一个或多个拷贝放入一个叫做复制分片或短暂复制品中。
1)高可用。它提供了高可用来以防分片或节点宕机。为此,一个非常重要的注意点是绝对不要将一个分片的拷贝放在跟这个分片相同的机器上。
2)高并发。它允许你地分片可以提供超出自身吞吐量的搜索服务,搜索行为可以在分片所有的拷贝中并行执行。
每个索引可以被切分成多个分片,一个索引可以被复制零次(就是没有复制)或多次。一旦被复制,每个索引将会有一些主分片(就是那些最原始不是被复制出来的分片),还有一些复制分片(就是那些通过复制主分片得到的分片)。主分片和复制分片的数量可以在索引被创建时指定。索引被创建后,你可以随时动态修改复制分片的数量,但是不能修改主分片的数量。
8、Mapping(映射)
ES中,所有的文档在存储之前都要首先进行分析。用户可根据需要定义如何将文本分割成token、哪些token应该被过滤掉,以及哪些文本需要进行额外处理等等。另外,ES还提供了额外功能,例如将域中的内容按需排序。事实上,ES也能自动根据其值确定域的类型。

3、什么是倒排索引;

倒排表以字或词为关键字进行索引,表中关键字所对应的记录表项记录了出现这个字或词的所有文档,一个表项就是一个字表段,它记录该文档的ID和字符在该文档中出现的位置情况。

4、DocValue的作用;

倒排索引也是有缺陷的,假如我们需要对数据做一些聚合操作,比如排序/分组时,lucene内部会遍历提取所有出现在文档集合的排序字段,然后再次构建一个最终的排好序的文档集合list,这个步骤的过程全部维持在内存中操作,而且如果排序数据量巨大的话,非常容易就造成solr内存溢出和性能缓慢。
DocValues 就是 es 在构建倒排索引的同时,构建了正排索引,保存了docId到各个字段值的映射,可以看作是以文档为维度,从而实现根据指定字段进行排序和聚合的功能。
另外doc Values 保存在操作系统的磁盘中,当docValues大于节点的可用内存,ES可以从操作系统页缓存中加载或弹出,从而避免发生内存溢出的异常,docValues远小于节点的可用内存,操作系统自然将所有Doc Values存于内存中(堆外内存),有助于快速访问。

5、text和keyword类型的区别;

text类型(文本类型)会使用默认分词器分词,也就是存入的数据会先进行分词,然后将分完词的词组存入索引,当然你也可以为他指定特定的分词器。
text类型检索不是直接给出是否匹配,而是检索出相似度,并按照相似度由高到低返回结果。这样会导致本来我们认为应该查询出来的数据有可能会查询不到。
如果定义成keyword类型(关键字类型),那么默认就不会对其进行分词,原样存储。当一个字段需要按照精确值进行过滤、排序、聚合等操作时, 就应该使用keyword类型.
keyword类型检索,直接被存储为了二进制,检索时我们直接匹配,不匹配就返回false。所以精确匹配可以用keyword。
6、什么是停顿词过滤;

7、query和filter的区别;

filter的查询注重结果,也就是目标与查询条件是否匹配;而query的查询注重过程,也就是目标与查询条件的匹配度,匹配度越高的优先输出
1、因此filter的查询是没有评分的,而query的查询是有评分的
2、filter有相应的缓存机制,可以提高查询效率,并且因为filter不计算相关性评分,所以查询速度更快

8、es写数据的过程;

(1)ES 客户端选择一个节点 node 发送请求过去,这个节点就是协调节点 coordinating node 
(2)协调节点对 document 进行路由,通过 hash 算法计算出数据应该落在哪个分片 shard 上,然后根据节点上维护的 shard 信息,将请求转发到对应的实际处理节点node上
shard = hash(document_id) % (num_of_primary_shards),
(3)实际的节点上的 primary shard 主分片处理请求,然后将数据同步到副本节点 replica node 
(4)coordinating node 等到 primary node 和所有 replica node 都执行成功之后,就返回响应结果给客户端。

9、es的更新和删除流程;

(1)如果是删除操作,文档其实并没有真的被删除,而是在 .del 文件中被标记为 deleted 状态。该文档依然能匹配查询,但是会在结果中被过滤掉。
(2)如果是更新操作,就是将旧的 doc 标识为 deleted 状态,然后创建一个新的 doc。memory buffer 每 refresh 一次,就会产生一个 segment 文件 ,所以默认情况下是 1s 生成一个 segment 文件,这样下来 segment 文件会越来越多,此时会定期执行 merge。每次 merge 的时候,会将多个 segment 文件合并成一个,同时这里会将标识为 deleted 的 doc 给物理删除掉,不写入到新的 segment 中,然后将新的 segment 文件写入磁盘,这里会写一个 commit point ,标识所有新的 segment 文件,然后打开 segment 文件供搜索使用,同时删除旧的 segment 文件

10、es的搜索流程

1、query阶段;1. node1在这6个主副分片中随机选择3个分片,发送search request;2. 被选中的3个分片会分别执行查询并排序,返回from+size个文档id和排序值;3. node1整合3个分片返回的from + size个文档id,根据排序值排序后选取from到from+size的文档id;
2、fetch阶段;1. node1向相关的分片发送multi_get请求;2. 3个分片返回文档详细数据3. node1拼接返回的结果并返回给客户

11、es在高并发下如何保证读写一致性;

(1)对于更新操作:可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖
每个文档都有一个_version 版本号,这个版本号在文档被改变时加一。Elasticsearch使用这个 _version 保证所有修改都被正确排序。当一个旧版本出现在新版本之后,它会被简单的忽略。
利用_version的这一优点确保数据不会因为修改冲突而丢失。比如指定文档的version来做更改。如果那个版本号不是现在的,我们的请求就失败了。
(2)对于写操作,一致性级别支持 quorum/one/all
默认为 quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。
one:要求我们这个写操作,只要有一个primary shard是active活跃可用的,就可以执行
all:要求我们这个写操作,必须所有的primary shard和replica shard都是活跃的,才可以执行这个写操作
quorum:默认的值,要求所有的shard中,必须是大部分的shard都是活跃的,可用的,才可以执行这个写操作
(3)对于读操作,可以设置 replication 为 sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置replication 为 async 时,
也可以通过设置搜索请求参数_preference 为 primary 来查询主分片,确保文档是最新版本。

12、es如何选举master节点;

Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含Ping(节点之间通过这个RPC来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要ping通)这两部分;
确认候选主节点的最少投票通过数量,elasticsearch.yml 设置的值 discovery.zen.minimum_master_nodes;
对所有候选 master 的节点(node.master: true)根据 nodeId 字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第0位)节点,暂且认为它是master节点。
如果对某个节点的投票数达到阈值,并且该节点自己也选举自己,那这个节点就是master。否则重新选举一直到满足上述条件。

13、es的分布式原理;

Elasticsearch 会对存储的数据进行切分,将数据划分到不同的分片上,同时每一个分片会保存多个副本,主要是为了保证分布式环境的高可用。在 Elasticsearch 中,节点是对等的,节点间会选取集群的 Master,由 Master 会负责集群状态信息的改变,并同步给其他节点。
Elasticsearch 的性能会不会很低:只有建立索引和类型需要经过 Master,数据的写入有一个简单的 Routing 规则,可以路由到集群中的任意节点,所以数据写入压力是分散在整个集群的。

14、es是如何避免脑裂现象;

(1)当集群中 master 候选节点数量不小于3个时(node.master: true),可以通过设置最少投票通过数量(discovery.zen.minimum_master_nodes),设置超过所有候选节点一半以上来解决脑裂问题,即设置为 (N/2)+1;
(2)当集群 master 候选节点 只有两个时,这种情况是不合理的,最好把另外一个node.master改成false。如果我们不改节点设置,还是套上面的(N/2)+1公式,此时discovery.zen.minimum_master_nodes应该设置为2。这就出现一个问题,两个master备选节点,只要有一个挂,就选不出master了

15、建立索引阶段性能提升方法;

(1)使用 SSD 存储介质
(2)使用批量请求并调整其大小:每次批量数据 5–15 MB 大是个不错的起始点。
(3)如果你在做大批量导入,考虑通过设置 index.number_of_replicas: 0 关闭副本
(4)如果你的搜索结果不需要近实时的准确度,考虑把每个索引的 index.refresh_interval 改到30s
(5)段和合并:Elasticsearch 默认值是 20 MB/s。但如果用的是 SSD,可以考虑提高到 100–200 MB/s。如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。
(6)增加 index.translog.flush_threshold_size 设置,从默认的 512 MB 到更大一些的值,比如 1 GB

16、es的深度分页与滚动搜索scroll;

一种快照的查询形式,快照一旦形成,本次滚动查询内便无法查出来新增的那些数据,而且scroll是无法进行排序的,也无法指定from,那么我们想查看指定页码的数据就必须将该页数据之前的全部数据取出来再进行丢弃,所以scroll一般用于导出全量数据。
数据量小的情况使用最方便,灵活性好,实现简单
一种快照的查询形式,快照一旦形成,本次滚动查询内便无法查出来新增的那些数据,而且scroll是无法进行排序的,也无法指定from,那么我们想查看指定页码的数据就必须将该页数据之前的全部数据取出来再进行丢弃,所以scroll一般用于导出全量数据。
导出全量数据时性能最好

相关文章:

【ES问题总结】

文章目录 1、什么是ElasticSearch;2、ElasticSearch的基本概念;3、什么是倒排索引;4、DocValue的作用;5、text和keyword类型的区别;7、query和filter的区别;8、es写数据的过程;9、es的更新和删除流程&#…...

数据结构----结构--线性结构--顺序存储--数组

数据结构----结构–线性结构–顺序存储–数组 数组:类型相同,空间连续,长度固定 搜索: (1)基于索引搜索,时间复杂度O(1) (2)基于数值搜索: ​ 1.有序的&…...

docker 启动kitex 的opentelemetry

https://github.com/cloudwego/kitex-examples/blob/main/opentelemetry/docker-compose.yaml 下载两个yaml文件:docker-compose.yaml otel-collector-config.yaml 在该目录下执行 docker-compose up -d...

Excel中——日期列后添加星期

需求:在日期列中添加星期几? 第一步:打开需要添加星期的Excel文件,在日期后面添加日期 第二步:选择日期列,点击鼠标右键,在下拉列表中,选择“设置单元格格式” 第三步: 在…...

谈谈DNS是什么?它的作用以及工作流程

作者:Insist-- 个人主页:insist--个人主页 作者会持续更新网络知识和python基础知识,期待你的关注 目录 一、DNS是什么? 二、DNS的作用 三、DNS查询流程 1、查看浏览器缓存 2、查看系统缓存 3、查看路由器缓存 4、查看ISP …...

Qt小项目贪吃蛇实线,主要掌握定时器、信号与槽、按键事件、绘制事件、坐标运算、随机数生成等

Qt小项目贪吃蛇实线,主要掌握定时器、信号与槽、按键事件、绘制事件、坐标运算、随机数生成等 Qt 贪吃蛇演示QWidget 绘制界面项目源文件 注释清晰widget.hwidget.cpp 拓展QTimerQKeyEventQRectFQPointFQPainterQIcon Qt 贪吃蛇演示 QWidget 绘制界面 项目源文件 注…...

使用HTTP隧道时如何应对目标网站的反爬虫监测?

在进行网络抓取时,我们常常会遇到目标网站对反爬虫的监测和封禁。为了规避这些风险,使用代理IP成为一种常见的方法。然而,如何应对目标网站的反爬虫监测,既能保证数据的稳定性,又能确保抓取过程的安全性呢?…...

怎么样通过Bootstrap已经编译好(压缩好)的源码去查看符合阅读习惯的源码【通过Source Map(源映射)文件实现】

阅读本篇博文前,建议大家先看看下面这篇博文: https://blog.csdn.net/wenhao_ir/article/details/132089650 Bootstrap经编译(压缩)后的源码百度网盘下载地址: https://pan.baidu.com/s/14BM9gpC3K-LKxhyLGh4J9Q?pwdm02m Bootstrap未经编译…...

【排序算法】python之冒泡,选择,插入,快速,归并

参考资料: 《Python实现5大排序算法》《六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序》 --代码似乎是C语言 ———————— 本文介绍5种常见的排序算法和基于Python实现: 冒泡排序(Bubble Sort&am…...

UML—用例图的那些事

目录 背景: 1.用例图的发展史 过程: 1.用例图中的元素和关系 2.应用中的例子 总结: 背景: 1.用例图的发展史 用例图是一种常用的软件工程工具,用于描述系统的功能需求和用户与系统的交互。它在软件开发过程中起到了重要的作用,并且经历了…...

迷宫出口问题求解(DFS)

题面 一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由 nn 的格点组成,每个格点只有 22 种状态, 00 和 11,前者表示可以通行后者表示不能通行。 同时当Extense处在某个格点时,他只能移动到东南西北…...

基础算法模板

数据结构 单链表的插入删除 const int N=1e6+10; int head,e[N],ne[N],idx; //head 存储头节点的下标 //idx 存储当前已经用到的那个点 void init() {head=-1;idx=0; } void add_to_head(int x)//插入头节点操作 {e[idx]=x;ne[idx]=head;head=idx;idx++; } void add(int k)/…...

react Ref 的基本使用

类组件中使用ref 在类组件中,你可以使用createRef来创建一个ref,并将它附加到DOM元素或类组件实例上。使用ref允许你在类组件中访问和操作特定的DOM元素或类组件实例。 下面是在类组件中使用ref的步骤: 引入React和createRef: …...

宝塔面板点击SSL闪退打不开怎么解决?

宝塔Linux面板点击SSL证书闪退如何解决?旧版本的宝塔Linux面板确实存在这种情况,如何解决?升级你的宝塔Linux面板即可。新手站长分享宝塔面板SSL闪退的解决方法: 宝塔面板点击SSL证书闪退解决方法 问题:宝塔Linux面板…...

如何将安卓 Gradle 模块打包发布到本地 Maven 仓库

文章目录 具体流程 笔者的运行环境: Android Studio Flamingo | 2022.2.1 Android SDK 33 Gradle 8.0.1 JDK 17 Android 的 Gradle 项目与一般的 Gradle 项目是不同的,因此对将 Gradle 模块打包发布到本地 Maven 仓库来说,对普通 Gradle …...

【Docker】Docker比虚拟机快的原因、ubuntu容器、镜像的分层概念和私有库的详细讲解

🚀欢迎来到本文🚀 🍉个人简介:陈童学哦,目前学习C/C、算法、Python、Java等方向,一个正在慢慢前行的普通人。 🏀系列专栏:陈童学的日记 💡其他专栏:CSTL&…...

java.lang.IllegalArgumentException: Invalid character found in methodname

postman请求异常:java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens...

【PCB专题】Allegro高速电路Xnet网络等长约束——SDIO信号为例

高速PCB板布线过程中,经常遇到等长设置问题,例如DDR的一组数据线和地址线等。但是由于数据线和地址线中间有一个电阻(或排阻),这种情况下设置等长就要引入Xnet的概念,通过设置Xnet的等长来确保数据线和地址线的等长。 由无源、分立器件(电阻、电容、电感)连接起来的几段…...

leetcode每日一练-第278题-第一个错误的版本

一、思路 二分查找——因为它可以快速地将版本范围缩小一半,从而更快地找到第一个坏版本。 二、解题方法 维护一个左边界 left 和一个右边界 right,在每一步循环中,我们计算中间版本 mid,然后检查它是否是坏版本。如果是坏版本…...

最小生成树笔记(Prim算法Kruskal算法)

1.最小生成树 最小生成树(Minimum Spanning Tree,简称MST)是指:在一个连通无向图中,找到一个包含所有顶点的树,且该树的所有边的权重之和最小。 换句话说,最小生成树是原图中的一个子图&#…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 ​ 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...