面试集中营—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");// 过滤pathString 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();// 过滤pathString 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博客

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

12G-SDI视频分配器JR104D-4K-SDI

JR104D-4K-SDI 12G-SDI分配器1分4,12G-SDI分配器1分2,12G-SDI分配器1分8,机架式12G-SDI分配器1分4&#xff0c;12G-SDI分配器4组1分4&#xff0c;12G-SDI分配器16组1分4&#xff0c; 广播级指标生产厂家。 一、产品介绍&#xff1a; JR104D-4K-SDI视频分配器&#xff0c;是按…

Pytest用例自定义 - 重复、并行、串行

简介&#xff1a;面对快速迭代和持续交付的需求&#xff0c;提高测试效率变得至关重要。并行测试因其显著的时间节省优势而备受青睐。然而&#xff0c;并非所有测试都适合并行执行。在某些情况下&#xff0c;串行执行是必要的&#xff0c;以确保测试的正确性和稳定性。本文将探…

【AI-GitHub】SceneScript 3D场景主体识别分割!让AR和人工智能设备了解物理空间的几何形状!

SceneScript是由Meta Reality Labs Research开发的一种新型3D场景重建技术。SceneScript采用语言模型来生成和表达物理空间的布局&#xff0c;能够通过机器学习直接推断出房间的几何形状&#xff0c;从而提供一种全新的环境重建和表现方式。想象一下&#xff0c;有这样一副时尚…

spring boot中一般如何使用线程池

在Spring Boot中&#xff0c;线程池作为并发编程的核心工具&#xff0c;对于提升应用程序性能、优化资源利用和保证系统稳定性具有重要作用。本文将详细阐述如何在Spring Boot中正确使用线程池&#xff0c;包括配置参数、实例化、任务提交、监控及常见问题处理等环节&#xff0…

HarmonyOS 鸿蒙下载三方依赖 ohpm环境搭建

前言 ohpm&#xff08;One Hundred Percent Mermaid &#xff09;是一个集成了Mermaid的命令工具&#xff0c;可以用于生成关系图、序列图、等各种图表。我们可以使用ohpm来生成漂亮且可读性强的图表。 本期教大家如何搭建ophm环境&#xff1a; 一、在DevEco Studio中&#…

比特币中用到的密码学功能【区块链学习笔记1】

想要转到web3&#xff0c;这次学习区块链看的课是北大肖臻老师的公开课。 比特币又称是加密货币crypto-currency&#xff0c;但其实是不加密的&#xff0c;全是公开的。比特币中用到的密码学中HASH和签名。 1. HASH 密码学中的哈希&#xff1a;cryptographic hash function …

【NLP练习】seq2seq

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、总结&#xff1a;seq2seq详解 1. seq2seq是什么 seq2seq&#xff08;sequence to sequence&#xff09;是一种常见的NLP模型架构&#xff0c;即&#xf…

c++图论基础(2)

目录 图的存储方式&#xff1a; 邻接矩阵&#xff1a; 代码实现&#xff1a; 邻接表&#xff1a; 代码实现&#xff1a; 邻接矩阵邻接表对比&#xff1a; 带权图&#xff1a; 邻接矩阵存储&#xff1a; 邻接表存储(代码实现)&#xff1a; 图的存储方式&#xff1a; 邻…

【数据结构】合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 Definition for singly-linked list.struct ListNode {int val;struct ListNode *next;};typedef struct ListNode ListNode; struct ListNode* mergeTwoLists(struct Lis…

小米消金强化普惠金融举措,构建消费者反诈安全屏障

近年来&#xff0c;随着通信技术的飞速发展&#xff0c;电信网络诈骗作为一种非接触性犯罪形式&#xff0c;给广大消费者带来了严重的经济损失&#xff0c;并对金融秩序造成了极大的破坏。为了应对这一严峻挑战&#xff0c;重庆小米消费金融有限公司&#xff08;以下简称“小米…

【网络安全】对称加密、非对称加密以及密钥分配

目录 1、对称加密 2、非对称加密 3、如何分配对称密钥&#xff1f; 4、如何分配非对称密钥&#xff1f; 1、对称加密 所谓对称加密&#xff0c;就是指加密密钥与解密密钥都使用相同的密钥。如下图所示&#xff0c;通信双方使用的就是对称加密密钥。//代表&#xff1a;DES和…

代码随想录算法训练营第三十七天| LeetCode738.单调递增的数字

LeetCode 738 单调递增的数字 题目链接&#xff1a;738. 单调递增的数字 - 力扣&#xff08;LeetCode&#xff09; 【解题思路】 需要采用从后往前的遍历 如果发现该数字的前一位比后一位大&#xff0c;那么前一位就做-1处理&#xff0c;后一位变为9 注意&#xff1a;千万不…

《DiffusionNER: Boundary Diffusion for Named Entity Recognition》

Submitted 22 May, 2023; originally announced May 2023. Comments: Accepted to ACL 2023, submission version https://github.com/tricktreat/DiffusionNER 在这里插入图片描述 问题&#xff1a; 命名实体识别任务中存在的噪声跨度&#xff08;边界不清晰&#xff09…

人工智能如何提高公司效率的 5 种方法

人工智能是当今最热门的话题之一&#xff0c;但并不是每个人都了解其对商业的价值规模。由此可见&#xff0c;现有的AI技术可以将企业的生产力提升40%。 在机器学习的帮助下&#xff0c;Netflix 利用自动化个性化推荐每年赚取 10 亿美元。当公司使用人工智能时&#xff0c;34%…

【QA】Git的底层原理

前言 本文通过一个简单的示例&#xff0c;来理解Git的底层原理。 示例 1、新建本地仓库并上传第一个文件 相关步骤&#xff1a; 新建仓库及创建文件查看文件状态将文件添加到暂存区将文件提交到本地仓库 HMTeenLAPTOP-46U4TV6K MINGW64 /d/GSF_Data/Github/Java/Git/git-…

文件下载的N种方式

文件下载有多种方式可以实现。下面是其中几种常见的方式&#xff1a; 1.使用<a>标签的download属性 <template><a :href"fileUrl" download>点击下载文件</a> </template><script> export default {data() {return {fileUrl: h…

宝塔面板手动执行定时任务脚本的解决方案

在终端命令行执行定时任务&#xff0c;可以使用 cron 这个工具。cron 是 Unix、BSD、Linux 和类 Unix 操作系统中的工具&#xff0c;用于在固定时间、日期或间隔执行预定的命令。 为了每10分钟执行 /www/wwwroot/jwcsv.kwfw.net_20080/cron/test.sh 这个脚本&#xff0c;需要编…

【C语言】每日一题,快速提升(6)!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 题目&#xff1a; 矩阵转置 有一个矩阵&#xff0c;他想知道转置后的矩阵&#xff08;将矩阵的行列互换得到的新矩阵称为转置矩阵&#xff09; 示例&#xff1a; 输…

深入了解Semaphore、CountDownLatch等实用工具的用法

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

JRT质控打印

最近客户端打磨完了&#xff0c;又来推进质控了。之前演示的打印都是Demo示例&#xff0c;这次真正的写质控图的打印&#xff0c;数据就是质控数据录入界面录入的数据。其中质控图打印应该算最复杂的类型了。涉及JS的绘图&#xff0c;打印表格等&#xff0c;表格比较简单、还没…