【迅搜09】索引管理(二)增删改操作

索引管理(二)增删改操作

今天我们来学习真正的,最核心的索引管理相关的操作。但其实今天的内容还更简单一些,为啥呢?因为索引管理中,最核心的就是对于数据的增、删、改呀。其实要往大了说,查询也是针对索引的操作,只不过相对来说,搜索引擎引用往往是读多写少,而且相比数据库来说,它的写还要少一些。

因此,XS 在 SDK 组件中,将索引对象和查询对象分开了。同样地,后端服务,也是通过 8383 和 8384 两个端口区分开了索引操作和查询操作。不过这也带来了一个问题,那就是索引的增、删、改操作是异步的,在查询的反馈上并不是完全及时的。

说了这些,其实就是要弄清楚我们的业务场景了。对于需要使用 XS 的系统来说,主要是文章、商品详情、存档数据这一类的信息。通常来说,文章文档类的应用可能会更多一些。这些应用往往修改的频率不高,而且就像我们的数据库设计,对于状态为未发布的文章,也完全没有必要进搜索引擎。大部分情况下,其实更多的前端调用还是在搜索上。根据这些业务场景,XS 的异步问题就完全不是问题了。当然,如果你想更快,更及时,那估计就是 ES 之类的了,但是,ES 也不是完全同步的哦,它也有刷分片级别设置的,默认情况下也不是直接就能马上读取到新写入的数据的,只是说,看到的效果比我们 XS 稍快一些。另外包括 Sphinx ,它对增量索引的支持都还没 XS 好呢(Sphinx绑定 MySQL 后全量索引速度非常快,它不推荐增量索引)。换句话说,搜索引擎的索引,应该是变动小的,而查询量,则是非常大的,需要全文检索分词的,这类应用,才是搜索引擎的主战场。

好吧,又扯了一遍搜索引擎的概念和应该在什么场景下使用搜索引擎。目的其实也是再次提醒大家一定要转变一下思维,要不看了 XS 的增、删、改功能之后,又用 MySQL 的思维来套,就会说 XS 的多垃圾呀什么的。了解之后就知道了吧,搜索引擎都是这个鸟样,千万不要直接用数据库的增、删、改思维来直接开骂哦。

好了,接下来我们就进入正文。

添加数据

添加数据的代码之前我们早就已经使用过的,没啥多说的。

$index = $xs->index; // 后期如果直接写一个 $index ,就是直接表示为 $xs->index 获得的 XSIndex 对象$index2 = $xs->index->add(new XSDocument(['title'=>'添加一条','content'=>'添加一条'.date('YmdHis'),'id'=>uniqid()]));var_dump($index === $index2);

add() 方法返回一个 XSIndex ,它自己本身,上面的代码我们就是来测一下 add() 返回的对象和调用 add() 方法时的对象是不是一样的,结果是全等于的 true 。

这里有什么问题呢?其实呀,这也是 XS 中一个比较被诟病的一点,添加操作,包括之后我们要学习的修改和删除操作,这些方法的返回值都只是一个 XSIndex 对象本身,没有其它内容。这样的话,我们就不知道这个操作是成功还是失败了。

在源码中,添加函数其实上调用的是修改的函数,这个我们在后面修改数据中再说。然后修改函数最终是通过之前我们学习过的 XSServer 对象中的 execCommand() 发送给服务端的。服务端在接收到之后返回的内容在 SDK 中没有处理,也没有返回或者记录。

这一点可能也会让大家比较困惑,其实 execCommand() 之后,是有返回值的,大家可以在源码中找到执行的地方打印它的返回值。

// /vendor/hightman/xunsearch/lib/XSIndex.class.php update() 方法下部
//…………………………
// execute cmd
if ($this->_bufSize > 0) {$this->appendBuffer(implode('', $cmds));
} else {for ($i = 0; $i < count($cmds) - 1; $i++) {$this->execCommand($cmds[$i]);}$this->execCommand($cmds[$i], XS_CMD_OK_RQST_FINISHED); // 打印这里
}
//…………………………

就能看到返回的是一个 XSCommand 对象,属性内容是:

XSCommand Object
([cmd] => 128[arg1] => 0[arg2] => 250[buf] => [buf1] => 
)

这样的,然后将 cmd 的值,也就是 128 放到 xs_cmd.inc.php 中查找,就会发现它对应的是 define('XS_CMD_OK', 128); 这个常量,意思很清楚了吧。

