Redis的五种常用数据类型详解及相关面试问题

目录

Redis的五种常用数据类型详解

简述

Redis五种基本数据类型

String字符串

常用命令

应用场景

Hash散列表

常用命令

使用场景

List链表

常用命令

应用场景

Set( 集合)

常用命令

应用场景

SortedSet( 有序集合) zset

常用命令介绍

应用场景

面试题常问的数据类型

简述

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings),散列(hashes), 列表(lists),集合(sets),有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询 。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

Redis的英文官网

Redis的中文官网

Redis的中文命令手册

本文主要介绍Redis五种常用的数据类型、三种特殊的数据类型的使用、应用场景。

Redis五种基本数据类型

String字符串

Redis 字符串是字节序列。Redis 字符串是二进制安全的,这意味着他们有一个已知的长度没有任何特殊字符终止,所以你可以存储任何东西,512 M为上限,主要的还是操作键值对。 String的数据结构是简单的Key-Value模型,Value可以是字符串,也可以是数字。

常用命令
  • 添加元素(SET命令)

格式:

set key value [expiration EX seconds|PX milliseconds] [NX|XX]

ex:秒级过期时间,nx:键不存在时才能设置成功,xx键存在时才能设置成功

(1)普通添加: set key value

(2)设置过期时间 setex: setex key seconds value

上图中:设置一个键为key1,值为hello,并且30秒后过期。 使用 ttl命令 可以查看该键还有多少时间过期。

(3)不存在设置 setnx(set if not exist) setnx key value 如果key不存在,则创建一个key,如果key存在,则创建失败并返回0。

上图中,执行第一条命令时,由于key2不存在,所以就创建一个key2,执行第二句命令时,由于key2前面已经创建了,已经存在了,所以就创建失败,并且返回0。

setnx在分布式锁中经常使用到


  • 获取值 (GET命令)

127.0.0.1:6379> set name oldou

OK

127.0.0.1:6379> get name   #获取name中的值

"oldou"


  • mset/mget命令 同时设置/获取一个或者多个键值对,主要就是批量设置和获取键值对。


  • msetnx key1 value1 key2 value2… 同时设置多个值,如果其中有一个存在,那么就都创建失败。 要么一起成功,要么一起失败,这是一个 原子性操作

  • incr命令 让当前键值以 1 的数量递增,并返回递增后的值。相当于Java中的自增,每次使用改命令都能让变量自增1。

  • 应用场景:(一般可用于设置浏览量、阅读量)


  • incrby命令 可以指定参数一次增加的数值,并返回递增后的值,(原来的num是2,加10之后变为12)


  • decr命令 可以指定参数一次递减的数值,并返回递减后的值,每次使用该命令都自减1,相当于Java中的自减。


  • decrby 可以指定参数一次递减的数值,并返回递减后的值


  • append命令 向键值的末尾追加 value。如果键不存在则将该键的值设置为 value。 返回值是追加后字符串的总长度。


  • strlen命令 :获取字符串长度


  • **getrange命令:**截去指定索引的字符串


  • setrange命令 :从指定索引开始替换字符串

  • 以上表示从索引为0开始的元素替换为QQQQQ


  • del命令 : 根据key删除一个或者多个元素


  • 设置对象 set user:1 {name:zhangsan,age:3}

  • 设置一个user:1对象,值为json字符串来保存一个对象。

  • 这是一个巧妙的设计: user:{id}:{field} 如此设置在Redis中是完全OK的。


  • getset命令 :先get然后再set 如果设置的键不存在值,则设置值,并且返回nil 如果设置的键存在值,则返回该值,并设置新的值

应用场景
  • 计数器—点赞,视频播放量,每播放一次就+1

  • 统计多单位的数量

  • 粉丝数

  • 对象缓存存储

Hash散列表

Redis 的哈希是键值对的集合。

Redis 的哈希值是字符串字段和字符串值之间的映射,因此它们被用来表示对象,还有用户信息之类的,经常变动的信息。

Hash更适合用于对象的存储,String更适合字符串存储。


