MySQL 8 SQL调优实战:手把手教你读懂执行计划 (EXPLAIN) 与性能分析

兄弟们,咱们搞技术的,特别是和数据库打交道的,有没有过这种经历?

平时在开发环境写代码,数据量就几百条,那SQL写得叫一个“行云流水”,各种SELECT *,各种LEFT JOIN连得飞起,跑起来也是嗖嗖的。结果一上线,真实数据量一上来,刚开始还好,过了一个月,突然有一天半夜,监控群炸了:CPU 飚到 100%,应用卡死,连接池爆满

这时候老板站在你背后,眼神犀利地盯着屏幕,你冷汗直流,手忙脚乱地打开数据库客户端,除了机械式地给每个字段加索引,是不是脑子里一片空白?

一定要记住老哥这句话:你会写SQL,但不代表你懂SQL。

在 MySQL 8 的时代,如果你不懂执行计划 (EXPLAIN),那你就是蒙着眼睛在高速公路上狂奔的 CRUD Boy,撞墙是早晚的事。而一旦你掌握了它,你就是拥有了“上帝视角”的架构师,每一个慢查询在你眼里都是“裸奔”的。

今天,老哥我就结合RHEL 8 + MySQL 8.0环境,从最基础的字段解析,到 MySQL 8 独有的EXPLAIN ANALYZE大杀器,带你通盘掌握 SQL 调优的硬核技能。哪怕你是刚入行的新手,跟着我把这篇文章啃完,也能把 1 年经验用出 3 年的效果!

1 环境与数据准备 (不仅要看,还要练)

光说不练假把式。为了让大家能看到真实的优化效果,优化器的“脾气”只有在数据量足够大时才能摸得准。咱们先来构造一个电商核心业务场景。

环境:CentOS/RHEL 8 + MySQL 8.0.22 以上版本。

建表脚本

我们创建两张表:sys_orders(订单表)和sys_order_detail(明细表)。

