搜索引擎关键字智能提示实践

news2024/12/25 9:34:48

为了提高阅读体验,请移步到:搜索引擎关键字智能提升实践

一、背景

搜索关键字智能提示是一个搜索应用的标配,主要作用是避免用户输入错误的搜索词,并将用户引导到相应的关键词上,以提升用户使用体验。

雪球以连接人与资产,让财富的雪球越滚越大为使命,在投资社区领域处于领先地位。为了让用户快速准确找到目标的股票,大V,我们搭建搜索提示系统(以下简称Sug系统)来自动补全 Query。用户在查找股票时主要输入股票名称、股票 Symbol 进行搜索,为了提升用户体验和输入效率,本文实现了一种基于 Trie 树前缀匹配查询 + 模型排序关键词智能提示(Suggestion)实现。

二、需求分析

  • 支持前缀匹配原则:在搜索框中输入 “中国”,搜索框下面会以 “中国” 为前缀,展示 “中国平安”、“中国神华”、“中国中免” 等搜索词;输入“贵州”,会提示 “贵州茅台”、“贵州燃气”、“贵州百灵”等搜索词。

  • 同时支持汉字、拼音输入:由于中文的特点,如果搜索自动提示可以支持拼音的话会给用户带来更大的方便,免得切换输入法。比如,输入 [zhongguo] 提示的关键字和输入 “中国” 提示的一样,输入 [guizhou] 与输入 “贵州” 提示的关键字一样。

  • 支持拼音缩写输入:对于较长关键字,为了提高输入效率,有必要提供拼音缩写输入。比如输入 “zg” 应该能提示出 [zhongguo] 相似的关键字,输入 [gzmt] 也一样能提示出 “贵州茅台” 关键字。

  • 支持多音字输入提示:比如输入 [chongqing] 或者 [zhongqing] 可以提示出 “重庆啤酒”、“重庆钢铁”、“重庆百货”。

  • 支持大写数字转阿拉伯数字:比如输入 [360] 可以提示出 “三六零” 这只股票。

  • 支持 Query 纠错:比如输入 “贵州毛台” 可以提示出 “贵州茅台”。

三、解决方案

什么是 Tire 树

Tire 树,也叫字典树,又称单词查找树或键树,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。

Trie 树的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

它有3个基本性质:

  1. 根节点不包含字符,除根节点外,每一个节点都只包含一个字符。

  1. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。

  1. 每个节点的所有子节点包含的字符都不相同。

Trie Tree 是一颗存储多个字符串的树,树的每条分支代表一个字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。例如给出一组字符串:中国平安、中国银行、中兴通讯、中信证券、平安银行、平安、zgpn 等。我们可以构建出下面的 Trie 树:

其中,绿色节点是数据节点,黄色节点是普通节点。从根节点到绿色节点的一条路径表示一个字符串(注意:数据节点并不都是叶子节点)。

由上图可知,当用户输入 “中国” 的时候,搜索框会展示以 “中国” 为前缀的 “中国平安”、“中国银行”等关键词;当用户输入 “中兴” 的时候,搜索框里面会提示以 “中兴” 为前缀的“中兴通讯”等关键词。

Trie 树的实现

插入过程

为了更容易理解 Trie 树是怎么构造出来的,我画了一个 Trie 树构造的分解过程。构造过程的每一步,都相当于往 Trie 树中插入一个字符串。当所有字符串都插入完成之后,Trie 树就构造好了。

以下代码是 Trie 树插入的 demo

class TrieNode:    def __init__(self, char=None):        self.char = char        self.data = None        self.children = {}    def is_data_node(self):        return self.data is not None    def is_leaf_node(self):        return len(self.children) == 0class Trie:    def __init__(self):        self.root = TrieNode("/")    def insert(self, text):        if not text: return        p = self.root        for char in text:            if char not in p.children:                p.children[char] = TrieNode(char)            p = p.children[char]        p.data = text

查询过程

精确查询

