Redis技术解析

引言

在Java高级开发的道路上,对Redis的掌握是必不可少的一环。Redis,作为一款开源的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。本文将深入探讨Redis的核心技术,并结合Java开发环境,分享一些实用的应用技巧和最佳实践。

一、Redis基础介绍

Redis支持多种数据类型,常见的5种包括String(字符串)、Hash(哈希)、List(列表)、Set(集合)和 Zset(Sorted Set,有序集合),这些数据类型为开发者提供了丰富的操作接口。同时,Redis提供了丰富的命令,支持数据的增删改查、过期设置、持久化等功能。
以下是 Redis 的一些数据类型,以及其及其常用使用场景:

  1. String(字符串)

    • 数据类型描述:Redis 的 String 类型是一种最基本的数据类型,它是一个键值对的存储结构,其中键和值都是字符串类型。String 类型的特点是快速存储和读取,适用于存储一些简单的数据,如字符串、整数或浮点数等。
    • 使用场景
      • 缓存:由于 String 类型具有快速读写特性,因此常用于缓存用户会话信息、页面缓存等。
      • 计数器:由于 String 类型的 value 可以是数字,所以可以通过 INCR、DECR 等命令实现计数器功能,如分布式环境中统计系统的在线人数、秒杀等。
      • 存储图片、视频等二进制数据:由于 String 类型支持二进制安全,可以用来存放图片、视频等内容。
  2. Hash(哈希)

    • 数据类型描述:Hash 类型是一种键值对的存储结构,其中键是字符串类型,而值是一个哈希表,可以包含多个键值对。
    • 使用场景
      • 对象缓存:Hash 类型可以方便地存储对象信息,如用户信息、商品信息等。通过 hmget、hset 等命令可以实现对象的读取和修改。
      • 购物车实现:Hash 类型可以方便地实现购物车功能,如添加商品、修改商品数量、删除商品等。
  3. List(列表)

    • 数据类型描述:List 类型是一个双向链表,支持在链表的两端插入和删除元素。
    • 使用场景
      • 消息队列:List 类型可以实现简单的消息队列功能,如使用 lpush 在队列左侧插入元素,使用 rpop 从队列右侧取出元素。
      • 朋友圈时间线:可以将用户发布的朋友圈内容按时间顺序存储在 List 中,实现时间线功能。
  4. Set(集合)

    • 数据类型描述:Set 类型是一个无序的字符串集合,不允许有重复元素。
    • 使用场景
      • 用户标签:可以将用户的标签存储在一个 Set 中,方便查询用户的所有标签。
      • 交集、并集、差集运算:Set 类型支持交集、并集、差集等集合运算,可以用于实现共同好友、推荐好友等功能。
  5. Zset(Sorted Set,有序集合)

    • 数据类型描述:Zset 类型是一个有序的字符串集合,每个元素都会关联一个分数(double 类型),通过分数来为集合中的元素进行从小到大的排序。
    • 使用场景
      • 排行榜:Zset 类型可以很方便地实现排行榜功能,如按照用户积分进行排序的排行榜。
      • 延迟队列:可以将需要延迟处理的任务按照处理时间戳作为分数存储在 Zset 中,然后定时从 Zset 中取出最早需要处理的任务进行处理。
      • 范围查询:Zset 可以按照分数范围进行查询,这在一些需要范围查询的场景中非常有用。
  6. Bitmap(位图)

    • 并不是 Redis 的一个独立数据类型,但可以使用 String 类型来模拟位图操作。
    • 用于存储大量的开关状态信息,如用户是否访问过某个页面。
  7. HyperLogLog

    • 用于基数统计的算法。
    • 在不存储具体元素的情况下,估计一个集合中不重复元素的数量。
  8. Geo(地理位置)

    • 用于存储地理位置信息。
    • 支持基于位置的查询,如获取附近的地点。
  9. Stream(流)

    • Redis 5.0 版本新增加的数据结构。
    • 用于实现消息队列或日志系统,支持消费者组(consumer group)模式。