SDK 没有封装返回状态的结果,我觉得可能是因为这个 SDK 以完全面向对象的方式来写得,如果有问题直接会报错了,你可以再继续深入父类 XSServer 中 execCommand() 的源码进行查看。所以我们在调用 add() 之后,如果没报错,那么就可以认为是成功了。另一点就是由于索引的操作是异步的,返回的这个 128 状态也只是说服务端接收到了数据,完成了校验,但并不代表数据是正式插入成功了。

修改数据

修改数据使用的是 update() 方法。

$xs->index->update(new XSDocument(['title'=>'添加一条','content'=>'添加一条'.date('YmdHis'),'id'=>uniqid()]));

上面这条语句是有问题的,不知道各位同学发现了没有。

语法没问题,数据没问题,问题出在那个 id 字段上。我们使用的是 uniqid() 这个函数是 PHP 中生成不唯一字符串的。也就是说,上面的这个更新语句中,主键是一个新的 id 。不过幸好,在 XS 中,update() 的执行原理是根据主键 id ,先删除原来的数据,然后再添加一条。如果主键 id 指定的数据不存在,就是新添加一条数据。

划重点,先删除原来的,再添加一条新的进去。也就是说,这个更新也不是传统数据库层面的上更新,而是类似于很多 OLAP 大数据数据库的处理方式。这样的操作就会带来几个问题,我们来看下。

首先,主键 id 是可以重复的,可以有多条数据的主键是一样的。比如我们插入两条 id 一样的数据。

$xs->index->add(new XSDocument(['title'=>'添加一条1','content'=>'添加一条'.date('YmdHis'),'id'=>'123123123']));
$xs->index->add(new XSDocument(['title'=>'添加一条2','content'=>'添加一条'.date('YmdHis'),'id'=>'123123123']));

关于为啥主键 id 可以插入同样的数据问题,之前已经说过了,这里就不在赘述了,不记得的小伙伴可以翻看一下之前的文章。添加成功之后,执行下面的更新操作。

$xs->index->update(new XSDocument(['title'=>'添加一条3','pub_time'=>time(),'id'=>'123123123']));

这里我们更新 id 为 123123123 的数据,但内容产生了很大的变化,标题最后的数字是 3 ,还没有 content 了,出来的结果是什么呢?

> php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini "添加一条"
--------------------
解析后的 QUERY 语句:Query(<alldocuments>)
--------------------
在 1 条数据中,大约有 1 条包含  ,第 1-1 条,用时:0.0118 秒。1. 添加一条3 #123123123# [100%,0.00]Category_name:  Tags:  Pub_time:20221128

看到结果了吧,数据只剩一条了,content 内容也没有了,但是 pub_time 有数据了。

这就是 update() 先删除,后添加的典型效果演示。如果你需要只更新其中某一个字段的值,也必须将所有的字段都带上,否则别的字段可能就没了哦。另外,删除多余的相同主键的数据其实在逻辑上是正确的,这个并没有其它多说的。

先别着急开骂,因为我们又要搬老大出来救场了。没错,ES 也是这样的!

如果你学过一点 ES 的话,那就会知道它可以通过 PUT 和 POST 两种请求方式来更新数据。不管使用哪个,如果直接在参数中使用字段更新,原来的文档数据就会被覆盖,就跟 XS 的效果是一样的,没写的字段就没有了。但 ES 可以通过指定 doc 字段,然后再更新,达到更新指定的字段效果。

总结一下,XS 中的 update() 相当于就是 ES 中的普通更新方式,但 XS 中没有提供 doc 的语法糖,只有先删后增这一种更新方式。

至于为什么搜索引擎都要这样来更新呢?因为倒排索引,之前我们已经学习过了,倒排索引是分词之后通过词项来建立和索引文档主键 id 的映射关系。如果是修改的话,需要的工作量非常大,需要遍历每个单词词项然后修改它所指向的 id 。而现在,我们先删,然后重新使用添加的过程进行索引。另外还有分数以及各种其它的计算都要重来一次,因此,直接删除再添加效率会更高一些,大概是这么个意思,但具体的原因和解释要更加的复杂,也不是我的水平所能理解的了,有兴趣的小伙伴可以自己再查找资料进行深入的学习。

和 add() 的差别