在 Trie 树中查询 “中国平安”时,需要将字符串分割成单个字符:中、国、平、安,然后从 Trie 树的根节点开始匹配。如图所示,红色路径就是在 Trie 树中的匹配路径。

以下代码是 Trie 树精确查询的 demo

    def find_node(self, pattern):        if not pattern: return        p = self.root        for char in pattern:            if char not in p.children: return            p = p.children[char]        return p    def find(self, pattern):        node = self.find_node(pattern)        if node: return node.data

前缀查询

如果要查询 “中国”,用上面的同样的方法,从根节点开始,沿着红色路径来到节点 “国”。但是节点 “国” 不是数据节点。“中国” 是某些字符串的前缀子串,不能完全匹配任何字符串。如果需要给用户 Query 建议的话,需要继续下探,继续沿着蓝色路径,找到 “中国平安” 和 “中国银行”。

以下代码是 Trie 树前缀查询的 demo

    def prefix_find(self, pattern):        node = self.find_node(pattern)        if not node: return        res = []        for k, v in node.children.items():            self.drill_down(v, res)        return res    def drill_down(self, node, res):        if node.data:            res.append(node.data)        for k, v in node.children.items():            self.drill_down(v, res)

删除过程

如果要从 Trie 树删除 “中国银行” 这个字符串,需要先精确查找到对应的数据节点,将该数据节点的 data 属性设置为 None 即可。此时节点 “银” 和节点 “行” 已经没有用了,如果不删除,即浪费内存空间,也影响后续的前缀查询的下探效率。要删除废弃节点,需要用栈记录查找 “中国银行” 时所走的路径。利用栈先进后出的特性,从下向上依次删除废弃节点。删除条件为:当前节点是叶子节点并且不是数据节点。注意:节点 “中” 和节点 “国” 在“中国平安”中还在使用,不可以删除。

以下代码是 Trie 树删除过程的 demo

    def delete(self, pattern):        if not pattern: return        # 查找要删除的节点        p = self.root        stack = [p]        for char in pattern:            if char not in p.children: return            p = p.children[char]            stack.append(p)        if not p.data or p.data != pattern: return        # 删除数据        p.data = None        # 删除路径        node = stack.pop()        while stack:            parent = stack.pop()            if not node.is_leaf_node() or node.is_data_node() or node.char not in parent.children:                break            parent.children.pop(node.char)            node = parent

雪球的 sug 方案

雪球作为一个著名的投资社区,股票搜索是雪球搜索的重要应用场景。提升用户搜索股票的体验势在必行。后续为了便于讲解,所说的股票数据就代表了 sug 数据。

  • 股票关键字:股票名称和 symbol 是用户最常用的搜索关键词,对 sug 非常重要。因此 sug 系统对接行情部门来维护股票名称和 symbol。

  • 汉字转拼音:在中文语境下用户使用拼音来搜索股票很常见的,因此需要将股票名称转成拼音作为股票的关键词,java 中考虑使用 pinyin4j 组件实现转换。

  • 拼音缩写提取:考虑到需要支持拼音缩写,汉字转换拼音的过程中,顺便提取出拼音缩写,如“guizhoumaotai”,“zhongguopingan”—>“gzmt”,“zgpa”。

  • 多音字全排列:要支持多音字提示,对查询串转换成拼音后,需要实现一个字符串多音字全排列算法。

  • 大写数字转阿拉伯数字:比如:“三六零” 这只股票,有用户习惯用 “360” 进行搜索。

  • 挖掘股票关键词:通过一些离线挖掘来丰富股票关键词。比如:“英特尔” 这只股票, “intel” 这个关键词无法通过股票名和股票 symbol 生成出来。

  • 人工配置股票关键词:一切规则皆有漏,需要人工配置关键词兜底,作为最后的防线。

系统架构

当 sug-server 接收用户 Query 预处理完毕后,就会在本地内存中的 Trie 树上进行前缀查询,召回 TopN 条数据(一般 N = 10)进行组装特征,最后交给模型打分排序后返回给用户。为了给模型训练准备训练数据,需要将 sug 的展示数据及特征通过 Kafka 落盘到大数据平台,同时雪球 APP 上用户对 sug 的点击行为也需要埋点上报。