以上就是 Redis 的各种数据类型及其常用使用场景。需要注意的是,这些使用场景并不是绝对的,具体使用哪种数据类型还需要根据具体的业务需求和场景来选择。

二、Redis在Java中的应用

1. 作为缓存层

Redis的高性能和低延迟特性使其成为缓存层的理想选择。在Java应用中,我们可以使用Jedis或Lettuce等Redis客户端库来连接和操作Redis。通过将热点数据存储在Redis中,可以大大减少对数据库的访问,提高系统的整体性能。

实现细节:
  • 使用Jedis或Lettuce:Jedis和Lettuce是Java中常用的Redis客户端库。它们提供了丰富的API来连接和操作Redis。
// 使用Jedis
Jedis jedis = new Jedis("localhost");
System.out.println("Connection to server successfully");
// set key-value
jedis.set("runoobkey", "www.runoob.com");
// get the value
String strValue = jedis.get("runoobkey");
System.out.println("Stored string in redis:: " + strValue);
  • 缓存策略:设计合适的缓存策略,如LRU(最近最少使用)或LFU(最不经常使用)策略,来管理缓存的容量和淘汰机制。
  • 缓存失效:设置键的过期时间,以确保缓存数据的有效性。
jedis.expire("runoobkey", 60); // 设置key的过期时间为60秒

2. 分布式锁

Redis的setnx命令(在Redis 2.6.12及以后版本中推荐使用set命令的nx和px选项)可以实现分布式锁的功能。通过Redis的分布式锁,我们可以保证在分布式环境下对共享资源的互斥访问,避免数据的不一致。

实现细节:
  • 使用SETNX或SET命令的NX和PX选项:确保在分布式环境中对共享资源的互斥访问。