前面我们已经看到了,在使用 update() 的时候,如果主键数据不存在,就是新增。通过源码,相信大家也能看到 add() 里面就一行代码,直接就是去调用 update() ,但第二个参数设置成了 true 。那么,咱们直接用 update() 做新增不就好了?为啥还要一个 add() 呢?

其实从 add() 和 update() 的行为就可以看出来 ,add() 明显是少了一个判断的,而这个判断就是主键 id 数据是否存在。也就是我们一直在说的,add() 会忽略主键的唯一性,直接添加数据。而 update() 则不会,它会去进行查找判断,如果找到了,那么还得先删除,相对来说其实就是多了两个步骤。

由上可知,如果我们是针对大量数据的全量新建或者重建索引,那么 add() 的效率更好。而如果是日常的索引建立更新,比如说日常的添加修改文章、添加修改商品等等,则使用 update() 会更为保险,能够保证主键唯一性。

删除数据

在上面的添加和修改中,其实很多基础概念就已经讲完了,对于删除来说,没啥特别的东西,不过它有两种删除方式。

一是根据主键 id 进行删除,也是最推荐的方式。

$xs->index->del('6380e14c38b04');

这个参数就是 id 属性的字段值,我们在上面的测试代码中使用 add() 添加的是 uniqid() 类型的数据,所以 id 字段保存的内容就是这个样子的。除了单个 id 之外,我们也可以批量删除。

$xs->index->del(['6380e241c27e5','6380e2423b047']);

另一种就是根据分词词项删除,这个嘛,先看例子。

$xs->index->del('添加一条','title');

大家可以试试上面这条删除语句,不出意外的话,是删不掉数据的。这是为啥呢?又要提到我们关于分词的概念了。这里的第一个参数是一个词项,注意,是词项,就是我们之前说过的 term 。也就是说,倒排索引字典中需要有一个 “添加一条” 这样的完整的单词的词项索引,才会删除这条索引对应的文档。很明显,这一句话肯定是要被分词的。它会被分成什么词呢?在 SDK 的测试文件 Quest.php 后面增加的参数 --show-query ,就可以看到分词后的查询语句内容,大家可以使用 “添加一条” 来进行搜索,能看到它被拆分为这样的结果。

> php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini "添加一条"
--------------------
解析后的 QUERY 语句:Query((添@1 AND 加一@2 AND 条@3))
--------------------
…………………………

好了,后面的不用多说了,直接使用 “加一” 来删除好了。

$xs->index->del('加一','title');

这样我们之前测试的数据就都可以删掉了。

不过,并不推荐这种方式。为啥呢?没错,它很灵活,就像数据库中 Delete 语句时的 Where 条件一样。但是,如果你没有对分词和词项有清晰的了解,就很有可能删错或删多。毕竟,它不像数据库的 Where 是完全匹配的。因此,不是说不能用,只是说不太推荐而已。用不用,还是要看你自己的权衡咯。

批量操作

这个批量操作呀,还是要先拿数据库来做为例子。我们知道,在数据库操作时,如果有大量的写入,一条一条的 Insert 和一次 Insert 多个 Value 那样的批量插入相比,后者速度能提升不少。特别是如果数据库不在一个网段或者是远程连接数据库时。另外,之前我们学习过的 Redis 中的 Pipeline ,也是类似的效果,一次性批量提交一堆操作命令。而 XS 中的批量操作,是更类似于 Redis 的,因为它不止可以有 add() 操作,还可以有别的操作。

先来一个简单的测试。XS 中使用缓冲区的概念来实现批量操作,开始批量操作使用 openBuffer() 而结束则使用 closeBuffer() 。

$index = $xs->index;
$index->openBuffer();
$index->add(new XSDocument(['title'=>'添加一条','content'=>'添加一条'.date('YmdHis'),'id'=>uniqid()]));
sleep(60);
$index->add(new XSDocument(['title'=>'添加一条','content'=>'添加一条'.date('YmdHis'),'id'=>uniqid()]));
$index->add(new XSDocument(['title'=>'添加一条','content'=>'添加一条'.date('YmdHis'),'id'=>uniqid()]));
$index->closeBuffer();

在这个批量操作中,我们先开启 openBuffer() 然后使用 add() 添加一条数据。接着休息 60 秒,这时,你可以去尝试搜索查询数据,60秒内是查不到信息的,因为这时还没有提交。然后再 add() 两次,最后通过 closeBuffer() 关闭缓冲区实现数据提交。这时,再稍等几秒就可以查询到数据了。