整个过程涉及三个模块:

  1. Trie 树的维护

  1. 召回:Trie 树的前缀查询

  1. 模型排序:特征组装集模型打分

Trie 树的维护

sug-server 对外提供 sug 服务,召回时依赖 Trie 树。因此每台 sug-server 服务器内存中都保存一棵完整的 Trie 树。当 sug-server 服务启动时,需要从 FTP 下载 Trie 树的二进制文件来构建一棵完整的 Trie 树。当股票数据变更时,需要及时更新每台 sug-server 内存中的 Trie 树。Trie 树的生成、更新、删除、巡检等功能都依赖:Trie 树管理系统。

股票的 sug 数据都存在 MySQL 数据库中。每当股票名称需要变更时,管理系统会记录变更日志,将最新的数据存入 MySQL 数据库中,然后通过 ZK 的订阅与通知机制,将变数据更同步到每一台 sug-server 服务器上。管理系统还会定期从 MySQL 数据库中读取全部股票 sug 数据构建 Trie 树,将构建好的全新的Trie 树序列化成二进制文件,推送到 FTP 服务器上,供 sug-server 启动时使用。 管理系统还承担了 sug 数据巡检功能,一旦发现有错误的数据及时修复。

召回

Trie 树结构设计

雪球的 Trie 树结构比较复杂,因为需要存储更多的数据来加速前缀匹配,为此我们定制开发了 Trie 树。为了减少内存空间的占用和提升 sug-server 服务的稳定性,我们抽象出 Trie 树需要的检索数据,并与多变的业务数据进行隔离。索引数据存储在 Trie 树中,保存在内存里,而 sug 服务需要业务数据,保存在 redis 中。sug 服务的业务数据存储在 Trie 树中原因有二:一、业务数据经常跟随业务调整:比如产品经理要给 sug 中股票添加一个状态图标;二、业务数据对准确性要求更高,因为业务数据是要展示给用户的。下图中 data_map 就是存储在 redis 中业务数据。

Trie 节点

如果 data 不为空,表示数据节点,否则表示普通节点。前缀查询下探时利用 sortHelperList 进行剪枝,达到加速召回的目的。在讲前缀查询时,会介绍 sortHelperList 。

以下代码是 Trie 树节点:

@Datapublic class TrieNode {   // 字符    private char key;   // 子节点    private Map<Character, TrieNode> subNodes;   // 节点权重(股票粉丝数)    private double weight;   // 数据节点(倒序)    private List<DataNode> data;   // 辅助排序    private List<SortHelper> sortHelperList = new ArrayList<>();    public TrieNode(Character key, double score) {        this.key = key;        this.score = score;        subNodes = new HashMap<>(2);    }    public Boolean isDataNode() {        return !CollectionUtils.isEmpty(data);    }    public Boolean isLeafNode() {        return subNodes == null || subNodes.isEmpty();    }}

以下代码是 Trie 树中数据节点存储的数据:

@Datapublic class DataNode {    private String symbol;    private double weight;}

前缀查询

前缀查询的整体思路与之前将的一致:精确匹配 + 下探,来召回 TopN 条数据。

用户在使用雪球 APP 进行搜索时,每输入一个字符都会调用一次 sug 服务。因此对 sug 服务的性能要求极高,必须几毫秒返回数据,否则用户就有卡顿感。然而雪球 Trie 树中,最多的子节点有:435个,Trie 树的高度:320。在某些 Query 场景下,下探所有路径获取所有数据后,再排序截取 TopN ,用户卡顿感非常明显。这是不可以接受的。因此我们从以下两个方面进行优化。

  1. 限制下探最深层数:下探时记录下探层数,当达到限制时,就停止下探。

