面试集中营—ElasticSearch架构篇

一、为什么用ElasticSearch?

       1、支持多种数据类型。它可以处理非结构化、数值和地理信息等多种类型的数据;

       2、简单的RESTful API。ES提供了一个简单易用的RESTful API,使得它可以从任何编程语言中调用,降低了学习的曲线。

       3、近实时搜索。ES每隔1秒将数据存储至系统缓存中,使用倒排索引提高检索效率,使得搜索数据变得快速且高效。

       4、支持相关性搜索。它可以根据条件对搜索结果进行打分,提供了基于文档的全文检索能力。

       5、天然分布式存储。ES是分布式的,使用分片支持处理PB级的数据量,易于扩展,可部署在数百台服务器的集群中。

      6、降低全文检索的学习曲线。它可以被任何编程语言调用,使得开发变得更加容易。

      7、高可用性。由于其分布式特性,ES可以通过添加更多节点来分担负载,增加可靠性,无需对应用进行任何改动。

二、ES基本概念名词

Cluster

       代表一个集群,集群中有多个节点,其中有一个为主节点,这个主节点是可以通过选举产生的,主从节点是对于集群内部来说的。ES的一个概念就是去中心化,字面上理解就是无中心节点,这是对于集群外部来说的,因为从外部来看es集群,在逻辑上是个整体,你与任何一个节点的通信和与整个es集群通信是等价的。

Shards

        代表索引分片,es可以把一个完整的索引分成多个分片,这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上。构成分布式搜索。分片的数量只能在索引创建前指定,并且索引创建后不能更改

replicas

        代表索引副本,es可以设置多个索引的副本。

        副本的作用:

        一是提高系统的容错性,当某个节点某个分片损坏或丢失时可以从副本中恢复。

        二是提高es的查询效率,es会自动对搜索请求进行负载均衡。

Recovery

        代表数据恢复或叫数据重新分布,es在有节点加入或退出时会根据机器的负载对索引分片进行重新分配;挂掉的节点重新启动时也会进行数据恢复。

三、ES基础架构

     角色

       五大角色:

       Master Node

        主节点,该节点不和应用创建连接,每个节点都保存了集群状态,master节点控制整个集群的元数据。只有master节点可以修改节点状态信息及元数据的处理,比如索引的新增、删除、分片路由分配、所有索引和相关Mapping、Setting配置等等。

       Master eligible nodes

       合格主节点。合格节点,每个节点部署后不修改配置信息,默认就是一个 eligible 节点。有资格成为Master节点但暂时并不是Master的节点被称为 eligible 节点,该节点可以参加选主流程,成为Mastere节点。该节点只是与集群保持心跳,判断Master是否存活,如果Master故障则参加新一轮的Master选举。

       Data Node

        数据节点,用于简历文档索引,接受应用创建连接,接受索引请求,接受用户的搜索请求。是真实春初数据的节点,正常情况下节点数量越多,集群的性能就越强大

       Coordinating Node

        协调节点(/路由节点/client节点) 协调节点,该节点专用与接收应用的查询连接、接受搜索请求,但其本身不负责存储数据。

       协调节点接受客户端搜索请求后将请求转发到与查询条件相关的多个data节点的分片上,然后多个data节点的分片执行查询语句或者查询结果再返回给协调节点,协调节点把各个data节点的返回结果进行整合、排序等一系列操作后再将最终结果返回给用户请求。

        搜索请求在两个阶段中执行(query 和 fetch),这两个阶段由接收客户端请求的节点 - 协调节点协调。在请求query 阶段,协调节点将请求转发到保存数据的数据节点。 每个数据节点在本地执行请求并将其结果返回给协调节点。在收集fetch阶段,协调节点将每个数据节点的结果汇集为单个全局结果集。

       Ingest Node

        ingest 节点可以看作是数据前置处理转换的节点,支持 pipeline管道设置,可以使用 ingest 对数据进行过滤、转换等操作,类似于 logstash 中 filter 的作用,功能相当强大。

        Ingest节点处理时机:在数据被索引之前,通过预定义好的处理管道对数据进行预处理。默认情况下,所有节点都启用Ingest,因此任何节点都可以处理Ingest任务。

        ingest的详细可以参考elasticsearch高可用 原理 (图解+秒懂+史上最全)-CSDN博客

        存储结构

           Elasticsearch -> Index-> Types -> Documents -> Fields

           1、ES的index代表Mysql中的数据库

           2、ES的types代表Mysql中的Tables,新版本中弱化types概念

           3、ES的Document代表Mysql中的行

           4、ES的Fields代表Mysql中的列

           5、ES的mapping代表Mysql中的表结构,

         由于Elasticsearch底层使用了lucene的原因,不支持对mapping的修改,可使用索引重建的方式。而且不能更改类型,为什么不能修改一个字段的type?原因是一个字段的类型修改以后,那么该字段的所有数据都需要重新索引。Elasticsearch底层使用的是lucene库,字段类型修改以后索引和搜索要涉及分词方式等操作,不允许修改类型在我看来是符合lucene机制的。

          存储就是集群,分片,副本。如下图所示

      Lucene

        Lucene是使用Java语言开发的开源的,高性能的查询库。Apache Solr,Apache Nutch,OpenSearch和Elasticsearch都是在Lucene的基础上创建的。Lucene已经有超过20年的历史,是Apache基金会管理的成熟项目。

        Lucene的核心是倒排索引(inverted search index),这是Lucene有快速查询能力的核心。倒排索引提供了关键词与包含关键词的文档的对应关系。在查询的过程中,从排序的列表中快速找到关键词并且找到关键词对应的文档列表。Lucene支持的信息类型包含数字、字符串以及文本类型。Lucene包含丰富的搜索接口,支持自然语言搜索,通配符搜索,模糊搜索和邻近搜索。

      倒排索引

        所谓倒排索引,就是相对于正排索引,正排索引典型例子就是mysql的B+树主键索引,一个索引字段对应一列数据,key是主键ID,value是内容。而倒排索引,key是内容,value是关联的主键列表。如下图所示。

      倒排索引有什么优点呢?

       1、快速查找包含特定单词的文档,这使得它特别适合用于搜索引擎和文本搜索应用。

       2、倒排索引可以快速查找文档集合中包含多个单词的文档,这在文本分类和关键词提取中也很有用、

       缺点:

       1、额外的存储空间来存储索引;

       2、倒排索引在建立和更新时需要一定的时间;

     Lucene中的倒排索引

        lucene就是影响最为广泛的一款倒排索引及搜索工具,lucene把关键字按照字典顺序的存储在磁盘上,这样可以充分的发挥二分查找的优势,加快查找的速度。

      存储内容      