openBuffer() 有一个参数,可以设置一个缓冲区大小的值。这个概念和我们之前在 Nginx 中的各种缓冲区大小的概念是类似的,也就是在批量操作内部的数据,如果超过了缓冲区设置的大小,直接就提交了,如果没有超过,就会继续往缓冲区添加。这个缓冲区的概念也不用多解释了吧,就是开辟的一片内存嘛。

有的同学可能会问,这个缓冲区大小要怎么设置呢?默认值是 4MB ,可以根据我们部署 XS 的服务器的内存大小和内存使用情况来设置。缓冲区越大,一次提交的数据就越多,网络频繁连接的次数就减少。大部分情况下其实可以不用设置,而如果有特殊需要,比如单个文档过大或者需要大量的全量操作索引时。

那么它的效果有那么明显吗?咱们可以来试试。

$index = $xs->index;
$time = microtime(true);
$index->openBuffer();
for($i=1;$i<=100000;$i++){$index->add(new XSDocument(['title'=>'添加一条'.$i,'content'=>'添加一条'.$i.date('YmdHis'),'id'=>$i]));
}
$index->closeBuffer();
echo microtime(true)-$time;
// 不使用buffer 91.203243017197
// 使用buffer 2.8002960681915

这一段测试是直接循环 100000 次添加 100000 条数据,可以看到我的测试结果写在下面的注释中了。差距还是非常明显的吧,又要搬出 ES 大佬了,在 ES 中,类似的功能是 _bulk

除了添加之外,在缓冲区中也可以执行其它操作。

$index->openBuffer(); 
...
$index->add($doc);
...
$index->del($doc);
...
$index->update($doc);
...
$index->closeBuffer();

这个就不测试了,大家可以自己试试哦。

清空索引

清空索引的代码我们其实也用过了。

$xs->index->clean();

这个就不多说了,没啥参数,一把清空整个索引项目里的所有文档数据,相当于 MySQL 中的 truncate 。在之前我们使用 的 SDK 提供的 Indexer.php ,也有 --clean 参数相当于调用这个函数。

但是需要注意的,clean() 清空索引是同步的操作,也就是说,一调用这个函数,马上进行查询,也查不到内容。数据马上被清空了,而且这个操作不可恢复,线上生产环境要慎用哦。同时,如果你想全量重建索引,使用 clean() 的话,因为后续的添加是异步的,所以会短暂出现索引库是空的情况,在这段时间内是没有任何数据的。要想避免这种情况,也就是想实现一边重建索引,一边还能继续查询,当索引重建完成后,查询到的也变成新数据的这种效果,就要使用下一个要学习的功能啦。

平滑重建索引

上面我们已经说过,要想平滑的,也就是不中断地完成索引地重建,就需要使用到平滑重建索引的功能。这个功能也是通过 XSIndex 的几个函数方法来实现的。