String lockKey = "mylock";
String requestId = UUID.randomUUID().toString();
String result = jedis.set(lockKey, requestId, "NX", "PX", 10000); // 锁10秒
if ("OK".equals(result)) {try {// 业务逻辑处理} finally {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) " +"else " +"return 0 " +"end";jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));}
}

3. 消息队列

Redis的List数据类型可以实现简单的消息队列功能。生产者可以使用lpush命令将消息推送到队列中,消费者使用rpop或brpop命令从队列中取出消息进行处理。这种基于Redis的消息队列实现方式轻量级、易部署,适用于一些对性能要求不高的场景。

实现细节:
  • 使用List作为队列:LPUSH命令将消息推送到队列,RPOP或BRPOP命令从队列中取出消息。
// 生产者
jedis.lpush("mylist", "message1");
jedis.lpush("mylist", "message2");// 消费者
List<String> list = jedis.brpop(0, "mylist");
System.out.println(list.get(1)); // 输出 message1 或 message2

4. 计数器

Redis的String数据类型支持incr和decr命令,可以实现计数器功能。这种计数器可以在分布式环境下使用,具有高性能和可扩展性。在Java应用中,我们可以使用Redis的计数器来实现如用户访问量统计、限流等功能。

实现细节:
  • 使用INCR或DECR命令:实现高性能的计数器功能。
jedis.incr("mycounter"); // 计数器加1
long count = jedis.get("mycounter").toLong(); // 获取计数器的值

5. 发布/订阅模式

Redis的发布/订阅模式是一种消息通信模式,发送者(发布者)发送消息到频道(channel),订阅了该频道的接收者(订阅者)都可以收到消息。

实现细节:
  • 发布消息:使用PUBLISH命令将消息发布到指定的频道。
  • 订阅频道:使用SUBSCRIBE命令订阅一个或多个频道。

在Java中,可以使用Jedis的PubSub接口来实现发布/订阅模式。

Jedis jedis = new Jedis("localhost");// 订阅者
jedis.subscribe(new JedisPubSub() {@Overridepublic void onMessage(String channel, String message) {System.out.println("Received <" + message + "> on " + channel + "!");}// ...其他回调方法...
}, "mychannel");// 在另一个线程或进程中
// 发布者
jedis.publish("mychannel", "Hello, Redis!");

三、Redis高级特性

1. 持久化

Redis支持RDB和AOF两种持久化方式。RDB通过定期将内存中的数据以快照的方式写入磁盘,实现数据的持久化。AOF则将写命令追加到文件中,每次修改数据都会记录。开发者可以根据实际需求选择合适的持久化方式。
redis.conf配置文件中,可以配置RDB和AOF的相关参数。

2. 复制与集群

Redis支持主从复制和集群模式。通过主从复制,我们可以将数据从主服务器同步到从服务器,实现数据的备份和读取扩展。
在构建 Redis 的高可用方案时,通常采用以下几种常见的方法:

1. Redis Sentinel

Redis Sentinel 是 Redis 官方提供的用于监控和管理 Redis 主从复制的工具。它可以监控 Redis 实例的健康状况,并在主节点失效时自动进行主从切换,确保系统的高可用性。Redis Sentinel 通常采用三个或更多个 Sentinel 节点组成一个监控集群,通过投票来决定故障转移的结果。

2. Redis Cluster

Redis Cluster 是 Redis 官方提供的分布式 Redis 解决方案,它支持数据自动分片和数据复制,可以在多个节点之间分配数据,并提供故障转移和数据重新分片的功能。Redis Cluster 适用于大规模的 Redis 集群部署,可以提供更高的可用性和扩展性。

3. Redis Sentinel + Redis Cluster

将 Redis Sentinel 和 Redis Cluster 结合起来使用,可以提供更高级的高可用解决方案。在这种方案中,Redis Sentinel 负责监控和管理 Redis 实例的健康状态,并在必要时进行故障转移,而 Redis Cluster 则负责分布式数据存储和故障恢复。

4. Redisson

Redisson 是一个基于 Redis 的 Java 客户端库,提供了丰富的功能和高级特性,包括分布式锁、分布式对象、分布式集合等。通过 Redisson,可以实现更加灵活和可靠的高可用方案,例如基于 Redlock 算法的分布式锁,以及基于 Redis 主从复制的读写分离等。

3. 事务

Redis支持MULTI/EXEC命令来实现事务功能。在事务中,一系列命令会被打包成一个单独的单元进行执行,要么全部成功,要么全部失败。这种事务机制可以确保数据的一致性。

  • 使用MULTI、EXEC、DISCARD等命令来实现Redis的事务功能。在MULTI命令之后执行的命令会被放入一个队列中,直到EXEC命令被执行时,队列中的所有命令才会被原子性地执行。如果在执行过程中发生错误,可以使用DISCARD命令来取消事务。

四、最佳实践

1. 合理设置键的过期时间

通过合理设置键的过期时间,我们可以有效地控制Redis的内存使用,避免无效数据的堆积。同时,过期时间也可以作为缓存失效的一种机制,确保数据的实时性。

2. 监控与调优

对Redis进行监控和调优是确保系统稳定运行的关键。我们可以使用Redis自带的INFO命令或第三方监控工具来获取Redis的运行状态信息,如内存使用情况、连接数、命令执行时间等。根据这些信息,我们可以对Redis进行调优,提高系统的性能和稳定性。

3. 安全配置

Redis默认监听在6379端口上,并且没有设置密码认证。因此,在部署Redis时,我们需要配置合理的安全策略,如设置密码认证、绑定IP地址等,确保Redis的安全性。

4. 缓存穿透

定义:缓存穿透是指查询一个不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

解决方案

  1. 缓存空对象:当一个key不存在或者查询结果为空时,仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
  2. 布隆过滤器:布隆过滤器是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

5. 缓存击穿

定义:缓存击穿是指某个热点key非常热门,访问非常频繁,但这个key在缓存中不存在,导致每次请求都要去数据库查询,从而给数据库带来巨大的压力。

解决方案

  1. 使用互斥锁:当缓存失效时,不是立即去加载数据,而是先使用缓存工具的某些带成功操作返回值的操作(如Redis的setnx)去设置一个互斥锁,当操作返回成功时,再进行加载数据的操作并回设缓存;否则,就重试获取缓存。
  2. 热点数据预加载:在启动服务的时候,预先加载热点数据到缓存中,这样即使热点数据过期,也只是第一次请求会查询数据库,后续请求依然可以从缓存中获取。

6. 缓存雪崩

定义:缓存雪崩是指当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统(比如数据库)带来很大压力,甚至造成数据库宕机。

解决方案

  1. 缓存数据的过期时间设置随机:给缓存设置过期时间时,加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在某一时刻同时失效。
  2. 限流降级:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  3. 二级缓存:A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2缓存失效时间设置为长期。
  4. 预先设置热门数据:在Redis启动的时候,预先将一些热点数据加载到缓存中,这样即使这些数据的缓存失效,也能从Redis中获取到数据,而不是直接查询数据库。

五、缓存淘汰策略

Redis 提供了多种缓存淘汰策略,这些策略决定了当 Redis 内存使用达到其 maxmemory 设置的上限时,如何选择和删除数据以释放内存。以下是 Redis 中常见的缓存淘汰算法:

  1. volatile-lru

    • 尝试淘汰设置了过期时间的 key。
    • 当 Redis 内存使用到达阈值的时候,会选择最近最少使用(Least Recently Used, LRU)的且设置了过期时间的 key 进行淘汰。
    • 没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不丢失。
  2. allkeys-lru

    • 淘汰整个数据集(包括设置了过期时间和未设置过期时间的 key)中最近最少使用的 key。
    • 不考虑 key 是否设置了过期时间,只根据访问频率来决定哪些 key 需要被淘汰。
  3. volatile-lfu

    • 尝试淘汰设置了过期时间的 key,但淘汰的依据是最少使用频率(Least Frequently Used, LFU)。
    • 类似于 volatile-lru,但使用了 LFU 算法而非 LRU 算法。
  4. allkeys-lfu

    • 淘汰整个数据集中最少使用频率的 key,无论它们是否设置了过期时间。
    • LFU 算法考虑了 key 的访问频率,而不仅仅是它们最后一次被访问的时间。
  5. volatile-random

    • 在设置了过期时间的 key 中随机选择 key 进行淘汰。
    • 这种策略不基于访问频率或最近访问时间,而是随机选择 key 进行淘汰。
  6. allkeys-random

    • 在整个数据集中随机选择 key 进行淘汰,无论它们是否设置了过期时间。
    • 同样,这种策略也不基于访问频率或最近访问时间。
  7. volatile-ttl

    • 淘汰设置了过期时间且剩余时间(TTL)最短的 key。
    • 这种策略基于 key 的剩余过期时间来决定哪些 key 需要被淘汰。
  8. noeviction

    • 不淘汰任何 key,而是返回一个错误。
    • 当内存使用达到 maxmemory 设置的上限时,如果尝试添加新的数据,Redis 将返回错误而不是淘汰任何现有的 key。这可能导致新写入的命令失败,但不会丢失现有数据。

在选择合适的淘汰策略时,需要考虑你的应用的需求和特性。例如,如果你的应用中的 key 都有明确的过期时间,并且你希望优先淘汰那些最久未使用的 key,那么 volatile-lru 可能是一个好的选择。如果你的应用需要快速响应并且不关心数据的持久性,那么 noeviction 策略可能不适合你,因为它会导致新写入的命令失败。

六、总结

Redis作为一款高性能的内存数据结构存储系统,在Java高级开发中发挥着重要的作用。通过掌握Redis的核心技术和应用技巧,我们可以更好地利用Redis来优化系统的性能、提高数据的实时性和一致性。

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

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

相关文章

FMEA助力医疗设备研发制造:领跑未来,实现弯道超车!

医疗设备作为保障人类健康的重要工具&#xff0c;其研发与制造水平直接关系到医疗技术的进步。然而&#xff0c;在激烈的市场竞争中&#xff0c;如何能够让自家医疗设备研发制造实现弯道超车&#xff0c;成为行业佼佼者&#xff1f;答案就在于——FMEA&#xff08;失效模式与影…

go方法定义

方法定义 Golang 方法总是绑定对象实例&#xff0c;并隐式将实例作为第一实参 (receiver)。 只能为当前包内命名类型定义方法。 参数 receiver 可任意命名。如方法中未曾使用 &#xff0c;可省略参数名。 参数 receiver 类型可以是 T 或 *T。基类型 T 不能是接口或指针。 不…

试用NXP官方的UDS bootloader

文章目录 1.前言2.资料获取2.1 MCU例程 2.2 开发环境2.3 上位机2.4 硬件 3.工程修改3.1 boot工程修改 3.2 app工程修改4.测试情况5.例程分享 1.前言 最近很多客户在开发S32K系列MCU时咨询是否可以提供基于UDS协议的bootloader。本文以S32K144为例&#xff0c;介绍如何使用NXP官…

qt基础类型转换

uchar*与QByteArray类型 uchar转为QByteArray QByteArray array; unsigned char buf ; arrayQByteArray::fromRawData((char)buf,sizeof(buf)); QByteArray转为uchar //一次性转换 buf reinterpret_cast<unsigned char*>(array.data()); //单个数据转换 (unsigned cha…

【Mybatis操作数据库】入门(一)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【MyBatis框架】 本专栏旨在分享MyBatis框架的学习笔记&#xff0c;如有错误定当洗耳恭听&#xff0c;欢迎大家在评论区交流讨论&#x1f…

NGINX App Protect现已支持NGINX开源版 全方位加强现代应用安全防护

近日&#xff0c;F5 NGINX 发布全新升级的NGINX App Protect 5.0版本&#xff0c;将先前专属于NGINX 商业版本NGINX Plus 的现代应用安全能力拓展至NGINX开源版中&#xff0c;为增强现代应用和API安全防护提供全方位支持。此次升级后&#xff0c;适用于云端及本地部署的NGINX A…

软考中级之数据库系统工程师笔记总结(六)多媒体基础

作者&#xff1a;Maynor 博客之星大数据领域Top1,GitHub项目awesome-chatgpt-project作者, 大厂程序员, 全网技术矩阵粉丝7w 公众号&#xff1a;Maynor996&#x1f4e2;博客主页&#xff1a;https://manor.blog.csdn.net &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &am…

基于TL431的线性可调恒压恒流电源的Multisim电路仿真设计

1、线性电源的工作原理 在我们日常应用里&#xff0c;直流电是从市电或电网中的交流电获取的。例如15V直流电压源、24V直流电压源等等。交流电变为直流电的过程大概分为一下几步&#xff1a; 首先&#xff0c;交流电通过变压器降低其电压幅值。接着&#xff0c;经过整流电路进…

【jetson】研华 jetson orin 开发套件系统镜像下载链接、刷入镜像时修改默认用户名的方法

1. 系统镜像下载链接 orin nano 8G&#xff1a;https://pan.baidu.com/s/1iE6ES5yV8CQ85HU0O34mHg?pwdmoyk orin nx 8G&#xff1a;https://pan.baidu.com/s/1F96NN1Hy0meNegzuqW-7mA?pwdqojr orin nx 16G&#xff1a;https://pan.baidu.com/s/1w0Gukoi9Ld-8wZLKQuhcgQ?pwd…

韩顺平0基础学Java——第6天

p87-p109 运算符&#xff08;第四章&#xff09; 四种进制 二进制用0b或0B开头 十进制略 八进制用0开头 十六进制0x或0X开头&#xff0c;其中的A—F不区分大小写 10转2&#xff1a;将这个数不断除以2&#xff0c;直到商为0&#xff0c;然后把每步得到的余数倒过来&#…

储能液冷系统中的管路介绍

储能液冷系统中管路占比约10%。储能液冷管路通过向系统中导入冷却剂&#xff0c;使产热元件与冷却剂进行换热&#xff0c;有效提高能源转化效率&#xff0c;并维持系统的温度稳定。 近年来液冷储能市场的兴起&#xff0c;也为尼龙管路打开了新的市场。储能液冷循环管路按照作用…

【GaussTech速递】数据库技术解读之细粒度资源管控

背景 对数据库集群内资源管控与资源隔离一直是企业客户长久以来的诉求。华为云GaussDB作为一款企业级分布式数据库&#xff0c;一直致力于满足企业对大型数据库集群的管理需要。 数据库可以管理的资源有计算资源与存储资源&#xff0c;计算资源包括CPU、内存、IO与网络&#…

【AI大模型】AI大模型热门关键词解析与核心概念入门

&#x1f680; 作者 &#xff1a;“大数据小禅” &#x1f680; 文章简介 &#xff1a;本专栏后续将持续更新大模型相关文章&#xff0c;从开发到微调到应用&#xff0c;需要下载好的模型包可私。 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 目…

【大模型赋能开发者】海云安入选数世咨询LLM驱动数字安全2024——AI安全系列报告

近日&#xff0c;国内知名数字产业领域第三方调研咨询机构数世咨询发布了LLM驱动数字安全2024——AI安全系列报告。报告通过调研、公开信息收集等方式对目前十余家已具备LLM相关的应用能力安全厂商对比分析出了这一领域当前的产业现状并进行了各厂商的能力展示。 海云安凭借近…

通过网络服务远程调用NVIDIA RTX系列GPU资源

摘要&#xff1a; 随着计算需求的增加&#xff0c;利用GPU进行计算加速已成为提高效率的关键。NVIDIA RTX系列GPU因其出色的性能被广泛用于各种计算密集型任务。本文提供了一套详细的指南&#xff0c;介绍如何在Windows和Linux服务器上通过网络服务远程调用RTX GPU资源&#xf…

C# SolidWorks 二次开发 -从零开始创建一个插件(3) 发布插件

五一节过完了吧&#xff0c;该上班学习了吧&#xff1f; 如何把自己开发好的程序优雅的给别人使用。 今天我们来简单讲解一下&#xff0c;这个之前不少粉丝咨询过相关问题&#xff0c;自己开发好的东西&#xff0c;如何给同事或者其它人使用。 先列一下使用到的主要工具&am…

使用.NET8实现Web API

目录 1、环境准备1.1、从官网下载及安装VS2022社区版1.2、下载及安装asp.net core的运行时及IIS Module 2、WebAPI工程创建2.2 推荐的库2.2.1 数据库篇2.2.1.1、 SQLSugar2.2.1.2、 OracleAccess 2.2.2、IOC篇2.2.2.1、autofac2.2.2.2、 2.2.3、日志记录篇2.2.3.1 log4net2.2.3…

C++ 智能指针使用

智能指针是C中用于自动管理动态分配内存的机制&#xff0c;它们通过确保在适当的时候删除对象来避免内存泄漏。 在C11及以后的版本中&#xff0c;有三种主要的智能指针&#xff1a;std::unique_ptr、std::shared_ptr和std::weak_ptr。 以下是这些智能指针之间的区别、优缺点以…

Ubuntu 20.04 安装 Ansible

使用官方的 Ubuntu PPA 更新包列表&#xff1a; apt update安装软件属性常用命令 apt install software-properties-common添加 Ansible PPA 到系统&#xff1a; add-apt-repository --yes --update ppa:ansible/ansible再次更新包列表以包括新添加的 PPA&#xff1a; apt …

【Linux】Centos7安装部署unimrcp,搭建MRCP服务器

yum install libtool yum install libtool-ltdl-devel yum install libsofia-sip-ua find / -name libsofia-sip-ua.so.0 2>/dev/null # 设置环境变量&#xff1a;如果库文件存在但不在默认搜索路径中&#xff0c;你可以通过设置 LD_LIBRARY_PATH 环境变量来告诉系统在哪…