term indexterm dictionaryPosting List
小米1,2...4
华为1,4,6
手机1,2,3...6

        FST

       新增一条数据的过程

        1、分词

        lucene使用字典文件记录所有的关键字,每个关键字不会重复。这个关键字是通过分词来完成的,如果是英文很简单就是按照英文单词来分割,但是中文就需要一个中文的分词器。

        2、分析整理

        下一步就是对分割出来的词进行分析和整理,比如时态统一,去除不需要的词比如“是”,“的”等等

       3、建立对应关系

      最后就是把关键词和对应的文件id关联上,一个关键词可以关联多个文件id。

      对索引的压缩算法

       针对海量的数据,会产生大量的索引文件,如果不对索引进行压缩,可以想见光索引文件就需要大量的硬件资源,同时对于索引的检索也会更加费时。此时压缩算法就非常必要了。

       1、大量的对数字的压缩

       索引中的Posting List不管主键Id是什么数据类型的,统一都是int类型,最大能够表示的正整数是2的31次方减1,当id很大的时候,比如存储两个连续的大整数,比如[1000000000,1000000001]这两个数字压缩之后可能会成为[100000000,1]类似的情况,后面的1表示和前一个数字的差值。因为 Posting List的id不一定是连续的但是肯定是有序的。所以使用差值列表来存储[Posting List]。

      2、关键词压缩

      比如有很多关键词都有相同的文字,比如中国,中国人,中国话,都有中国,那么就会把中国作为一个编码,变成中国,<1,人>,<1,话>

四、Elasticsearch数据备份与恢复

快照和还原机制

        Snapshot: 快照,是Elasticsearch中用于备份数据的核心概念。Snapshot是一个时间点上的数据的完整拷贝,可以用于恢复数据或迁移到其他集群。
        Restore: 还原,是从Snapshot中恢复数据的过程。Restore可以用于恢复单个索引或整个集群。 

        创建快照:curl -X PUT "http://localhost:9200/_snapshot/my_snapshot/snapshot_1?wait_for_completion=true" -H 'Content-Type: application/json' -d' { "indices": "my_index", "ignore_unavailable": true, "include_global_state": false }' 
        恢复快照:curl -X POST "http://localhost:9200/_snapshot/my_snapshot/snapshot_1/_restore" -H 'Content-Type: application/json' -d' { "indices": "my_index", "ignore_unavailable": true }' 或者使用elasticsearch-snapshot工具恢复快照: bin/elasticsearch-snapshot restore my_snapshot snapshot_1