  1. 在下探时进行剪枝:改造 Trie 树结构,添加路径权重,下探时剪除掉权重小的路径。

在下探时,假如有 100 个子节点等待下探,我们应该优先下探哪些子节点?应该丢弃哪些子节点?

数据节点权重越大表示数据越重要。下探时我们希望将权重大的数据优先获取到。要找到权重大的数据,我们需要将数据的权重赋值给普通节点,子节点的权重等于该子树上所有数据节点权重的最大值。至此每个子节点都已拥有权重,此时我们只需按照节点权重从大到小依次下探。 当获取够 N 条数据后,停止继续下探即可,从而达到了剪枝的目的。所有子节点的 key 按权重从大到小存储在 sortHelperList 中。

如下图,当用户 Query = “中国”时,有很多条路径等待我们去探索。从 sortHelperList 中获取到权重最大的子节点,优先遍历节点 “平” 这条路径。在下探节点 “平” 时,有可能获取到权重为 80 以下的数据,如果此时获取的数据条数达到了 N,那么节点 “银” 分支下更好的数据,就被丢失了。怎么解决这个问题呢?在下探过程中,可以带着一个权重范围,只获取在权重范围内的数据。比如:下探节点 “平”时,权重范围是 [100 , 80],只获取权重在范围 [100 , 80] 内的数据,这样就能保证这次下探获取的数据权重都大于后续下探。

节点 “平” 在 [100 , 80] 的范围被探索完毕后,权重范围变为 [80 , 70]。节点 “平” 在 [80 , 70] 之间的数据,在上一轮探索时并没有被获取,因此这轮带着权重范围 [80 , 70] 探索的节点是“平” 和 “银”。依次类推遍历后续路径。下探终止条件为:获取的数据量达到 N 或者所有路径都探索完毕。

注意:在数据权重发生变化时,需要维护路径上所有节点的权重。

在召回阶段(前缀查询阶段),对查询结果影响最大的两个因素:

“Query 与数据的相关性:体现在下探过程中,处在树层数越小的数据,越早的被获取到,数据相关性更强。
“数据的重要性:根据上面描述的下探规则,数据权重越大越早被遍历。当然数据的权重,可以简单是股票的粉丝数,也可以离线用模型打分,这样可以综合更多因素,比如:股票的状态(是否退市),股票的市场(A股,港股,美股)等。

到此线上 sug 服务已经介绍完毕。为了给用户更好的体验,我们还需要对结果进行模型打分。

模型排序

最初没有模型排序,雪球的 sug 完全是基于规则。sug 中的股票数据,以股票粉丝数打底,加入一些业务规则,比如 Query 完全等于股票名称或者股票 symbol 的数据排在第一位。已退市的股票进行降权。市场是 A 股的股票排在港股的股票之上等等。这些规则系统,在绝大多数场景下是有效的,但是总有 basecase 出现,并且在解决掉这个 basecase 时,会引入其他的 basecase。规则之间也会有冲突。

在 sug 仅仅基于规则实现时,像下边表格里的 badcase 是比较典型的,出现的原因: query 与 symbol 全匹配优先级最高。没有考虑 Query 与股票的历史 CTR 以及股票的近期热度。

基于规则

模型排序

基于规则

模型排序

规则系统缺点

  1. 容易规则冲突。

  1. 线上代码逻辑复杂:各种规则堆砌。

  1. 容易出现 bug。

  1. 后期维护成本高:不易扩展。

线性加权

根据 socre 排序优点:

  1. 在线代码简单,计算 score ,倒序。

  1. 容易扩展,增减排序因子简单。

想通过线性加权给 sug 数据打分排序。那么怎么去拟合这些维度的权重呢?逻辑回归处理这类问题的经典算法。

LR

逻辑回归(Logistic Regression)的数学模型和求解都相对比较简洁,实现相对简单。通过对特征做离散化和其他映射,逻辑回归也可以处理非线性问题,是一个非常强大的分类器。因此在实际应用中,当我们能够拿到许多低层次的特征时,可以考虑使用逻辑回归来解决我们的问题。

模型

Sigmoid 函数,其数学形式是:

对应的函数曲线如下图所示:

从上图可以看到 sigmoid 函数是一个 S 形的曲线,它的取值在 [0, 1] 之间,在远离 0 的地方函数的值会很快接近 0 / 1。这个性质使我们能够以概率的方式来解释。

sigmoid 函数优点