常用命令
  • (1) hset命令 :存储一个哈希键值对的集合 格式为:hset key field value -----表示的是在key的field下设置一个为“value”的值。

  • (2) hget命令 :获取一个哈希键的值    格式为:hget key field

  • (3) hmset : 存储一个或多个哈希是键值对的集合

  • 格式为:hmset key field1 value1 …fieldN keyN

  • (4) hmget : 获取多个指定的键的值       

  • 格式为:hmget key field1 … fieldN


  • (5) hexists : 判断哈希表中的字段名是否存在 如果存在返回 1 否则返回 0

  • 格式为:hexists key field

  • (6) hdel : 删除一个或多个字段

  • 格式为:hdel key field

  • (7) hgetall : 获取一个哈希是键值对的集合

  • 格式为:hgetall key

  • (8) hvals : 只返回字段值

  • 格式为:hvals key

  • (9) hkeys : 只返回字段名

  • 格式为:hkeys key

  • (10) hlen : 返回 key 的 hash 的元素个数

  • 格式为:hlen key

  • 这里是因为user下有两个属性 username和userage


  • (11) hincrby key field value : 指定增量value

  • (12) hsetnx key field value : 如果该键不存在就创建,如果该键存在就创建失败。

使用场景

主要用于存储部分变更数据,比如存储用户信息等等

List链表

Redis 的链表是简单的字符串列表,排序插入顺序。您可以添加元素到 Redis 的列表的头部或尾部 Lpush:表示的是向链表的左添加,也就是向链表的头添加; Rpush:表示的是向链表的右添加,也就是向链表的尾添加;

常用命令
  • (1) lpush key value : 向链表左侧添加—头插法

  • (2) rpush key value : 向链表右侧添加–尾插法

  • (3) lpop key : 从左边移出一个元素,就是从最左边的那个节点剔除掉。

  • (4) rpop key : 从右边移出一个元素,就是从最右边的那个节点剔除掉。

  • (5) lrange key start end lrange : 命令将返回索引从 start 到 stop 之间的所有元素。Redis 的列表起始索引为 0。

  • 如果是要获取全部的元素: lrange key 0 -1

  • (6) llen key : 返回链表中元素的个数 相当于关系型数据库中 select count(*)

  • (7) lindex key indexnumber :lindex 命令用来返回指定索引的元素,索引从 0 开始,如果是负数表示从右边开始计算的索引,最右边元素的索引是-1。如果要将列表类型当做数组来用,lindex 命令是必不可少的。

  • (8) lset key indexnumber value : 是另一个通过索引操作列表的命令,它会将索引为 index的元素赋值为 value,原来的值会被覆盖。如果该列表不存在就会报错。所以使用这个命令之前先使用exists判断一下。

  • (9) lrem key count value : 移除key链表中count个元素的value值,精确匹配,如果链表中可以有多个重复的值,这里的count指的是可以删除多个相同key的值。

  • (10) ltrim list startIndex endIndex :通过下标截取指定的长度,这个时候list已经改变了,只剩下截断的元素。

  • (11) rpoplpush source destination :移除当前的source链表中的最后一个元素,并且将该元素移动到destination链表当中。

  • (12) linsert key BEFORE|AFTER pivot value :在key列表的pivot元素的前/后面插入元素value。

  • 小结:

  • 实际上list是一个链表,before node after,left ,right都可以插入值;

  • 如果key不存在,就创建新的链表;

  • 如果key存在就创建新的值;

  • 如果移除了所有的值,空链表,也就代表不存在;

  • 在两边插入或者改动值,效率最高,中间元素相对来说效率会低一点。

应用场景
  • 消息队列:利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段的api,你可以直接查询,删除List中某一段的元素。

  • 消息排队,消息队列(Lpush、Rpop)、栈(Lpush、Lpop)

  • 使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。

Set( 集合)

Redis 的集合是字符串的无序集合。

在Set集合当中,是不允许有重复的。

set是通过hash table实现的,可以进行添加、删除和查找。对集合我们可以取并集,交集,差集.

