提升性能300ms:深入解析Spring多表联接查询优化与SQL调优实战
优化所需知识点(必须掌握)
索引篇
explain命令
重点:这是后续分析是否使用索引以及使用是否恰当的工具
作用:查看sql的执行计划,可以看sql语句是否使用了索引,索引的使用情况,以及sql的性能。
使用方法:explain+查询语句,例如:explain select * from test
字段解释(只讲需要用到的)
字段 | 含义 | |
id | 一条sql语句表的执行顺序(id相同从上到下顺序执行。id不同,值越大越先执行) | |
type | 表示连接类型,性能有好到差连接类型为 null,system,const,eq_ref,ref,range,index,all | |
possible_key | 可能应用在这张表上的索引(一个或多个) | |
key | 实际使用的索引,如果为null,则没有使用索引 | |
Extra | 字段 | 含义 |
Using index | 性能好,不需要回表查询 | |
Using where | 性能好,不需要回表查询 | |
Using index condition | 需要回表查询 | |
Using temporary | 在查询过程中使用了临时表 | |
Using filesort | 使用了额外的排序操作来满足 ORDER BY 子句 |
索引使用原则
重点:这决定了你创建哪些索引可以提升性能
- 针对数据量大并且查询比较频繁的表建立索引
- 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
- 尽量选择区分度高的列(例如身份证)作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
- 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间, 避免回表,提高查询效率
- 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
- 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。
索引失效场景
重点:创建了索引但是失效了得不偿失
- 联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效(解决方案:使用>=、<=)
- 不要在索引列上进行运算操作, 索引将失效
- 字符串类型字段使用时,不加引号,索引将失效
- 如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
- 用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会 被用到。
优化实战
代码展示
SELECTDISTINCTu.id AS id,uicm.code,mbi.trueName AS name,FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) AS age,mbi.education,mbi.height,CASEWHEN mbi.marriage IN (2, 11, 12, 13) THEN '离异'WHEN mbi.marriage = 3 THEN '丧偶'ELSE ''END AS marriage,mbi.hometownCode,mbi.workCity,mbi.occupation,u.sex,u.gmt_create AS time,CASE WHEN uicm.status = 0 THEN 0 ELSE 1 END AS is_off,u.phone,sc.worker_id AS workerId,-- mbi.car,-- mbi.house,-- CASE-- WHEN mbi.income = 3 THEN '3K以上'-- WHEN mbi.income = 4 THEN '3K-5K'-- WHEN mbi.income = 5 THEN '5K-8K'-- WHEN mbi.income = 6 THEN '8K-12K'-- WHEN mbi.income = 7 THEN '12K-20K元'-- WHEN mbi.income = 8 THEN '20K-50K元'-- WHEN mbi.income = 9 THEN '50K以上'-- ELSE NULL-- END AS income,CASEWHEN EXISTS (SELECT 1 FROM lspace_member.sea_customer_listWHERE member_id = u.idAND tenant_id = u.tenant_idAND shop_id = u.shop_id) THEN 1ELSE 0 END as isSea,CASEWHEN EXISTS (select 1 from lspace_member.blank_member where member_id = u.id) THEN 1ELSE 0 END as isBlank,CASEWHEN (SELECT urlFROM lspace_member.member_photoWHERE member_id = u.idAND type = 16AND STATUS = 1ORDER BY id DESC LIMIT 1) IN (NULL, 'https://static.lspace.vip/images/my/boy.jpg', 'https://static.lspace.vip/images/my/girl.jpg')THEN NULLELSE (SELECT urlFROM lspace_member.member_photoWHERE member_id = u.idAND type = 16AND STATUS = 1ORDER BY id DESC LIMIT 1)END AS urlsFROMlspace_usercenter.`user` uinner join lspace_diga.user_id_code_mapping uicm on uicm.user_id=u.idINNER JOINlspace_member.memberbaseinfo mbi ON mbi.memberId = u.id<if test="memberListReq.isMyCustomer == 1">inner join lspace_member.service_case sc on sc.worker_id=#{memberListReq.workerId} and sc.member_id = u.id and sc.case_type=1</if><if test="memberListReq.isMyCustomer == 0">left join lspace_member.service_case sc on sc.member_id = u.id and sc.case_type=1</if>left JOIN lspace_member.upgrade_vip_queue uvq on uvq.member_id=u.id and uvq.status=2left JOIN lspace_member.service_period sp on sp.member_id=u.id and uvq.order_id=sp.order_idWHEREu.sex = #{memberListReq.sex}AND u.sex IS NOT NULL<if test="memberListReq.homeTowns != null and !memberListReq.homeTowns.isEmpty()">and mbi.hometownCode in<foreach item="item" index="index" collection="memberListReq.homeTowns" open="(" separator=","close=")">#{item,jdbcType=INTEGER}</foreach></if><if test="memberListReq.workCitys != null and !memberListReq.workCitys.isEmpty()">and mbi.workCity in<foreach item="item" index="index" collection="memberListReq.workCitys" open="(" separator=","close=")">#{item,jdbcType=INTEGER}</foreach></if><if test="memberListReq.minAge != null">AND FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) >= #{memberListReq.minAge}</if><if test="memberListReq.maxAge != null">AND FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) <= #{memberListReq.maxAge}</if><if test="memberListReq.text != null and memberListReq.text != ''">AND(uicm.code LIKE CONCAT('%',#{memberListReq.text}, '%')oru.phone=#{memberListReq.text})</if>group byu.idORDER BYuicm.score DESC,CASEWHEN MIN(sp.start_time) IS NOT NULL THEN MIN(sp.start_time)ELSE u.gmt_createEND DESC,u.id ASClimit #{offest},#{size}
第一步:连表优化
根据阿里巴巴的《Java 开发手册》中的 SQL 设计规范,关于 SQL 查询中涉及的表的数量并没有具体的硬性限制,但是在实践中,阿里规范建议尽量避免过多的表连接,建议连表数量小于3张。
上面这段sql主要是为了查询出符合条条件的用户。这是优化前代码,链表7张,非必要链表2张。根据业务需求,可以看看你业务需求中是不是连的表都必须连,很影响性能,能拆则拆。我根据业务分析后我发现有两张表可以拆出来,拆!
拆后代码:
SELECTDISTINCTu.id AS id,uicm.code,mbi.trueName AS name,FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) AS age,mbi.education,mbi.height,CASEWHEN mbi.marriage IN (2, 11, 12, 13) THEN '离异'WHEN mbi.marriage = 3 THEN '丧偶'ELSE ''END AS marriage,mbi.hometownCode,mbi.workCity,mbi.occupation,u.sex,u.gmt_create AS time,CASE WHEN uicm.status = 0 THEN 0 ELSE 1 END AS is_off,u.phone,sc.worker_id AS workerId,CASEWHEN (SELECT urlFROM lspace_member.member_photoWHERE member_id = u.idAND type = 16AND STATUS = 1ORDER BY id DESC LIMIT 1) IN (NULL, 'https://static.lspace.vip/images/my/boy.jpg', 'https://static.lspace.vip/images/my/girl.jpg')THEN NULLELSE (SELECT urlFROM lspace_member.member_photoWHERE member_id = u.idAND type = 16AND STATUS = 1ORDER BY id DESC LIMIT 1)END AS urlsFROMlspace_usercenter.`user` uinner join lspace_diga.user_id_code_mapping uicm on uicm.user_id=u.idINNER JOINlspace_member.memberbaseinfo mbi ON mbi.memberId = u.id<if test="memberListReq.isMyCustomer == 1">inner join lspace_member.service_case sc on sc.worker_id=#{memberListReq.workerId} and sc.member_id = u.id and sc.case_type=1</if><if test="memberListReq.isMyCustomer == 0">left join lspace_member.service_case sc on sc.member_id = u.id and sc.case_type=1</if>left JOIN lspace_member.upgrade_vip_queue uvq on uvq.member_id=u.id and uvq.status=2left JOIN lspace_member.service_period sp on sp.member_id=u.id and uvq.order_id=sp.order_idWHEREu.sex = #{memberListReq.sex}AND u.sex IS NOT NULL<if test="memberListReq.homeTowns != null and !memberListReq.homeTowns.isEmpty()">and mbi.hometownCode in<foreach item="item" index="index" collection="memberListReq.homeTowns" open="(" separator=","close=")">#{item,jdbcType=INTEGER}</foreach></if><if test="memberListReq.workCitys != null and !memberListReq.workCitys.isEmpty()">and mbi.workCity in<foreach item="item" index="index" collection="memberListReq.workCitys" open="(" separator=","close=")">#{item,jdbcType=INTEGER}</foreach></if><if test="memberListReq.minAge != null">AND FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) >= #{memberListReq.minAge}</if><if test="memberListReq.maxAge != null">AND FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) <= #{memberListReq.maxAge}</if><if test="memberListReq.text != null and memberListReq.text != ''">AND(uicm.code LIKE CONCAT('%',#{memberListReq.text}, '%')oru.phone=#{memberListReq.text})</if>group byu.idORDER BYuicm.score DESC,CASEWHEN MIN(sp.start_time) IS NOT NULL THEN MIN(sp.start_time)ELSE u.gmt_createEND DESC,u.id ASClimit #{offest},#{size}
第二步:SQL优化
在SQL语句中少做运算可提高新增,而且可以有效避免索引失效,所以尽量不要在SQL做运算。
这三种,第一种在java代码中预先计算生日后作为条件搜索,避免了函数运算和索引失效,所以做了第二步优化
1. mbi.birthday <= '2000-01-08'
2. FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) >= 25
3.mbi.birthday <= DATE_SUB(CURDATE(), INTERVAL 25 YEAR)
优化后代码:
SELECTu.id AS id,uicm.code,mbi.trueName AS name,FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) AS age,mbi.education,mbi.height,CASEWHEN mbi.marriage IN (2, 11, 12, 13) THEN '离异'WHEN mbi.marriage = 3 THEN '丧偶'ELSE ''END AS marriage,mbi.hometownCode,mbi.workCity,mbi.occupation,u.sex,u.gmt_create AS time,CASE WHEN uicm.status = 0 THEN 0 ELSE 1 END AS is_off,u.phone,sc.worker_id AS workerId,CASEWHEN (SELECT urlFROM lspace_member.member_photoWHERE member_id = u.idAND type = 16AND STATUS = 1ORDER BY id DESC LIMIT 1) IN (NULL, 'https://static.lspace.vip/images/my/boy.jpg', 'https://static.lspace.vip/images/my/girl.jpg')THEN NULLELSE (SELECT urlFROM lspace_member.member_photoWHERE member_id = u.idAND type = 16AND STATUS = 1ORDER BY id DESC LIMIT 1)END AS urlsFROMlspace_usercenter.`user` uinner join lspace_diga.user_id_code_mapping uicm on uicm.user_id=u.idINNER JOINlspace_member.memberbaseinfo mbi ON mbi.memberId = u.id<if test="memberListReq.isMyCustomer == 1">inner join lspace_member.service_case sc on sc.worker_id=#{memberListReq.workerId} and sc.member_id = u.id and sc.case_type=1</if><if test="memberListReq.isMyCustomer == 0">left join lspace_member.service_case sc on sc.member_id = u.id and sc.case_type=1</if>left JOIN lspace_member.upgrade_vip_queue uvq on uvq.member_id=u.id and uvq.status=2left JOIN lspace_member.service_period sp on sp.member_id=u.id and uvq.order_id=sp.order_idWHEREu.sex = #{memberListReq.sex}AND u.sex IS NOT NULL<if test="memberListReq.homeTowns != null and !memberListReq.homeTowns.isEmpty()">and mbi.hometownCode in<foreach item="item" index="index" collection="memberListReq.homeTowns" open="(" separator=","close=")">#{item,jdbcType=INTEGER}</foreach></if><if test="memberListReq.workCitys != null and !memberListReq.workCitys.isEmpty()">and mbi.workCity in<foreach item="item" index="index" collection="memberListReq.workCitys" open="(" separator=","close=")">#{item,jdbcType=INTEGER}</foreach></if><if test="memberListReq.minBirthday != null and memberListReq.minAge!=0">AND mbi.birthday <= #{memberListReq.minBirthday}</if><if test="memberListReq.maxBirthday != null">AND mbi.birthday >= #{memberListReq.maxBirthday}</if>
<!-- <if test="memberListReq.minAge != null">-->
<!-- AND FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) >= #{memberListReq.minAge}-->
<!-- </if>-->
<!-- <if test="memberListReq.maxAge != null">-->
<!-- AND FLOOR(DATEDIFF(CURDATE(), mbi.birthday) / 365.25) <= #{memberListReq.maxAge}-->
<!-- </if>--><if test="memberListReq.text != null and memberListReq.text != ''">AND(uicm.code LIKE CONCAT('%',#{memberListReq.text}, '%')oru.phone=#{memberListReq.text})</if>group byu.idORDER BYuicm.score DESC,MIN(sp.start_time) DESC,u.gmt_create desc,u.id ASClimit #{offest},#{size}
第三步:加索引
根据索引篇的知识点,在区分度高,数据列多,条件搜索,排序这个上面创建索引。创建后使用explain命令逐一分析,创建的索引是否有效,是否被使用。在进行调整
根据我的业务需求我添加了3个索引来满足我的业务需求
CREATE INDEX idx_workCity ON lspace_member.memberbaseinfo (workCity);
CREATE INDEX idx_hometown ON lspace_member.memberbaseinfo (hometownCode);CREATE INDEX idx_code ON lspace_diga.user_id_code_mapping (code);
第四步:java代码优化
根据感觉简单优化了一下代码,对效率其实影响不太大。大家也自我感觉优化一下吧,特别涉及是for循环,for循环中有查询的重点关注一下!!!
总结
根据以上方式速度快了将近一半,300ms左右。主要也是对SQL的优化,合理的使用索引,恰当的链表方式可以大大提高性能。
相关文章:

提升性能300ms:深入解析Spring多表联接查询优化与SQL调优实战
优化所需知识点(必须掌握) 索引篇 explain命令 重点:这是后续分析是否使用索引以及使用是否恰当的工具 作用:查看sql的执行计划,可以看sql语句是否使用了索引,索引的使用情况,以及sql的性能。 …...
增量导入和全量导入的区别是什么?
定义 全量导入:是指将数据源中的所有数据一次性全部导入到目标系统中。例如,一个电商公司要将其旧数据库中的所有商品信息(包括商品名称、价格、库存等)全部迁移到新的数据库系统中,这个过程就是全量导入。这种方式会覆…...

【百度智能云客悦智能客服】搭建AI agent智能对话 - 购车推荐
前期准备 平台链接:https://keyue.cloud.baidu.com/ 一、开始创建 二、会话流程配置 我们以购车推荐的案例,来进行 AI agent 配置演示 1.添加开场白 在 起始主题 画布中,我们可以配置 AI agent 的开场白,画布左侧默认有 开始 …...
【HTML+CSS+JS+VUE】web前端教程-3-标题标签
标题介绍与应用 标题是通过<h1>-<h6>标签进行定义的 <h1>定义最大的标题 <h6>定义最小的标题<h1...
逐笔成交逐笔委托Level2高频数据下载和分析:20250102
level2逐笔成交逐笔委托下载 链接: https://pan.baidu.com/s/1p7OOj5p-QGFrWkt6KKoYng?pwd7f4g 提取码: 7f4g Level2逐笔成交逐笔委托数据分享下载 通过Level2逐笔成交和逐笔委托这种每一笔的毫秒级别的数据可以分析出很多有用的点,包括主力意图,虚假动…...

JavaEE之线程池
前面我们了解了多个任务可以通过创建多个线程去处理,达到节约时间的效果,但是每一次的线程创建和销毁也是会消耗计算机资源的,那么我们是否可以将线程进阶一下,让消耗计算机的资源尽可能缩小呢?线程池可以达到此效果&a…...
java 中 main 方法使用 KafkaConsumer 拉取 kafka 消息如何禁止输出 debug 日志
pom 依赖: <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId><version>2.5.14.RELEASE</version> </dependency> 或者 <dependency><groupId>org.ap…...
【后端面试总结】Golang可能的内存泄漏场景及应对策略
Golang可能的内存泄漏场景及应对策略 一、引言 Golang作为一种高性能、并发友好的编程语言,其内置的垃圾回收机制极大地简化了内存管理。然而,这并不意味着开发者可以完全忽视内存泄漏问题。在实际开发中,由于不当的资源管理、循环引用、以…...
Java 反射机制详解
在 Java 编程世界中,反射机制犹如一把神奇的钥匙,它能够打开许多隐藏在代码深处的 “大门”,让开发者突破常规的限制,实现一些极具灵活性的功能。今天,就跟随我一同深入探究 Java 反射机制的奥秘。 一、什么是反射 反…...

【k8s】scc权限 restricted、anyuid、privileged
文章目录 概述1. 内置的scc2. OpenShift如何确定pod的scc2.1 Pod未带SCC标签的情况2.2. Pod带有SCC标签的情况 参考 概述 在OpenShift(后文简称OCP)中,很早就一个概念:Security Context Constraints ,简称SCC…...

2025华数杯国际赛A题完整论文讲解(含每一问python代码+数据+可视化图)
大家好呀,从发布赛题一直到现在,总算完成了2025“华数杯”国际大学生数学建模竞赛A题Can He Swim Faster的完整的成品论文。 本论文可以保证原创,保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文…...
ThreadLocal 的使用场景
在现代电商平台中,ThreadLocal 常用于以下场景,特别是与线程隔离相关的业务中,以提高性能和简化上下文传递。 1. 用户上下文信息管理 场景:在用户发起的每次请求中,需要携带用户 ID、角色、权限等信息,而这…...
后端开发 Springboot整合Redis Spring Data Redis 模板
目录 redis 配置 RedisConfig 类 完整代码 代码讲解 1. 类定义和注解 2. 定义 RedisTemplate Bean 3. 配置 JSON 序列化 4. 配置 Redis 的 key 和 value 序列化方式 5. 完成配置并返回 RedisTemplate 总结 redis 服务接口实现类 类级别 注入 RedisTemplate 常用 Re…...

代码随想录算法训练营第 4 天(链表 2)| 24. 两两交换链表中的节点19.删除链表的倒数第N个节点 -
一、24. 两两交换链表中的节点 题目:24. 两两交换链表中的节点 - 力扣(LeetCode) 视频:帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点_哔哩哔哩_bilibili 讲解:代码随想录 dummy-…...

【RDMA学习笔记】1:RDMA(Remote Direct Memory Access)介绍
从帝国理工的PPT学习。 什么是RDMA Remote Direct Memory Access,也就是Remote的DMA,是一种硬件机制,能直接访问远端结点的内存,而不需要处理器介入。 其中: Remote:跨node进行数据传输Directÿ…...
网络安全常见的35个安全框架及模型
大家读完觉得有帮助记得关注和点赞!!! 01、概述 网络安全专业机构制定的一套标准、准则和程序,旨在帮助组织了解和管理面临的网络安全风险。优秀的安全框架及模型应该为用户提供一种可靠方法,帮助其实现网络安全建设…...

Elasticsearch介绍及使用
Elasticsearch 是一款基于 Lucene 库构建的开源、分布式、RESTful 风格的搜索引擎和分析引擎,具有强大的全文搜索、数据分析、机器学习等功能,广泛应用于日志分析、实时数据分析、全文检索等场景。 核心概念 索引(Index)…...

Leetocde516. 最长回文子序列 动态规划
原题链接:Leetocde516. 最长回文子序列 class Solution { public:int longestPalindromeSubseq(string s) {int n s.size();vector<vector<int>> dp(n, vector<int>(n, 1));for (int i 0; i < n; i) {dp[i][i] 1;if (i 1 < n &&…...

iOS 逆向学习 - Inter-Process Communication:进程间通信
iOS 逆向学习 - Inter-Process Communication:进程间通信 一、进程间通信概要二、iOS 进程间通信机制详解1. URL Schemes2. Pasteboard3. App Groups 和 Shared Containers4. XPC Services 三、不同进程间通信机制的差异四、总结 一、进程间通信概要 进程间通信&am…...

高级生化大纲
一,蛋白质化学: 蛋白质分离是生物化学和分子生物学研究中的一项基本技术,用于根据蛋白质的物理和化学特性将其从混合物中分离出来。 1. 离心分离法 离心分离法利用离心力来分离不同质量或密度的颗粒和分子。 差速离心:通过逐…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...

相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...

EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...