CREATE DATABASE IF NOT EXISTS shop_core; USE shop_core; -- 订单主表 CREATE TABLE `sys_orders` ( `order_id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '订单ID', `user_id` VARCHAR(32) NOT NULL COMMENT '用户ID (注意是varchar)', `order_no` VARCHAR(64) NOT NULL COMMENT '订单编号', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-待支付,2-已支付,3-发货,4-完成', `total_amount` DECIMAL(10,2) NOT NULL COMMENT '总金额', PRIMARY KEY (`order_id`), KEY `idx_user_status` (`user_id`, `status`), -- 联合索引 KEY `idx_create_time` (`create_time`) -- 时间索引 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表'; -- 订单明细表 CREATE TABLE `sys_order_detail` ( `item_id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `order_id` INT UNSIGNED NOT NULL, `product_name` VARCHAR(100) NOT NULL, `price` DECIMAL(10,2) NOT NULL, `quantity` INT NOT NULL, PRIMARY KEY (`item_id`), KEY `idx_order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

造数据存储过程

咱们整一个存储过程,快速往orders表插 10 万条数据,detail表插 20 万条左右数据。这在生产环境只能算“毛毛雨”,但足够演示执行计划了。

-- 执行造数 (耐心等待十几秒) CALL generate_data(); -- 查看结果 mysql> select count(*) from sys_orders; +----------+ | count(*) | +----------+ | 100000 | +----------+ 1 row in set (0.01 sec) mysql> select count(*) from sys_order_detail; +----------+ | count(*) | +----------+ | 199869 | +----------+ 1 row in set (0.00 sec)

2 读懂 EXPLAIN 的“天书” (核心字段全解析)

数据有了,现在我们随便跑一条 SQL,看看它的“体检报告”。

EXPLAIN SELECT * FROM sys_orders WHERE user_id = 'U1001' AND status = 2;

输出大概长这样(不同环境ID可能不同):

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEsys_ordersNULLrefidx_user_statusidx_user_status131const,const1100.00NULL

兄弟们,别看列这么多,作为 DBA,平时我盯着看的主要是这 5 个核心指标:type, key, key_len, rows, Extra

type: 访问类型 (性能的风向标)

这是重中之重!它告诉我们 MySQL 到底是怎么找数据的。我把常见的性能从好 -> 坏排个序,大家心里要有数:

  1. system/const: 只有一行匹配。比如WHERE primary_key = 1。这是性能的天花板,快得飞起。
  2. eq_ref: 连表查询时,前表的一行对应后表的唯一一行(通常是主键或唯一索引关联)。
  3. ref: 普通索引查找。比如我们上面的例子,user_id不是唯一的,可能搜出多条。
  4. range:这是我们优化的底线!索引范围扫描。常见于>,<,BETWEEN,IN。如果你的 SQL 跑出了range,通常是可以接受的。
  5. index(全索引扫描): ⚠️注意坑!很多人以为看到index就是走了索引,其实它是Full Index Scan。它扫描了 B+ 树的所有叶子节点,只是比全表扫描少读了点数据(因为索引文件通常比数据文件小)。
  6. ALL(全表扫描): ☠️红色警报!也是所谓的 Full Table Scan。MySQL 从硬盘头读到尾。如果是小表无所谓,大表出现ALL,基本就是因为没建索引或者索引失效。

key&key_len: 到底用了哪个索引?

  • possible_keys: MySQL 觉得可能会用到的索引(备胎)。
  • key: 最终实际选用的索引(正宫)。如果是NULL,恭喜你,全表扫描了。
  • key_len: 索引使用的字节数。这个很有用!
    • 比如我们的联合索引idx_user_status (user_id, status)
    • user_idvarchar(32),utf8mb4 编码下,最长 32*4 + 2(变长长度) = 130字节。
    • statustinyint,1字节。
    • 如果key_len显示 130 或 131 (视是否允许NULL),说明只用了user_idstatus没用到!这能帮你检查联合索引是不是只用了一部分。

rows&filtered: 只是个估算!

这里的坑最多,老哥给你们总结几个最关键的。看懂这几个,你基本就拿捏住了:

  • NULL(空): 😐不好不坏,常规操作。
    • 这意味着查询走了索引,但是索引无法覆盖所有查询的列,所以必须回表(Back to Table)去主键聚簇索引里把原本的数据行捞出来。
    • 场景:比如SELECT * FROM sys_orders WHERE user_id = 'U1'. 索引里只有user_id,但你要*,MySQL 只能拿着 ID 回去查正文。这是最常见的状态,只要不是全表扫描(type=ALL),通常可以接受。
  • Using index: 👍好东西!
    • 这叫“覆盖索引”。查询的字段全在索引树上,直接从索引就能拿结果,压根不用回表
    • 场景:SELECT user_id FROM sys_orders WHERE user_id = 'U1'. 这种性能极高,是我们优化的终极目标。
  • Using index condition: 👌值得鼓励的“小聪明”。
    • 这是 MySQL 5.6 引入的ICP (Index Condition Pushdown)特性。简单说,就是 MySQL 把一部分过滤工作“下沉”到了存储引擎层。
    • 大白话解释:本来存储引擎只管拿数据,过滤是 Server 层的事。现在 Server 层把部分WHERE条件丢给存储引擎:“兄弟,你在查索引的时候顺便帮我把这个条件卡一下,不符合的就别回表捞数据了,省点 I/O。”
    • 结论:Using where强,比Using index弱,属于一种性能优化手段。
  • Using where: 😐普普通通。
    • 说明存储引擎读上来数据后,Server 层还得再过滤一遍。
    • 注意:如果type=ALL且有Using where,说明你在全表扫描并过滤,这种必须要优化!如果type=ref且有Using where,通常问题不大。
  • Using filesort: 🚨红色警报!
    • 说明 MySQL 无法利用索引顺序来排序,必须在内存(Sort Buffer)或者磁盘里进行排序。
    • 比喻:这就好比你去图书馆找书,书架上的书是乱的,你得把书全搬到地上,自己一本本排好序才能给读者。极度消耗 CPU。
  • Using temporary: 🚨红色警报!
    • 既然用到了临时表(可能是内存的也可能是磁盘的),性能通常好不到哪去。
    • 常见于GROUP BYDISTINCT或复杂的UNION。看到这个,一定要想办法优化索引或简化 SQL。

3 MySQL 8 的大杀器:EXPLAIN ANALYZE(不但要估算,还要实测)

以前我们用EXPLAIN,就像是看地图估算时间:“这路应该不堵,大概 10 分钟”。但实际上可能路面塌陷,你堵了 1 小时。EXPLAIN只是优化器的“预判”,有时候它的成本计算(Cost)并不代表真实的执行时间。

MySQL 8.0 引入了EXPLAIN ANALYZE它不仅生成执行计划,ule还会真正运行这条 SQL,并告诉你每一步到底花了多久。

单表操作

实战演示:让 MySQL “汗流浃背”

为了看到真实的性能损耗,我们来构造一个无法利用索引排序的场景。我们查询 3 月份以后的订单(数据量大),并且强制按“金额”排序(无索引),取前 20 条。

-- 强制按 total_amount 排序,让它必须在内存里排 EXPLAIN ANALYZE SELECT * FROM sys_orders WHERE create_time > '2025-03-01' ORDER BY total_amount LIMIT 20;

输出解读 (TREE 格式):

| -> Limit: 20 row(s) (cost=10104.95 rows=20) (actual time=46.883..46.885 rows=20 loops=1) -> Sort: sys_orders.total_amount, limit input to 20 row(s) per chunk (cost=10104.95 rows=99687) (actual time=46.882..46.883 rows=20 loops=1) -> Filter: (sys_orders.create_time > TIMESTAMP'2025-03-01 00:00:00') (cost=10104.95 rows=99687) (actual time=0.030..32.416 rows=83350 loops=1) -> Table scan on sys_orders (cost=10104.95 rows=99687) (actual time=0.028..25.081 rows=100000 loops=1) |

老哥带你深度拆解:

怎么看这棵树?记住口诀:从里往外看,从下往上看,先看缩进最深的。

  1. Table scan on sys_orders(最底层):
  • 发生了什么:缩进最深,MySQL 选择了全表扫描
  • 数据说话:rows=100000(扫描了10万行),actual time=...25.081。光是把这10万行数据从硬盘/内存里读一遍,就花了25毫秒
  1. Filter(过滤层):
  • 发生了什么:拿着刚才读出来的10万行,逐行比对create_time > '2025-03-01'
  • 数据说话:rows=83350。说明大部分订单(8.3万条)都符合条件。这一步结束时,时间累积到了32.416毫秒
  1. Sort: sys_orders.total_amount(性能杀手!):
  • 发生了什么:这里的Sort就是传说中的Filesort!因为total_amount没有索引,MySQL 必须把这83350条数据扔到内存(Sort Buffer)里进行排序。
  • 数据说话:注意看时间,从 Filter 结束的 32ms 跳到了 Sort 结束的46.883ms。这意味着,光是排序这一下,就消耗了14毫秒的 CPU 时间!
  • 细节:limit input to 20 row(s) per chunk说明 MySQL 采用了优先队列排序(Priority Queue),不用给8万行全排序,只维护最小的20个即可,否则时间会更长。
  1. Limit(顶层):
  • 最后取前20条返回。
[思考一下:为什么有时候 EXPLAIN 显示走了索引(比如 range),但 EXPLAIN ANALYZE 里的 actual time 还是很长?]老哥点拨:EXPLAIN只能看到逻辑路径(走了索引),但它看不到物理成本。 如果你看到Index scan很快,但像上面一样Sort这一层actual time暴涨,说明瓶颈不在“找数据”,而在“排序”。这时候加任何过滤索引都没用,必须加包含排序字段的联合索引(例如idx_time_amount(create_time, total_amount))才能彻底消灭这个Sort节点,让性能直接起飞!

读懂多表关联 (JOIN) 的“套娃”结构**

单表看完了,咱们来看看多表关联。这可是小白最容易晕的地方。在 MySQL 8 的TREE格式里,JOIN 就像是一个“套娃”或者“嵌套循环”。

实战演示:我们要查询“用户 U1 的所有订单详情”。这需要关联sys_orderssys_order_detail

EXPLAIN ANALYZE SELECT d.* FROM sys_orders o JOIN sys_order_detail d ON o.order_id = d.order_id WHERE o.user_id = 'U1';

输出解读 (TREE 格式):

-> Nested loop inner join (cost=12.50 rows=5) (actual time=0.055..0.120 rows=5 loops=1) -> Index lookup on o using idx_user_status (user_id='U1') (cost=2.50 rows=2) (actual time=0.045..0.050 rows=2 loops=1) -> Index lookup on d using idx_order_id (order_id=o.order_id) (cost=2.10 rows=2) (actual time=0.015..0.020 rows=2.5 loops=2)

老哥带你深度拆解:

看完这个树,你得学会找“谁是老司机(驱动表)”“谁是乘客(被驱动表)”

  1. Nested loop inner join(最外层):
  • 这告诉我们,MySQL 决定用嵌套循环连接 (NLJ)的方式来处理。你可以把它理解为两层for循环。
  1. Index lookup on o(驱动表/外层循环):
  • 位置:缩进较少,排在上面。这就是驱动表
  • 解读:MySQL 先去sys_orders(别名 o) 表里找user_id='U1'的记录。
  • 数据:rows=2 loops=1。找到了 2 个订单。注意,这里的loops=1说明这个动作只做了一次。
  1. Index lookup on d(被驱动表/内层循环):
  • 位置:缩进更深,排在下面。这就是被驱动表
  • 关键点:注意看loops=2为什么是 2?
  • 核心逻辑:因为驱动表(订单表)找到了 2 条记录,所以内层循环就要执行 2 次。拿着订单 A 的 ID 去明细表查一次,再拿着订单 B 的 ID 去明细表查一次。
  • 性能公式:被驱动表的查询成本×驱动表的行数
[思考一下:如果驱动表扫出了 1 万条数据,会对被驱动表产生什么影响?]老哥点拨:那就是灾难!如果驱动表有 1 万行,被驱动表就要被查询 1 万次。这时候,被驱动表的关联字段(order_id)必须要有索引,否则就是做 1 万次全表扫描,数据库直接宕机!
4 实战演练:常见“坑”与优化方案

知道了原理,咱们来看几个平时开发中最容易踩的坑。

场景一:联合索引的“最左前缀”原则失效

坑爹 SQL:

-- 索引是 (user_id, status),但我跳过了 user_id 直接查 status EXPLAIN SELECT * FROM sys_orders WHERE status = 1; +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+-------------+ | 1 | SIMPLE | sys_orders | NULL | ALL | NULL | NULL | NULL | NULL | 99687 | 10.00 | Using where | +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+-------------+

结果:type: ALL分析:联合索引就像是“按姓氏、再按名字”排序的电话簿。你现在只给我一个名字(status),不给姓氏(user_id),我没法查,只能从头翻到尾。

还有一种坑:在索引列上做运算。

EXPLAIN SELECT * FROM sys_orders WHERE LEFT(user_id, 2) = 'U1'; +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+-------------+ | 1 | SIMPLE | sys_orders | NULL | ALL | NULL | NULL | NULL | NULL | 99687 | 100.00 | Using where | +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+-------------+

结果:type: ALL优化:只要对索引字段用了函数,索引立马失效。改成LIKE 'U1%'就可以走range索引了。

场景二:隐式转换导致的灾难

坑爹 SQL:

-- user_id 是 VARCHAR 类型,但我查询时没加单引号,用了数字 EXPLAIN SELECT * FROM sys_orders WHERE user_id = 1001; +----+-------------+------------+------------+------+-----------------+------+---------+------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+-----------------+------+---------+------+-------+----------+-------------+ | 1 | SIMPLE | sys_orders | NULL | ALL | idx_user_status | NULL | NULL | NULL | 99687 | 10.00 | Using where | +----+-------------+------------+------------+------+-----------------+------+---------+------+-------+----------+-------------+

结果:type: ALL,且Extra显示Using where分析:这是新手最容易犯的错!MySQL 发现类型对不上,会偷偷把user_id转成数字进行比较,相当于执行了CAST(user_id AS SIGNED)一旦在索引列上加了隐式函数,索引全废!优化:乖乖加上单引号user_id = '1001'

场景三:ORDER BY导致的Using filesort

坑爹 SQL:

-- 有 user_id 索引,但我非要按 total_amount 排序 EXPLAIN SELECT * FROM sys_orders WHERE user_id = 'U1001' ORDER BY total_amount; +----+-------------+------------+------------+------+-----------------+-----------------+---------+-------+------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+-----------------+-----------------+---------+-------+------+----------+----------------+ | 1 | SIMPLE | sys_orders | NULL | ref | idx_user_status | idx_user_status | 130 | const | 2 | 100.00 | Using filesort | +----+-------------+------------+------------+------+-----------------+-----------------+---------+-------+------+----------+----------------+

结果:Extra出现Using filesort分析:虽然user_id走了索引,但查出来的数据是按user_id排序的,不是按total_amount排序的。MySQL 只能把数据拿出来在内存里重排。优化:如果这个业务非常高频,建立联合索引(user_id, total_amount)。这样查出来的数据本身就是按金额排序的,直接取就行,省去了排序的 CPU 消耗。

场景四:深分页问题 (LIMIT 100000, 10)

坑爹 SQL:

EXPLAIN SELECT * FROM sys_orders ORDER BY create_time LIMIT 90000, 10; +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+----------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+----------------+ | 1 | SIMPLE | sys_orders | NULL | ALL | NULL | NULL | NULL | NULL | 99687 | 100.00 | Using filesort | +----+-------------+------------+------------+------+---------------+------+---------+------+-------+----------+----------------+

结果:type: ALL或者index,扫描行数巨大。分析:MySQL 需要查出 90010 条记录,抛弃前 90000 条,只取最后 10 条。这在数据量大时是灾难。

优化方案:延迟关联 (Late Row Lookup)

EXPLAIN SELECT * FROM sys_orders t1 JOIN ( SELECT order_id FROM sys_orders ORDER BY create_time LIMIT 90000, 10 ) t2 ON t1.order_id = t2.order_id; +----+-------------+------------+------------+--------+---------------+-----------------+---------+-------------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+--------+---------------+-----------------+---------+-------------+-------+----------+-------------+ | 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 90010 | 100.00 | NULL | | 1 | PRIMARY | t1 | NULL | eq_ref | PRIMARY | PRIMARY | 4 | t2.order_id | 1 | 100.00 | NULL | | 2 | DERIVED | sys_orders | NULL | index | NULL | idx_create_time | 5 | NULL | 90010 | 100.00 | Using index | +----+-------------+------------+------------+--------+---------------+-----------------+---------+-------------+-------+----------+-------------+

原理解析:

  1. 子查询只查order_id(主键),可以利用覆盖索引,不需要回表,速度极快。
  2. 拿到 10 个 ID 后,再回表去拿完整的*数据。
  3. 这样就把 90000 次回表 I/O 变成了 10 次。

场景五:多表关联时的“隐形杀手” (被驱动表无索引)**

兄弟们,JOIN 慢,90% 的原因是因为被驱动表(Joined Table)的连接列上没有索引

但在 MySQL 8 中,情况变了!以前没有索引会用“Block Nested Loop (BNL)”,慢得像蜗牛;现在 MySQL 8 引入了Hash Join

坑爹 SQL (模拟现场):假设我们手贱,把sys_order_detail表上的idx_order_id索引给删了,或者连接条件写错了导致无法走索引。

-- 假设 sys_order_detail 的 order_id 上没有索引 EXPLAIN ANALYZE SELECT * FROM sys_orders o JOIN sys_order_detail d ON o.order_id = d.order_id;

MySQL 8 的“救命”输出:

-> Inner hash join (no condition) (cost=xxxx rows=xxxx) (actual time=...) -> Table scan on d (cost=...) (actual time=...) -> Hash -> Table scan on o (cost=...) (actual time=...)

分析与优化:

  1. 现象:你会看到Inner hash join。这意味着 MySQL 放弃了循环查找,而是把其中一张表(通常是小表)全部读入内存,构建一个哈希表,然后扫描另一张大表去碰撞。
  2. 它是好事还是坏事?
  • 相比于 MySQL 5.7 的 BNL,Hash Join 是巨大的进步。它让本来要跑几个小时的烂 SQL,可能几分钟就跑完了。
  • 但是!它依然意味着全表扫描。它需要把两张表都读一遍。
  • 优化方案:
    • 别指望 Hash Join 救命。给被驱动表的连接字段(order_id)加上索引,让它变回Nested loop inner join配合Index lookup
    • 对比:Hash Join 是全量扫描(扫 20 万行);加了索引后的 NLJ 可能只需要扫描几十行。这中间的 I/O 差距是数量级的。

老哥心法:

  • 小表驱动大表原则依然适用,但在 MySQL 8 优化器面前,它会自动帮你选谁是小表,你不用太纠结LEFT JOIN还是RIGHT JOIN(除非业务逻辑限制)。
  • 死死盯住被驱动表:只要 EXPLAIN 里出现了Hash Join或者Block Nested Loop,第一时间检查关联字段有没有索引,或者关联字段的数据类型是否一致(避免隐式转换)。

总结

兄弟们,写到这,大概的套路你们应该都看明白了。EXPLAIN是我们手里的静态地图,而 MySQL 8 的EXPLAIN ANALYZE则是实时导航,能告诉你哪里堵车。

最后,老哥再送大家三句SQL 调优心法,这是我这十几年踩坑换来的:

  1. 不要过度优化:只有慢查询才需要优化。如果一个报表 SQL 一天只跑一次,跑 2 秒和 0.1 秒对业务没区别,别为了它把索引搞得巨复杂,导致插入数据变慢。
  2. 索引不是越多越好:索引是把双刃剑。查询快了,增删改(DML)必然变慢,因为要维护索引树。一张表的索引最好不要超过 5 个。
  3. 核心关注点RowsxCost所有的优化手段(加索引、改写法),最终目的都是为了减少 MySQL 扫描的行数。扫描的数据越少,I/O 就越少,速度自然就快。

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

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

相关文章

线上二手交易小程序源码系统 源码全开源可以二开

温馨提示&#xff1a;文末有资源获取方式 随着消费观念的升级与可持续生活方式的普及&#xff0c;线上二手交易正成为数字经济中不可或缺的一环。一个成功的平台不仅需要稳固的交易功能&#xff0c;更需要独特的社区氛围与用户粘性。我们向您推荐一款深度融合电商与社交基因的二…

农业文档中的WORD图片粘贴到CKEDITOR为何模糊?

要求&#xff1a;开源&#xff0c;免费&#xff0c;技术支持 编辑器&#xff1a;ckeditor 前端&#xff1a;vue2,vue3.vue-cli 后端&#xff1a;asp,java,jsp,springboot,php,asp.net,.net core 功能&#xff1a;导入Word,导入Excel,导入PPT(PowerPoint),导入PDF,复制粘贴word,…

2026年本地化部署支持多轮对话电话语音机器人厂商名单

随着企业数字化转型进入深水区,客户联络中心正经历从“成本中心”向“价值中心”的智能化跃迁。其中,支持复杂多轮对话的本地化部署电话语音机器人,因能兼顾数据安全、业务适配与智能化体验,成为众多对合规性、稳定…

2026版大厂Java面试 / 学习指南,共计1100+ 题全面解析

程序员一步入中年&#xff0c;不知不觉便会被铺天盖地的“危机感”上身&#xff0c;曾经的那个少年已经不在&#xff0c;时间就是这样公平。就算你能发明 Java 语言&#xff0c;随着时间的推移&#xff0c;你注定还是要成为慢慢变蔫的茄子&#xff0c;缓缓变黑的葡萄。 看着金…

Android 基础入门教程3.4 TouchListener PK OnTouchEvent + 多点触碰

3.4 TouchListener PK OnTouchEvent 多点触碰 分类 Android 基础入门教程 本节引言&#xff1a; 如题&#xff0c;本节给大家带来的是TouchListener与OnTouchEvent的比较&#xff0c;以及多点触碰的知识点&#xff01; TouchListener是基于监听的&#xff0c;而OnTouchEvent…

2026年可靠的方形摇摆筛排名,中药粉、矿山用筛全解析

在工业生产的细分领域里,方形摇摆筛作为关键的物料分选设备,其性能优劣直接关系到生产线的效率、产品品质与成本控制。尤其在中药粉加工、矿山开采等对筛分精度与稳定性要求严苛的场景中,选择一款可靠的方形摇摆筛,…

Android 基础入门教程3.5 监听EditText的内容变化分类

3.5 监听EditText的内容变化 分类 Android 基础入门教程 本节引言&#xff1a; 在前面我们已经学过EditText控件了&#xff0c;本节来说下如何监听输入框的内容变化&#xff01; 这个再实际开发中非常实用&#xff0c;另外&#xff0c;附带着说下如何实现EditText的密码可见…

学霸同款2026 TOP9 AI论文网站:专科生毕业论文必备测评

学霸同款2026 TOP9 AI论文网站&#xff1a;专科生毕业论文必备测评 2026年学术写作工具测评&#xff1a;专科生毕业论文的高效助手 在当前高校教育日益注重学术规范与创新能力的背景下&#xff0c;专科生在撰写毕业论文时常常面临资料查找困难、格式不规范、写作效率低等挑战。…

盘点知名卧式喷雾干燥机设备厂家,有哪些值得关注?

本榜单依托全维度市场调研与真实行业口碑,深度筛选出五家标杆企业,为企业选型提供客观依据,助力精准匹配适配的服务伙伴。 TOP1 推荐:无锡市双瑞机械有限公司 推荐指数:★★★★★ | 口碑评分:国内精制提纯装备企…

弱网条件下基于阻抗小扰动稳定性分析,小信号模型,阻抗扫描(电容电流反馈有源阻尼),单逆变器SR...

弱网条件下基于阻抗小扰动稳定性分析&#xff0c;小信号模型&#xff0c;阻抗扫描&#xff08;电容电流反馈有源阻尼&#xff09;&#xff0c;单逆变器SRF-PLL&#xff0c;时域频域结果对应验证&#xff08;文档主要有奈奎斯特分析&#xff0c;simulink仿真结果&#xff0c;逆变…

2026 儿童学习桌TOP5:把“可调、护眼、环保、稳不稳、好不好坚持坐正”一次讲明白

2026 儿童学习桌 TOP5:把“可调、护眼、环保、稳不稳、好不好坚持坐正”一次讲明白 TOP5 排名(先给结论)博士有成 护童 光明园迪 爱果乐 心家宜选儿童学习桌别绕弯:先看这 7 件“用起来才知道”的事高度能不能跟着…

总结东莞靠谱保安服务公司,恒博保安口碑出众

随着企业和社区对安全需求的不断升级,选择一家专业可靠的保安服务公司成为许多组织的重要课题。本文围绕保安服务公司求推荐、保安公司找哪家、专业保安公司哪家专业等高频问题展开解答,结合深圳市恒博保安服务有限公…

算法备案中 服务提供者与技术支持者的区别

一、服务提供者&#xff1a; 定义与定位&#xff1a; 服务提供者&#xff0c;是直接向最终用户&#xff08;C端客户&#xff09;提供算法服务的主体。他们是算法应用的直接运营者&#xff0c;也是用户体验的最终承载者。 核心特征&#xff1a; C端服务属性&#xff1a; 服务提…

鸡西市鸡冠恒山滴道梨树城子河英语雅思培训辅导机构推荐,2026权威出国雅思课程中心学校口碑排行榜

在全球化留学热潮持续升温的背景下,雅思考试已成为鸡西市鸡冠、恒山、滴道、梨树、城子河等区域学子开启海外求学之路的核心门槛。然而,本地学子在雅思培训选课过程中普遍面临诸多棘手痛点:优质教育机构资源稀缺,难…

2026年推荐:厌氧颗粒污泥领域靠谱厂商合集,头部厌氧颗粒污泥聚焦优质品牌综合实力排行

引言 在制药、化工、造纸、钢铁等工业领域,污水处理效率与达标排放直接关乎企业安全生产与环保合规。作为污水处理核心生物载体,厌氧颗粒污泥的性能直接影响有机物降解率、泥水分离效果及系统稳定性。据国内环保行业…

阿波罗工业四足机器人发布,优必选工业人形机器人规模化落地,dToF传感器量产筹备,Anthropic修订具身智能模型

镜识科技与凯尔达机器人发布阿波罗工业四足机器人&#xff0c;破解量产落地痛点镜识科技与凯尔达机器人在杭州联合举办发布会&#xff0c;正式推出新一代工业级四足具身智能机器人 “阿波罗”&#xff0c;同步签署深度合作协议&#xff0c;构建 “技术研发 - 规模化制造 - 场景…

​2026海外AI年度复盘及财报综述:泡沫与机遇并存,结构性机会凸显

摘要&#xff1a;本报告基于2025年产业数据与企业财报&#xff0c;揭秘AI产业从“狂热扩张”向“需求兑现效率竞争”转型的核心逻辑&#xff0c;为投资者、从业者、行业观察者提供全景式产业指南。 AI 产业正站在关键转折点&#xff01;2025Q3 泡沫论发酵引发美股 AI 相关股回…

2026年互联网+汽车+零售工单系统推荐,选型实用不踩坑攻略

在互联网技术深度渗透汽车与零售行业的当下,服务场景日趋复杂——汽车后市场的跨区域维保、零售连锁的多门店协同、线上线下融合的全渠道服务需求,都对工单系统提出了“高效流转、全程可溯、数据联动”的核心要求。当…

2025虚拟现实产业报告:AI+XR双引擎驱动!XR3.0时代开启,从技术突破到规模落地

摘要&#xff1a;本报告基于XR3.0阶段“AIXR”双技术引擎特征&#xff0c;揭秘终端形态创新、技术突破、产业融合与生态构建的核心逻辑&#xff0c;为产业链从业者、投资者、政策制定者、行业观察者提供全景式产业指南。XR产业已迈入发展“快车道”&#xff01;2024年起进入XR3…

2026 年全国真发假发定制品牌权威推荐榜 个性化定制 供应链实力全景解析

2026 年真发假发定制行业正完成从 “功能满足” 到 “体验与品质双升级” 的转型,线下实体体验与线上便捷服务的融合模式成为行业常态,手工定制与智能生产的互补也让产品能适配更多元的需求场景。真发原料筛选、手工…