常用命令
  • (1) sadd key value :添加一个 string 元素到,key 对应的 set 集合中, 成功返回 1,如果元素已经在集合中返回 0

  • (2) scard key : 返回 set 的元素个数,如果 set 是空或者 key 不存在返回 0

  • (3) smembers key : 返回 key 对应 set 的所有元素,结果是无序的

  • (4) sismember key value : 判断 value 是否在 set 中,存在返回 1,0 表示不存在或者 key 不存在

  • (5) srem key value : 从 key 对应 set 中移除给定元素,成功返回 1,如果 value 在集合中不存在或者 key 不存在返回 0

  • (6) srandmember key nums : 从key集合中随机抽取nums个元素。

  • (7) spop key :随机删除一些key集合中的元素。

  • (8) smove source destination member :将原集合source中的member元素移动到destination集合中。


  • (9) sdiff key1 key2 :取出key1中与key2集合的不同元素,差集

  • (10) sinter key1 key2 :取key1与key2两个集合中相同的元素,交集

  • (11) sunion key1 key2 :将key1与key2两个集合中的元素合在一起,并集

应用场景
  • 微博、用户将所有关注的人都放入到一个set集合当中,将它的粉丝也放在一个集合中。

  • 共同关注、共同爱好、二度好友、QQ的好友推荐(六度分割理论)

SortedSet( 有序集合) zset

Redis 的有序集合类似于 Redis 的集合,字符串不重复的集合。

常用命令介绍
  • (1) zadd key score value : 将一个或多个 value 及其 socre 加入到 set 中

  • (2) zrange key start end :0 和-1 表示从索引为 0 的元素到最后一个元素(同 LRANGE 命令相似)

  • (3) zrange key 0 -1 withscores 也可以连同 score 一块输出,使用 WITHSCORES 参数

  • (4) zremrangebyscore key start end :可用于范围删除操作

  • (5) zrangebyscore key min max : 升序排序操作,将key按最小值到最大值进行输出。 zrevrange salary 0 -1 :这个是倒序全部输出

  • 以上是从小到大排序,也就是升序排序。

  • (6) zrevrangebyscore key max min :倒序排序操作,将key按照从大到小排序输出

  • (6) zrem key value : 删除指定的元素

  • (7) zcard key :获取有序集合中的个数

  • (8) zcount key min max : 获取指定区间的成员数量

应用场景
  • 存储班级成绩表、工资表排序

面试题常问的数据类型

1、Redis 的数据类型有哪些?
  • Redis支持五种数据类型:String(字符串)、hash(哈希)、list(列表)、set(集合)以及zsetsorted set(有序集合)。

  • 我们实际项目中比较常用的是String和hash,如果你是Redis的中高级用户,还需要加上以下几种数据类型,分别是:HyperLogLog、Geo、Pub/Sub。

  • 如果你玩过Redis Module,像BloomFilter、RedisSearch、Redis-ML,,等等,是加分项。

2、一个字符串类型的值能存储最大容量是多少?

一个字符串类型的值能存储的最大容量为512M。

3、Redis key 的过期时间和永久有效分别怎么设置?
  • 使用expire命令对key的过期时间进行设置;

  • 使用persist命令对key永久有效进行设置;

4、一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set他们最多能存放多少元素?

理论上Redis可以处理多达232个keys,并且在实际中进行了测试,每个实例至少存放了2亿5千万的keys。

任何list、set和sorted set都可以放232个元素,换句话说,Redis的存储极限是系统中的可用内存值。

5、Redis 最适合的场景?
  • (1)会话缓存(最常用的一种使用 Redis 的情景是会话缓存(session cache)。用 Redis 缓存会话比其他存储(如 Memcached)的优势在于:Redis 提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗? 幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用 Redis 来缓存会话的文档。甚至广为人知的商业平台Magento 也提供 Redis 的插件。

  • (2)全页缓存(FPC) 除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。回到一致性问题,即使重启了 Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似 PHP 本地 FPC。 再次以 Magento 为例,Magento提供一个插件来使用 Redis 作为全页缓存后端。 此外,对 WordPress 的用户来说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

  • (3)队列 Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis能作为一个很好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言(如 Python)对 list 的 push/pop 操作。 如果你快速的在 Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用 Redis 创建非常好的后端工具,以满足各种队列需求。例如,Celery 有一个后台就是使用 Redis 作为 broker,你可以从这里去查看。

  • (4)排行榜/计数器 Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的 10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可: 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行: ZRANGE user_scores 0 10 WITHSCORES Agora Games 就是一个很好的例子,用 Ruby 实现的,它的排行榜就是使用 Redis 来存储数据的,你可以在这里看到。

  • (5)发布/订阅 最后(但肯定不是最不重要的)是 Redis 的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用 Redis 的发布/订阅功能来建立聊天系统!)