  1. 数据压缩能力,将数据压缩至 (0 , 1)

  1. 处处连续,便于求导。

  1. 两端不灵敏,分界面处灵敏。

策略

极大似然估计

假设:样本 独立同分布

每一个样本就是一个时间,整个样本集中事情同时发生的概率是: ,独立事件。

似然函数:

如果 ,那么 , 越大越好。

如果 ,那么 , 越大越好。

在数据集上,先 log 然后求和,越大越好。

  • 损失函数

算法

最小化损失函数

这是一个凸优化问题,根据凸优化理论,经典数值优化算法:梯度下降法,牛顿法。

梯度下降法

梯度:

目标:最小化 L(w)

步骤:

  1. 设置初始 w (随机数),计算 L(w)

  1. 计算梯度:

  1. 下降方向:

  1. :步长。

  1. 计算

  1. 如果 较小(收敛条件)或者达到轮数限制,停止;跳转到 2 。

牛顿法

Hession 矩阵:

牛顿下降方向:

训练模型

目标:将线上 sug 的展示日志与用户点击日志生成训练数据,训练 LR 模型,在线预估用户对 sug 点击率,根据点击率排序。

准备训练数据

样本数据:

例子:

X1 = [ query_stock_ctr, 匹配度,粉丝数,股票状态,股票近期热度 ]

Y1 = {1,0}

  • 1:用户点击

  • 0:没有点击

训练代码:

from sklearn.linear_model import LogisticRegression as LRclass SugRank:    def __init__(self):        self.model = LR(penalty="l2", solver="liblinear", C=0.2, max_iter=1000)        def train(self, fileName):        # 加载训练数据        x = []        y = []        # 特征选择时使用        feature_selector = set(range(100)) - set([])            for line in open(fileName, "r"):            try:                data = line.strip().split(",")                y.append(int(data[0]))                x_feature = [float(data[i]) for i in range(1, len(data)) if i in feature_selector]                 x.append(x_feature)            except Exception as e:                print("load data:", line, e)        # 训练        self.model = self.model.fit(x, y)        # 导出模型        self.w = self.model.coef_[0]        self.b = self.model.intercept_[0]

离线效果

预测值 = 1

预测值 = 0

真实值 = 1

TP

FN

真实值 = 0

FP

TN

准确率(Accuracy)= --- 针对整个模型

下图是调参准确率曲线:

在线效果

雪球 sug 模型排序上线后,点击率相对提升 31.56%。sug 的 badcase 明显减少。

下图是 sug 模型排序切流上线后点击率的变化:

左图:sug 第一位的点击率:

右图:sug Mean reciprocal rank:

四、总结

  1. 雪球 sug 服务基于定制化 Trie 树 + 模型排序,提升用户搜索体验。为了提高 sug 性能稳定性,创新性设计出 Trie 树下探时的剪枝策略。正是由于 sug 服务性能极好、准确率高,在 Query 理解项目中也调用 sug 服务,去识别 Query 中的股票。sug 服务抽象出索引数据,并与业务数据进行隔离,使得 sug 基础服务(Trie 树检索部分)非常稳定,一般个性化业务需求只需上层修改即可满足。

  1. 目前雪球 sug 服务接入的数据源:股票,公募基金,私募基金,大 V,热门 Query。

  1. sug 服务带来好处:

  1. 自动补全用户要输的 Query,提升了用户的搜索体验和输入效率。

  1. 对 Query 中错别字进行纠错处理,给出正确的 Query 建议。

  1. 多样化的用户 Query,经过 sug 服务变成了标准化 Query ,方便后续召回,甚至可以提前准备好搜索结果,减轻了搜索引擎的压力。

五、展望