$index = $xs->index;
$index->stopRebuild();
$index->beginRebuild();
$time = microtime(true);
$index->openBuffer();
for($i=1;$i<=100000;$i++){$index->add(new XSDocument(['title'=>'添加一条'.$i,'content'=>'添加一条'.$i.date('YmdHis'),'pub_time'=>time(), // 增加pub_time数据'id'=>$i]));
}
$index->closeBuffer();
$index->endRebuild();

stopRebuild() 方法,用于清除上次重建失败的错误状态。在重建过程中,可能因为各种原因导致重建工作意外终止,这时索引库会进入一个崩溃状态,出现 DB has been rebuilding 的错误。我们就需要先通过 stopRebuild() 清除错误状态。

接着通过 beginRebuild() 方法开始重建,这时你可以尝试继续访问查询数据,还是可以正常搜索到的。然后我们开始重建工作,针对之前的数据,我们增加了 pub_time 属性内容。前面的测试中,所有数据的 pub_time 字段是没有数据的,现在我们就给它加上。

操作完成之后,使用 endRebuild() 方法结束结束重建。

和全量添加索引一样,过一会我们再次查询数据,就会发现所有数据都有 pub_time 属性了。在这个过程中,服务一直都是可用的。

平滑重建的内部实现,相当于是在一个临时的区域开辟一个新的库,把所有数据先更新到新库,等到全部数据索引完成后,再用新库来替换老的库,从而保证服务的不中断。为确保重建的顺利完成,在重建时,不要对同一个项目开启多个进程、连接,避免同时交替重建引发错乱。

总结

今天的内容真的不复杂吧,只是针对索引数据的增、删、改操作,另外还加上了清空、批量操作以及平滑重建的内容。函数方法的使用都简单,重点还是需要转变很多思维,与数据的操作不同的地方都是需要关注的。比如说添加是异步的、修改是先删后增、删除如果按分词词项的注意点等等。

下篇文章,我们将继续学习 XSIndex 中剩余部分的内容。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/xunsearch/source/9.php

参考文档:

http://www.xunsearch.com/doc/php/api/XSIndex#addExdata-detail

http://www.xunsearch.com/doc/php/guide/index.add

http://www.xunsearch.com/doc/php/guide/index.update

http://www.xunsearch.com/doc/php/guide/index.del

http://www.xunsearch.com/doc/php/guide/index.clean

http://www.xunsearch.com/doc/php/guide/index.rebuild

http://www.xunsearch.com/doc/php/guide/index.buffer

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

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

相关文章

缓存的定义及重要知识点

文章目录 缓存的意义缓存的定义缓存原理缓存的基本思想缓存的优势缓存的代价 缓存的重要知识点 缓存的意义 在互联网高访问量的前提下&#xff0c;缓存的使用&#xff0c;是提升系统性能、改善用户体验的唯一解决之道。 缓存的定义 缓存最初的含义&#xff0c;是指用于加速 …

宝塔PostgreSQL设置数据库远程访问

宝塔PostgreSQL设置数据库远程访问 宝塔的PostgreSQL1. 添加数据库2. 打开PostgreSQL设置界面3. 修改配置4. 重载配置/重启数据库 Docker的PostgreSQL1. postgresql.conf2. pg_hba.conf3. 重启数据库 注意其他问题 宝塔PostgreSQL设置数据库远程访问&#xff1f;docker容器Post…

CleanMyMac X2024(Mac优化清理工具)v4.14.5中文版

CleanMyMac X是一款颇受欢迎的专业清理软件&#xff0c;拥有十多项强大的功能&#xff0c;可以进行系统清理、清空废纸篓、清除大旧型文件、程序卸载、除恶意软件、系统维护等等&#xff0c;并且这款清理软件操作简易&#xff0c;非常好上手&#xff0c;特别适用于那些刚入手苹…

《每天一个Linux命令》 -- (15) mkdir命令

欢迎阅读《每天一个Linux命令》系列 &#xff01;在本篇文章中&#xff0c;将说明mkdir命令用法。 概念 mkdir命令是Linux系统下的目录创建命令&#xff0c;用于创建指定的目录。 英文原意&#xff1a;make directories&#xff0c;所在路径&#xff1a;/bin/mkdir&#xff0…

【AI】YOLO学习笔记

作为经典的图像识别网络模型&#xff0c;学习YOLO的过程也是了解图像识别的发展过程&#xff0c;对于初学者来说&#xff0c;也可以了解所采用算法的来龙去脉&#xff0c;构建解决问题的思路。 1.YOLO V1 论文地址&#xff1a;https://arxiv.org/abs/1506.02640 YOLO&#x…

软件设计师——计算机网络(三)

&#x1f4d1;前言 本文主要是【计算机网络】——软件设计师——计算机网络的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1…

混淆相加和拼接(js的问题)

JavaScript 中的加号 "()" 有两个功能&#xff1a;相加和拼接。也就是数字的相加和字符串的拼接。一些开发者经常会误用这个操作符。 比如&#xff1a; const num1 30; ​​const num2 "20"; ​​const num3 30; ​​const word1 "Java" ​…

成本管理常用的ChatGPT通用提示词模板

成本分析&#xff1a;如何进行成本分析&#xff1f; 成本核算&#xff1a;如何进行成本核算&#xff1f; 成本控制&#xff1a;如何控制成本&#xff1f; 成本效益分析&#xff1a;如何进行成本效益分析&#xff1f; 成本预测&#xff1a;如何预测成本&#xff1f; 成本决…

Automotive Bionics

汽车仿生学是一种通过模拟生物系统的某些特征来设计汽车的方法。一些典型的汽车仿生学例子包括&#xff1a; 鲨鱼式车型&#xff1a;这种车型的设计灵感来源于鲨鱼的流线型身体&#xff0c;这种设计能够减少空气阻力和水阻力&#xff0c;提高车辆的燃油效率和行驶速度。 甲壳虫…

汽车EDI:Chrysler EDI项目案例

菲亚特克莱斯勒汽车Fiat Chrysler Automobiles(FCA)是一家全球性汽车制造商&#xff0c;主营产品包括轿车、SUV、皮卡车、商用车和豪华车等多种车型。其旗下品牌包括菲亚特、克莱斯勒、道奇、Jeep、Ram、阿尔法罗密欧和玛莎拉蒂等。 Chrysler通过EDI来优化订单处理、交付通知、…

Fuzz进阶教学——基于机器学习的模糊测试相关工作

【参考文献】[1]王鹃,张冲,龚家新等.基于机器学习的模糊测试研究综述[J].信息网络安全,2023,23(08):1-16. 目录 一、机器学习在测试用例生成中的应用 1、文件解析软件的测试用例生成 2、网络协议的测试用例生成 3、代码解析工具的测试用例生成 二、机器学习在测试用例变异中…

TrustGeo代码理解(三)model.py

代码链接:https://github.com/ICDM-UESTC/TrustGeo 一、导入各种模块和神经网络类 from math import gamma from re import L from .layers import * import torch import torch.nn as nn import torch.nn.functional as Func import numpy as np 这段代码是一个 Python 模…

快速学习C++中的模板

模板是一个让C支持范型编程的重要功能&#xff0c;它本质上是一个万能变量适配器&#xff1b;vector,pair等都是使用模板实现的 模板是C的一个强大特性&#xff0c;它允许您编写通用的代码来处理不同的数据类型。您可以有函数模板和类模板。 函数模板: 函数模板允许您创建一…

Python中的程序逻辑经典案例详解

我的博客 文章首发于公众号&#xff1a;小肖学数据分析 Python作为一种强大的编程语言&#xff0c;以其简洁明了的语法和强大的标准库&#xff0c;成为了理想的工具来构建这些解决方案。 本文将通过Python解析几个经典的编程问题。 经典案例 水仙花数 问题描述&#xff1a…

极坐标下的牛拉法潮流计算39节点MATLAB程序

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 潮流计算&#xff1a; 潮流计算是根据给定的电网结构、参数和发电机、负荷等元件的运行条件&#xff0c;确定电力系统各部分稳态运行状态参数的计算。通常给定的运行条件有系统中各电源和负荷点的功率、枢纽…

设计模式之建造者模式(二)

目录 概述概念角色类图适用场景 详述画小人业务类的介绍代码解析 建造者基本代码类介绍代码解析 总结设计原则其他 概述 概念 建造者模式是一种创建型设计模式&#xff0c;它可以将复杂对象的构建过程与其表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 角色 …

Python函数和模块的使用

我的博客 文章首发于公众号&#xff1a;小肖学数据分析 在开发过程中&#xff0c;函数和模块帮助我们将复杂的代码逻辑分解为可管理的部分&#xff0c;提升代码的可读性、可维护性和重用性。 本文将介绍如何在Python中有效利用函数和模块&#xff0c;提供详细的示例。 函数的…

【C++干货铺】会搜索的二叉树(BSTree)

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 前言&#xff1a; 二叉搜索树 二叉搜索树概念 二叉搜索树操作 二叉搜索树的查找 二叉搜索树的插入 二叉搜索树元素的删除 ​二叉搜索树的实现 BSTree结点 …

GraphicsProfiler 使用教程

GraphicsProfiler 使用教程 1.工具简介&#xff1a;2.Navigation介绍2.1.打开安装好的Graphics Profiler。2.2.将手机连接到计算机&#xff0c;软件会在手机中安装一个GraphicsProfiler应用(该应用是无界面的&#xff09;。2.3.Show files list2.4.Record new trace2.4.1.Appli…

TSINGSEE视频智能解决方案边缘AI智能与后端智能分析的区别与应用

视频监控与AI人工智能的结合是当今社会安全领域的重要发展趋势。随着科技的不断进步&#xff0c;视频监控系统已经不再局限于简单的录像和监视功能&#xff0c;而是开始融入人工智能技术&#xff0c;实现更加智能化的监控和安全管理。传统的监控系统往往需要人工操作来进行监控…