分页查询与插入

        可以使用scroll分页查询的方式,按照时间周期,从源ES中导入到目标ES中。代码如下:

// 设定滚动时间间隔
        final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
        SearchRequest searchRequest = new SearchRequest(formIndices);
        searchRequest.scroll(scroll);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 设定每次返回多少条数据
        int number = size + new Random().nextInt(size / 2);
        searchSourceBuilder.size(number);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse;
        try {
            CountRequest countRequest = new CountRequest(formIndices);
            CountResponse countResponse = client.count(countRequest, getNewOptions());
            total = countResponse.getCount();
            searchResponse = client.search(searchRequest, getNewOptions());
        } catch (IOException e) {
            LOGGER.error("formIndices:" + formIndices, e);
            return;
        }
        count = count + searchResponse.getHits().getHits().length;
        String scrollId = searchResponse.getScrollId();
        SearchHit[] searchHits = searchResponse.getHits().getHits();
        LOGGER.info("-----首页-----" + System.currentTimeMillis());
        request = new BulkRequest();
        for (SearchHit documentFields : searchHits) {
            Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
            String timestamp = (String) sourceAsMap.get("@timestamp");
            // 过滤path
            String path = (String) sourceAsMap.get("path");
            if(checkDiscardPath(path)){
                continue;
            }
            if (!StringUtils.isEmpty(timestamp)) {
                try {
                    sourceAsMap.put("@timestamp", toIndices + timestamp.substring(10));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            Map<String, Object> httpMap = (Map<String, Object>) sourceAsMap.get("http");
            if (httpMap != null && httpMap.size() > 0) {
                Map<String, Object> requestMap = (Map<String, Object>) httpMap.get("request");
                if (requestMap != null && requestMap.size() > 0) {
                    requestMap.remove("body");
                    requestMap.remove("params");
                    httpMap.put("request", requestMap);
                }
                Map<String, Object> responseMap = (Map<String, Object>) httpMap.get("response");
                if (responseMap != null && responseMap.size() > 0) {
                    responseMap.remove("body");
                    httpMap.put("response", responseMap);
                }
                sourceAsMap.put("http", httpMap);
            }
            indexRequest = new IndexRequest("ai-cloud-gateway-prod-packet-" + toIndices)
                    .source(sourceAsMap);
            request.add(indexRequest);
        }
        // 写
        BulkResponse bulk = clientNew.bulk(request, getNewOptions());
        LOGGER.info(bulk.status().getStatus() + ":" + System.currentTimeMillis());
        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
        // 遍历搜索命中的数据,直到没有数据
        while (searchHits.length > 0) {
            try {
                scrollRequest.scroll(scroll);
                try {
                    searchResponse = client.scroll(scrollRequest, getNewOptions());
                } catch (IOException e) {
                    e.printStackTrace();
                    errorTimes++;
                    continue;
                }
                scrollId = searchResponse.getScrollId();
                searchHits = searchResponse.getHits().getHits();
                if (searchHits.length == 0) {
                    LOGGER.info(" searchHits is null -----  end");
                    break;
                } else {
                    LOGGER.info(" searchHits is  not null ----- " + searchHits.length);
                }
                count = count + searchHits.length;
                LOGGER.info("-----下一页-----");
                request = new BulkRequest();

                LOGGER.info("-----过滤前----- {}",searchHits.length);
                SearchHit[] finalSearchHits = checkDiscardHits(searchHits);
                LOGGER.info("-----过滤后----- {}",finalSearchHits.length);
                IndexRequest[] requests = new IndexRequest[finalSearchHits.length];
                CountDownLatch countDownLatch = new CountDownLatch(finalSearchHits.length);
                try {
                    for (int i = 0; i < finalSearchHits.length; i++) {
                        int finalI = i;
                        // 清除 request  response 中的body 减少数据量
                        executorServiceTwo.execute(() -> {
                            try {
                                Map<String, Object> sourceAsMap = finalSearchHits[finalI].getSourceAsMap();
                                String timestamp = (String) sourceAsMap.get("@timestamp");
                                sourceAsMap.put("@timestamp", toIndices + timestamp.substring(10));
                                Map<String, Object> httpMap = (Map<String, Object>) sourceAsMap.get("http");
                                if (httpMap != null && httpMap.size() > 0) {
                                    Map<String, Object> requestMap = (Map<String, Object>) httpMap.get("request");
                                    if (requestMap != null && requestMap.size() > 0) {
                                        requestMap.remove("body");
                                        requestMap.remove("params");
                                        httpMap.put("request", requestMap);
                                    }
                                    Map<String, Object> responseMap = (Map<String, Object>) httpMap.get("response");
                                    if (responseMap != null && responseMap.size() > 0) {
                                        responseMap.remove("body");
                                        httpMap.put("response", responseMap);
                                    }
                                    sourceAsMap.put("http", httpMap);
                                }
                                requests[finalI] = new IndexRequest("ai-cloud-gateway-prod-packet-" + toIndices)
                                        .source(sourceAsMap);
                                countDownLatch.countDown();
                            } catch (Exception e) {
                                LOGGER.error("", e);
                            }
                        });
                    }
                } catch (Exception e) {
                    LOGGER.error("", e);
                    Map<String, Object> sourceAsMap;
                    for (SearchHit documentFields : searchHits) {
                        sourceAsMap = documentFields.getSourceAsMap();
                        // 过滤path
                        String path = (String) sourceAsMap.get("path");
                        if(checkDiscardPath(path)){
                            continue;
                        }
                        String timestamp = (String) sourceAsMap.get("@timestamp");
                        if (!StringUtils.isEmpty(timestamp)) {
                            try {
                                sourceAsMap.put("@timestamp", toIndices + timestamp.substring(10));
                            } catch (Exception ee) {
                                LOGGER.error("", ee);
                            }
                        }
                        Map<String, Object> httpMap = (Map<String, Object>) sourceAsMap.get("http");
                        if (httpMap != null && httpMap.size() > 0) {
                            Map<String, Object> requestMap = (Map<String, Object>) httpMap.get("request");
                            if (requestMap != null && requestMap.size() > 0) {
                                requestMap.remove("body");
                                requestMap.remove("params");
                                httpMap.put("request", requestMap);
                            }
                            Map<String, Object> responseMap = (Map<String, Object>) httpMap.get("response");
                            if (responseMap != null && responseMap.size() > 0) {
                                responseMap.remove("body");
                                httpMap.put("response", responseMap);
                            }
                            sourceAsMap.put("http", httpMap);
                        }
                        indexRequest = new IndexRequest("ai-cloud-gateway-prod-packet-" + toIndices)
                                .source(sourceAsMap);
                        request.add(indexRequest);
                        countDownLatch.countDown();
                    }
                }

                try {
                    countDownLatch.await(60, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    LOGGER.warn("InterruptedException", e);
                }
                LOGGER.info("request {}",requests.length);
                request.add(requests);
                // 写
                LOGGER.info("begin writing to " + toIndices + ",from" + formIndices);
                bulk = clientNew.bulk(request, getNewOptions());
                LOGGER.info(bulk.status().getStatus() + ":" + System.currentTimeMillis());

                if (count >= total - 5000) {
                    break;
                }
                Thread.sleep(100);
            } catch (Exception e) {
                ......
            }
        }

  完整代码:GitHub - EricLoveMia/elasticsearch-import

五、性能调优

1、不要返回数据量非常大的结果集

2、避免出现大文档,即单条索引记录的体积不要过大。

3、有条件的话尽量使用大的内存,SSD硬盘,分配给ES的内存为最大内存的50%;

4、尽量批请求bulk;

5、大量写入的情况下,增加索引刷新时间大小index.refresh_interval,默认1s刷新一次,设置为-1表示关闭索引刷新;

6、初始加载数据时禁用副本,将index.number_of_replicas 设置为0;

7、尽量避免使用深度分页,实在不能避免可以采用Scroll 遍历查询或Search After 查询;

8、Data too large 的报错。下图可以看到有两条界限:驱逐线 和 断路器。当缓存数据到达驱逐线时,会自动驱逐掉部分数据,把缓存保持在安全的范围内。当用户准备执行某个查询操作时,断路器就起作用了,缓存数据+当前查询需要缓存的数据量到达断路器限制时,会返回Data too large错误,阻止用户进行这个查询操作。

        此时就要根据情况来修改一些参数的值。

        indices.fielddata.cache.size :配置fieldData的Cache大小,可以配百分比也可以配一个准确的数值。cache到达约定的内存大小时会自动清理,驱逐一部分FieldData数据以便容纳新数据。默认值为unbounded无限。
        indices.fielddata.cache.expire:用于约定多久没有访问到的数据会被驱逐,默认值为-1,即无限。expire配置不推荐使用,按时间驱逐数据会大量消耗性能。而且这个设置在不久之后的版本中将会废弃。

        indices.breaker.fielddata.limit:这个 fielddata 断路器限制fielddata的大小,默认情况下为堆大小的60%。
        indices.breaker.request.limit:这个 request 断路器估算完成查询的其他部分要求的结构的大小, 默认情况下限制它们到堆大小的40%。
        indices.breaker.total.limit:这个 total 断路器封装了 request 和 fielddata 断路器去确保默认情况下这2个部分使用的总内存不超过堆大小的70%。

参考:

elasticsearch高可用 原理 (图解+秒懂+史上最全)-CSDN博客

elasticsearch常识:存储结构、优化_es的存储结构-CSDN博客

Elasticsearch数据备份与恢复-CSDN博客

ES性能调优详解-CSDN博客

ElasticSearch:从[FIELDDATA]Data too large错误看FieldData配置_indices.fielddata.cache.size-CSDN博客ES 性能调优,这可能是全网最详细的 Elasticsearch 性能调优指南_es性能优化-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/575391.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

德语口语学习的8种练习方法

简洁明了一点&#xff0c;方便大家理解&#xff0c;我总结了以下8点&#xff1a; 1.模拟对话&#xff1a; 创造实际生活场景&#xff0c;例如购物、问路、餐厅点餐等&#xff0c;并自言自语或者与伙伴一起模拟这些对话。 参加角色扮演活动&#xff0c;通过不同情境练习口语。…

Camtasia2024破解版激活许可证秘钥永久免费使用

Camtasia Studio是一款专业的屏幕录像和视频编辑软件套装&#xff0c;它提供了从屏幕录制到视频编辑、菜单制作、视频播放等一系列功能。以下是对Camtasia Studio及其2024年最新版本的详细介绍。 一、Camtasia Studio概述 Camtasia Studio是一款集屏幕录制、视频剪辑、菜单制…

java:SpringBoot入门

Spring 提供若干子项目,每个项目用于完成特定功能 Spring Boot 可以简化配置并且快速开发 SpringBootWeb快速入门 创建Springboot模块并使用Springweb依赖 在类上添加注解 RestController可以将字符串自动转成json返回数据给页面 再在方法上添加注解 RequestMapping(&…

深入理解GTK、Qt、AWTK:跨平台GUI框架对比

目录标题 GTK特性&#xff1a;优点&#xff1a;缺点&#xff1a; Qt特性&#xff1a;优点&#xff1a;缺点&#xff1a; AWTK特性&#xff1a;优点&#xff1a;缺点&#xff1a; 适用场景 在当今的软件开发领域&#xff0c;图形用户界面&#xff08;GUI&#xff09;的开发是不可…

Log4j日志框架多种日志级别

Log4j日志框架定义了多种日志级别&#xff0c;这些级别按照优先级从高到低排列如下&#xff1a; OFF&#xff1a;这是最高等级的日志级别&#xff0c;用于关闭所有日志记录。FATAL&#xff1a;指出每个严重的错误事件将会导致应用程序的退出。ERROR&#xff1a;表明发生错误事…

python之excel加工处理小案例一则

一、工具用途 工作中&#xff0c;需要对各类excel进行加工处理&#xff0c;当表和字段比较多时&#xff0c;关联条件又有多个&#xff0c;每次通过execl的vlookup之类的关联公式手工可以解决工作需求&#xff0c;但一般耗时较长&#xff0c;且人工统计匹配也存在出错的情况。 …

官方售价299元的自媒体博客资讯类wordpress主题

官方售价299元的自媒体博客资讯类wordpress主题。 自媒体一号是一款由主题巴巴团队原创设计开发的WordPress主题&#xff0c;这款主题页面布局简约大气&#xff0c;设计细节精美考究&#xff0c;内置功能非常强大&#xff0c;通过后台的主题设置面板&#xff0c;你可以轻松自定…

kaggle不显示中文字体

链接&#xff1a;【kaggle】在matplotlib中使用中文字体_kaggle使用中文字体打印图片-CSDN博客 下载字体链接 完整代码&#xff1a; import matplotlib.pyplot as plt import matplotlib.font_manager as font_managerdef plot_df(df, x, y, title"", xlabelDate,…

【Qt常用控件】—— 多元素控件

目录 1.1 List Widget 1.2 Table Widget 1.3 Tree Widget 1.4 小结 Qt 中提供的多元素控件有: QListWidget QListView QTableWidget QTableView QTreeWidget QTreeView xxWidget 和 xxView 之间的区别 以 QTableWidget 和 QTableView 为例&#xff1a; QTableView 是基于…

快速构建Spring boot项目

1、Idea里新建项目 2、创建HelloController 3、运行 4、开发环境热部署 pom.xml 查看目前已有的依赖 配置properties 设置 ctrlshiftalt/ 新版本的compiler.automake.allow.when.app.running已经不在registry里面了&#xff0c;在settings里面的Advanced settings里面Allow au…

【R语言简介】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

【LLVM】在Windows中配置LLVM开发环境

本文内容 0.动机1. 安装Visual Studio 20192 安装Python3已安装Python未安装Python 3. 配置Visual Studio4.安装LLVM获取源码将LLVM切换到我们的目标版本编译LLVM 0.动机 想着ubuntu用着别手&#xff0c;看看能不能在Windows中进行开发。于是就动手实践了下 Getting Started w…

Android Studio实现内容丰富的安卓养老平台

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 158安卓养老 1.开发环境 后端用springboot框架&#xff0c;安卓的用android studio开发android stuido3.6 jak1.8 idea mysql tomcat 2.功能介绍 安卓端&#xff1a; 1.注册登…

Mockaroo - 在线生成测试用例利器

简介&#xff1a;Mockaroo 是一个无需安装的在线工具&#xff0c;用于生成大量的自定义测试数据。它支持多种数据格式&#xff0c;如JSON、CSV、SQL和Excel&#xff0c;并能模拟复杂的数据结构。 历史攻略&#xff1a; 测试用例&#xff1a;多条件下编写&#xff0c;懒人妙用…

How to solve matplotlib Chinese garbled characters in Ubuntu 22.04

conda create -n huizhou python3.8conda activate huizhouconda install numpy matplotlibpip install mplfontsmplfonts init# 导入必要的库 import numpy as np import matplotlib.pyplot as plt# 创建角度数组&#xff0c;从0到2π x np.linspace(0, 2 * np.pi, 100)# 计算…

微信小程序-------模板与配置

能够使用 WXML 模板语法渲染页面结构能够使用 WXSS 样式美化页面结构能够使用 app.json 对小程序进行全局性配置能够使用 page.json 对小程序页面进行个性化配置能够知道如何发起网络数据请求 一.WXML 模板语法 数据绑定 1. 数据绑定的基本原则 ① 在 data 中定义数据 ② 在…

架构师系列-消息中间件(11)- RocketMQ 进阶(5)-深入分析(2)

3. 文件刷盘机制 RocketMQ 的消息是存储在磁盘上的&#xff0c;这样做有两个优点&#xff1a; 保证断电后恢复 让存储的消息量超出内存的限制 RocketMQ 存储与读写是基于 JDK NIO 的内存映射机制&#xff0c;具体使用 MappedByteBuffer&#xff08;基于 MappedByteBuffer 操…

通配符HTTPS安全证书

众多类型的SSL证书&#xff0c;要说适用或者说省钱肯定是通配符了&#xff0c;因为谁都想一本SSL证书包括了整条域名&#xff0c;而且也不用一条一条单独管理。 通配符HTTPS安全证书&#xff0c;其实就是通配符SSL证书&#xff0c;SSL证书主流CA的参数都一样&#xff0c;通配符…

python中如何用matplotlib写雷达图

#代码 import numpy as np # import matplotlib as plt # from matplotlib import pyplot as plt import matplotlib.pyplot as pltplt.rcParams[font.sans-serif].insert(0, SimHei) plt.rcParams[axes.unicode_minus] Falselabels np.array([速度, 力量, 经验, 防守, 发球…

迅雷不限速破解方法

背景&#xff1a;现在迅雷和百度云的下载速度真的太恶心了&#xff0c;所以总有大佬可以采用厉害的方法进行破解&#xff0c;在网上看了一圈&#xff0c;很多都是骗人或者是无效的&#xff0c;找了一个靠谱的方法&#xff0c;亲测速度能达到10M以上&#xff0c;非常给力。 以下…