  1. Trie 树的数据节点的权重,目前还是股票粉丝数,后续可以优化成模型打分。

  1. Trie 树目前还是单机模式,受限服务器内存大小,后续可以做成分布式。

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

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

相关文章

【K8S之调度器流程和扩展】如何给 scheduler 添加扩展插件、关闭默认插件、创建多个 scheduler?

参考 自定义 Kubernetes 调度器 阳明https://github.com/cnych/sample-scheduler-extender kube-scheduler 源码位置 kubernetes 调度器的源码位于 kubernetes/pkg/scheduler 中&#xff0c;大体的代码目录结构如下所示&#xff1a;(不同的版本目录结构可能不太一样) kuber…

如何检测文章被搜索引擎收录(如何让搜索引擎收录网站)

如何写网站内容才利于搜索引擎收录 网站关键词要想有好的排名&#xff0c;网站本身必须是被搜索引擎收录的状态&#xff0c;另外&#xff0c;网站上的相关内容收录越多&#xff0c;搜索引擎给与网站关键词的排名靠前概率会越大&#xff0c;那么&#xff0c;网站内容怎样来写会…

[Linux]生产者消费者模型(基于BlockQueue的生产者消费者模型 | 基于环形队列的生产者消费者模型 | 信号量 )

文章目录生产者消费者模型函数调用角度理解生产者消费者模型生活角度理解生产者消费者模型为什么要使用生产者消费者模型生产者消费者模型优点321原则基于BlockingQueue的生产者消费者模型POSIX信号量回顾信号量概念信号量操作函数环形队列基于环形队列的生产者消费者模型生产者…

Android Studio如何打jar包和aar包并使用

Android Studio如何打jar包和使用生成jar包方式module方式生成jar方式第一类修改主app的方式第二类&#xff1a;通过新建module方式生成jar包如何使用jar包aar生成并使用aar生成aar使用之前有篇文章介绍了so库的生成和使用&#xff0c;看这里&#xff0c;但是&#xff0c;如果我…

[NOI Online #1 入门组] 文具订购

题目描述: 小明的班上共有 n 元班费&#xff0c;同学们准备使用班费集体购买 3 种物品&#xff1a; 圆规&#xff0c;每个 7 元。笔&#xff0c;每支 4 元。笔记本&#xff0c;每本 3 元。 小明负责订购文具&#xff0c;设圆规&#xff0c;笔&#xff0c;笔记本的订购数量分别…

Linux 下安装 JDK 和 Maven 环境

目录 1. 进入 maven 官网下载安装包 2. 安装 maven 3. 添加 Maven 环境变量 4. 配置 Maven 本地仓库 5. 配置镜像 6. 配置 JDK 7. 测试 操作系统&#xff1a;Centos 7.6 安装 maven 环境前&#xff0c;需要先安装 java 环境&#xff0c;笔者这里已经成功安装 java 环境&…

PHP基础知识 - PHP面向对象OOP

目录 一. 面向对象基本知识 1.1 面向对象概念 1.2 什么是类 1.3 什么是对象 1.4 类与对象的关系 1.5 PHP创建类的示例 二、类、属性、方法的修饰符 2.1 类的修饰符 2.2 成员方法的修饰符 2.3 成员属性修饰符 2.4 访问控制修饰符 2.5 static 静态修饰符 2.6 final…

多模块项目中,SpringBoot项目下启动失败-无法加载主类com.xch.XxxApplication

错误&#xff1a;项目启动时&#xff0c;无法找到主类(启动类)XxxApplication由于&#xff0c;主类需要先被编译&#xff0c;再被JVM找到编译后的文件运行如&#xff1a;XxxApplication.java-(编译)-XxxApplication.class-(运行)所以&#xff0c;原因&#xff1a;1、未编译情况…

基于微信小程序的校运会管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端框架&#xff1a;VUE 数据库&#xff1a;MySQL5.7以上 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录 一、项目简介…

Erlik 2:一个基于Flask开发的包含大量安全漏洞的研究平台

关于Erlik 2 Erlik 2&#xff0c;也被称为Vulnerable-Flask- App&#xff0c;该工具是一个基于Flask开发的包含大量安全漏洞的研究平台。本质上来说&#xff0c;它是一个包含了大量漏洞的Flask Web应用程序。有了这个实验环境&#xff0c;广大研究人员可以轻松在Web渗透测试领…

【算法题解】14. 有效的括号

文章目录题目解法&#xff1a;使用栈的特性Java 代码实现Go 代码实现复杂度分析这是一道 简单 题。 来自&#xff1a;leetcode 题目 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字…

哈工大操作系统学习笔记(二)进程与线程

文章目录CPU管理的直观想法多进程图像用户级线程内核级线程内核级线程实现操作系统之树CPU 调度策略一个实际的schedule 函数进程同步与信号量信号量临界区保护信号量的代码实现死锁处理CPU管理的直观想法 CPU的工作原理&#xff1a; 自动的取值执行&#xff0c;给了初始地址&…

Swift 周报 第二十一期

前言 本期是 Swift 编辑组自主整理周报的第十二期&#xff0c;每个模块已初步成型。各位读者如果有好的提议&#xff0c;欢迎在文末留言。 Swift 周报在 GitHub 开源&#xff0c;欢迎提交 issue&#xff0c;投稿或推荐内容。目前计划每两周周一发布&#xff0c;欢迎志同道合的…

永久删除的照片怎么找回来?教你三招恢复方法

如果文件被永久删除了&#xff0c;想要恢复就没有这么简单了&#xff0c;永久删除的文件可能是已经从回收站清空的文件&#xff0c;或者是我们按住快捷键“shiftdelete”快捷键删除的文件&#xff0c;这样的话&#xff0c;我们无法在电脑上面查找到文件&#xff0c;潜意识里面认…

SAP ABAP SY-REPID 变化「Note」

6.10 前&#xff08;知悉&#xff09; SY-CPROG The name of the calling program in an external routine, otherwise the name of the current program. 外部例程中调用程序的名称&#xff0c;否则为当前程序的名称 SY-REPID Name of the current ABAP program. For externa…

第一章-操作系统引论

&#x1f31e;欢迎来到操作系统的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff…

解决数据兼容性问题

数据兼容性问题解决 问题说明 最近经常遇到新老数据兼容性的问题&#xff1a;某些新同事更改代码后&#xff0c;没有兼容旧数据&#xff0c;已有用户的数据显示不全或者错误&#xff0c;很麻烦。 技术层面&#xff0c;就是某个数据或者字段&#xff0c;存储在服务器上&#…

java后端工程师面试题(笔试):2022-11-04 经历(一)

java后端工程师面试题&#xff08;笔试&#xff09;&#xff1a;2022-11-04&#xff1a; 面试题&#xff1a;总分100 1、关于盒子模型(5分) 1&#xff09;盒子模型的种类有几种?分别是什么?(1分) 2种&#xff0c;分别是1、W3C标准盒子模型 2、IE盒子模型2) 容器中使用di…

射频已调波同步广播技术在山区高速公路同步广播建设中的应用

北京恒星科通发布于2023-2-2 我国高速公路建设速度的加快&#xff0c;目前我国已经建成通车的高速公路总里程已经达到14万公里&#xff0c;高速公路的安全与信息化建也达到了快速发展&#xff0c;高速公路调频广播覆盖一直是困扰高速公路管理方的一个重要问题&#xff0c;我国…

实时分析全面赋能金融业务,马上消费基于 Apache Doris 构建实时数仓的实践

导读&#xff1a; 近年来&#xff0c;马上消费的业务体量呈飞跃式增长&#xff0c;每天产生数据可达上千亿条&#xff0c;如何更高效挖掘这些数据的价值&#xff0c;成为了其必须要面临的挑战。随着各业务对实时数据分析的需求越来越强烈&#xff0c;马上消费于 2021 年引入 Ap…