6、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表。

7、如果这个Redis正在给线上的业务提供服务,那使用keys指令会有什么问题? 这个时候就要回答:Redis是单线程的,keys指令会导致线程阻塞一段时间,线上服务会停顿,知道指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取指令模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。
8、如果有大量的 key 需要设置同一时间过期,一般需要注意什么?

如果大量的key过期时间设置过于集中,那么到过期的那个时间点,Redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一点。

9、使用过 Redis 做异步队列么,你是怎么用的?

一般使用list结构作为队列,rpush生产消息,lpop消费消息。 当lpop没有消息的时候,要适当sleep一会儿再重试。

追问:可不可以不使用sleep呢?

list还有个指令叫blpop,在没有消息的时候,它会阻塞住,直到消息到来。

再追问:能不能生产一次,消费多次呢?

使用pub/sub主题订阅者模式,可以使用1:N的消息队列。

再问:pub/sub有什么缺点?

在消费者下线的情况下,生产的消息会丢失,解决这样的问题得使用专业的消息队列,如RabbitMQ等。

Redis如何实现延时队列呢?

使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangbyscore指令获取N秒之前的数据轮询进行处理。

10、使用过 Redis 分布式锁么,它是什么回事?

先拿setnx来争抢锁,抢到之后再用expire给锁加一个过期时间防止锁忘记了释放。

问:如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?

这个时候确实锁会永远得不到释放了,但是set指令有个非常复杂的参数是可以同时把setnx和expire合成一条指令来用的。

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

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

相关文章

退货通知单下推销售退货单,无法下推问题排查

文章目录 退货通知单下推销售退货单,无法下推问题排查报错界面排查原因 退货通知单下推销售退货单,无法下推问题排查 报错界面 排查 检验单已做。 原因 合格未勾选判退。

按键协管指南针加速计陀螺仪GPS等原理图纸2

1.imu电路。 加速计包含重力感应。 到传感器芯片u8, U16, U18的信息都是用的spi接口,如下图所示。OSCAR_TO_IMU_SPI_SCLK_FL, IMU_TO_OSCAR_SPI_MISO_FL, OSCAR_TO_IMU_SPI_MOSI_FL接了u8, u16, u18,通过片选信号cs选择哪个芯片接收。 加速计,陀螺仪&…

Redash 默认key漏洞(CVE-2021-41192)复现

Redash是以色列Redash公司的一套数据整合分析解决方案。该产品支持数据整合、数据可视化、查询编辑和数据共享等。 Redash 10.0.0及之前版本存在安全漏洞,攻击者可利用该漏洞来使用已知的默认值伪造会话。 1.漏洞级别 中危 2.漏洞搜索 fofa "redash"…

289. 生命游戏

根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 &am…

主播考核体系相关基础

1.主播薪资类型 2.主播考核体系 1.分为日常考核、月度考核 日常考核分为三部曲:播前、播中、播后 (1)播前 (2)播中 (3)播后 月度考核 月度考核表列举 主播等级划分要素 主播晋升考核方…

JVM篇----第六篇

系列文章目录 文章目录 系列文章目录前言一、堆(Heap-线程共享) -运行时数据区二、方法区/永久代(线程共享)三、JVM 运行时内存四、新生代前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看…

Qt6入门教程 11:父子对象关系

在上一篇中的纯手写部分,不管是创建菜单、工具栏还是状态栏,我们new完之后都未显式的调用delete进行销毁,这样难道不会有内存泄漏么? QMenuBar *menuBar new QMenuBar(this); QToolBar *toolBar new QToolBar(this); QStatusBa…

web前端之不一样的居中方式、解决tabBar选项卡居中问题、css支持嵌套、auto

MENU 前言htmlstyle效果 前言 这里不能使用justify-content: center;&#xff0c;因为在小屏幕上&#xff0c;这种方式无法显示最前面的两个tabBar。 html <div id"box" class"d_f o_a mt_50 mb_50 ml_20 mr_20"><div class"ws_n">…

【MySQL】如何通过DDL去创建和修改员工信息表

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-fmKISDBsFq74ab2Z {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

【vue】Vue2和Vue3中的代码逻辑复用对比(mixins、自定义hook):

文章目录 一、前言&#xff1a;二、mixins&#xff1a;【1】mixins是什么&#xff1f;【2】mixins如何使用&#xff1f;【3】mixins的一些特性&#xff1a;【4】mixins的缺点&#xff1a; 三、hook&#xff1a;【1】Vue3.x中的自定义hook函数是什么&#xff1f;【2】mixins和Co…

4.【SpringBoot3】文章管理接口开发

序言 在文章管理模块&#xff0c;有以下接口需要开发&#xff1a; 新增文章文章列表&#xff08;条件分页&#xff09;获取文章详情更新文章删除文章 数据库表字段和实体类属性&#xff1a; 1. 新增文章 需求分析 当用户点击左侧菜单中的“文章管理”后&#xff0c;页面主…

SpringBoot_基础

学习目标 基于SpringBoot框架的程序开发步骤 熟练使用SpringBoot配置信息修改服务器配置 基于SpringBoot的完成SSM整合项目开发 一、SpringBoot简介 1. 入门案例 问题导入 SpringMVC的HelloWord程序大家还记得吗&#xff1f; SpringBoot是由Pivotal团队提供的全新框架&…

java数据结构与算法刷题-----LeetCode697. 数组的度

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 方法一&#xff1a;hash表 此方法是工作中时间可以使用的&#xff0c;因为…

阅读go语言工具源码系列之gopacket(谷歌出品)----第一集 DLL的go封装

gopacket项目是google出品的golang第三方库&#xff0c;项目源码地址google/gopacket: Provides packet processing capabilities for Go (github.com) gopacket核心是对经典的抓包工具libpcap(linux平台)和npcap(windows平台)的go封装&#xff0c;提供了更方便的go语言操作接…

嵌入式linux学习之系统烧录

1.所需文件 1. 开发板为正点原子stm32mp157,文件可按照linux驱动教程编译&#xff0c;也可在正点原子文档->08、系统镜像\02、出厂系统镜像中找到&#xff1a; 2.烧录 1.拨码开关为000(usb启动)&#xff0c;otg接口接入虚拟机&#xff0c;打开stm32cubeProgrammer: 2.页面…

AP5101C 高压线性 LED恒流驱动器 DFN2*2 LED灯汽车雾灯转向灯

产品描述 AP5101C 是一款高压线性 LED 恒流芯片 &#xff0c; 简单 、 内置功率管 &#xff0c; 适用于6- 100V 输入的高精度降压 LED 恒流驱动芯片。电流2.0A。AP5101C 可实现内置MOS 做 2.0A,外置 MOS 可做 3.0A 的。AP5101C 内置温度保护功能 &#xff0c;温度保护点为 130 …

CQ 社区版 2.8.0 | 支持TiDB、StarRocks,新增列过滤算法、导出模式设置等

Hello&#xff0c;CloudQuery 社区版 2.8.0 已发布&#xff0c;本文将带大家详细解析本次更新的功能~&#xff08;完整的讲解视频可点击 &#x1f449;&#x1f3fb; CloudQuery 社区版2.8.0 功能讲解演示 本期亮点更新 新增支持数据源 TiDB、StarRocks数据保护新增列过滤脱敏…

cmd命令行输出的内容复制粘贴到文本中

cmd程序执行完后按任意键进行结束&#xff0c;无法直接复制命令行里输出的内容&#xff0c;如下图&#xff0c;在Windows系统里按ctrlC&#xff0c;然后该窗口就关闭了&#xff0c;内容也没有复制成功到粘贴板。 解决办法如下&#xff1a; 在上方打开设置 然后在“交互”里打…

JDBC 总结

一、JDBC概述 JDBC&#xff08;Java DataBase Connectivity&#xff09;java数据库连接是一种用于执行SQL语句的Java API&#xff0c;可以为多种关系型数据库提供统一访问&#xff0c; 它由一组用Java语言编写的类和接口组成。有了JDBC,java开发人员只需要编写一次程序,就可以…

Linux系统中虚拟文件系统原理与方法

在 Unix 的世界里&#xff0c;有句很经典的话&#xff1a;一切对象皆是文件。这句话的意思是说&#xff0c;可以将 Unix 操作系统中所有的对象都当成文件&#xff0c;然后使用操作文件的接口来操作它们。Linux 作为一个类 Unix 操作系统&#xff0c;也努力实现这个目标。 虚拟文…