论索引影响性能的一面④ 索引失踪之谜【上】

梁敬彬梁敬弘兄弟出品

往期回顾
论索引影响性能的一面①索引的各种开销
论索引影响性能的一面②索引的使用失效
论索引影响性能的一面③ 索引开销与经典案例

开篇:DBA的深夜“寻人启事”

作为数据库的守护者,我们最信赖的伙伴莫过于“索引”。它如同一位效率超群的图书管理员,能于浩如烟海的数据中,精准地为我们取出所需的那一页。但你是否也曾遇到过这样的“灵异事件”:明明为它精心创建了索引,性能却依旧惨不忍睹?执行计划里,那个熟悉的身影消失不见,取而代之的是令人绝望的“全表扫描”(TABLE ACCESS FULL)。

索引失踪了,到底去哪儿了?

这并非灵异故事,而是一系列潜藏在日常操作中的“性能悬案”。今天,我们将化身侦探,深入四个经典的“案发现场”,揭开索引失踪的秘密。

在这里插入图片描述

悬案一:like与 %间一波三折的故事

案情简介:
江湖传言,“LIKE一出,索引必废”。这个说法流传甚广,让许多开发者谈%色变。但这究竟是真相,还是误解?让我们用证据说话。

现场勘查与证据收集:
首先,我们准备“案发现场”——一张T表,并在OBJECT_NAME列上建立索引。

drop table t purge;
create table t as select * from dba_objects where object_id is not null;
set autotrace off
update t set object_id=rownum;
update t set object_name='AAALJB' where object_id=8;
update t set object_name='LJBAAA' where object_id=10;
commit;
create index idx_object_name on t(object_name);
SET AUTOTRACE ON
SET LINESIZE 1000

1. 场景一:前缀匹配查询 (‘LJB%’)

select object_name,object_id from t where object_name like 'LJB%';OBJECT_NAME OBJECT_ID
-------------------------------------------------------------------------------------
LJBAAA 10
LJB_TMP_SESSION 72521
LJB_TMP_SESSION 72910
LJB_TMP_TRANSACTION 72522
LJB_TMP_TRANSACTION 72911
已选择5行。执行计划
--------------------------------------------------------------------------------------
| Id |Operation | Name |Rows | Bytes | Cost (%CPU)| Time |
| 0 |SELECT STATEMENT | | 5| 395 | 6 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 5| 395 | 6 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_OBJECT_NAME | 5| | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------0  recursive calls0  db block gets9  consistent gets0  physical reads0  redo size602  bytes sent via SQL*Net to client415  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)5  rows processed

破案分析:
索引健在,且表现优异! INDEX RANGE SCAN(索引范围扫描)赫然在列。like 'LJB%'这种前缀匹配,Oracle可以精准定位到索引树中“LJB”的位置开始扫描,当然能用到索引。

2. 场景二:模糊匹配查询 (‘%LJB%’)

select object_name,object_id from t where object_name like '%LJB%';执行计划
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 12 | 948 | 292 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 12 | 948 | 292 (1)| 00:00:04 |
--------------------------------------------------------------------------------------统计信息
----------------------------------------------------------0  recursive calls0  db block gets1049  consistent gets0  physical reads0  redo size653  bytes sent via SQL*Net to client415  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)6  rows processed

破案分析:
索引“人间蒸发”! TABLE ACCESS FULL出现了。因为前导%的存在,Oracle不知道检索何时能停下来,只能放弃索引,选择全表扫描。

3. 场景三:后缀匹配的“曲线救国” (‘%LJB’)

正常情况下,后缀匹配与模糊匹配原理相似,无法使用常规索引。但我们可以通过reverse函数和函数索引,巧妙破局。

create index idx_reverse_objname on t(reverse(object_name));
set autotrace on
select object_name,object_id from t where reverse(object_name) like reverse('%LJB');OBJECT_NAME OBJECT_ID
-------------------------------------------------------------------------------------
AAALJB 8执行计划
--------------------------------------------------------------------------------------
|Id |Operation |Name |Rows |Bytes |Cost (%CPU)| Time |
| 0|SELECT STATEMENT | | 3596| 509K| 290 (0)| 00:00:04 |
| 1| TABLE ACCESS BY INDEX ROWID|T | 3596| 509K| 290 (0)| 00:00:04 |
|* 2| INDEX RANGE SCAN |IDX_REVERSE_OBJNAME| 647| | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------0  recursive calls0  db block gets5  consistent gets0  physical reads0  redo size496  bytes sent via SQL*Net to client415  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1  rows processed

在这里插入图片描述

悬案二:move命令引发的“血案”

案情简介:
这是一起发生在某大型制造业系统的真实“血案”。一个看似无害的ALTER TABLE … MOVE操作,竟导致系统核心查询性能骤降,业务近乎瘫痪。

现场勘查与证据收集:

drop table t_p cascade constraints purge;
drop table t_c cascade constraints purge;
CREATE TABLE T_P (ID NUMBER, NAME VARCHAR2(30));
ALTER TABLE T_P ADD CONSTRAINT T_P_ID_PK PRIMARY KEY (ID);
CREATE TABLE T_C (ID NUMBER, FID NUMBER, NAME VARCHAR2(30));
ALTER TABLE T_C ADD CONSTRAINT FK_T_C FOREIGN KEY (FID) REFERENCES T_P (ID);
INSERT INTO T_P SELECT ROWNUM, TABLE_NAME FROM ALL_TABLES;
INSERT INTO T_C SELECT ROWNUM, MOD(ROWNUM, 1000) + 1, OBJECT_NAME FROM ALL_OBJECTS;
COMMIT;
CREATE INDEX IND_T_C_FID ON T_C (FID);-- 检查索引初始状态
SELECT TABLE_NAME,INDEX_NAME,STATUS FROM USER_INDEXES WHERE INDEX_NAME='IND_T_C_FID';
TABLE_NAME           INDEX_NAME           STATUS
-------------------- -------------------- --------
T_C                  IND_T_C_FID          VALID-- 执行一个“不小心”的操作
ALTER TABLE T_C MOVE;-- 再次检查索引状态
SELECT TABLE_NAME,INDEX_NAME,STATUS FROM USER_INDEXES WHERE INDEX_NAME='IND_T_C_FID';
TABLE_NAME           INDEX_NAME           STATUS
-------------------- -------------------- --------
T_C                  IND_T_C_FID          UNUSABLE

破案分析:
凶手正是MOVE命令! MOVE操作改变了表中所有行的ROWID,而索引中存储的恰恰是旧的ROWID。Oracle为了避免数据错乱,将该索引标记为UNUSABLE。这个“暗杀”是无声的,操作本身不报错,但索引已悄然“死亡”。

受害者(索引失效后的查询):

SET LINESIZE 1000
SET AUTOTRACE TRACEONLY
SELECT A.ID, A.NAME, B.NAME FROM T_P A, T_C B WHERE A.ID = B.FID AND A.ID = 880;执行计划
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 25 | 1500 | 111 (1)| 00:00:02 |
| 1 | NESTED LOOPS | | 25 | 1500 | 111 (1)| 00:00:02 |
| 2 | TABLE ACCESS BY INDEX ROWID| T_P | 1 | 30 | 0 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | T_P_ID_PK | 1 | | 0 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | T_C | 25 | 750 | 111 (1)| 00:00:02 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------3 - access("A"."ID"=880)4 - filter("B"."FID"=880)统计信息
----------------------------------------------------------0  recursive calls0  db block gets394  consistent gets0  physical reads0  redo size3602  bytes sent via SQL*Net to client459  bytes received via SQL*Net from client6  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)72  rows processed

对T_C表执行了TABLE ACCESS FULL,逻辑读高达394。

“救治”方案与效果:

ALTER INDEX IND_T_C_FID REBUILD;-- 再次执行查询
SELECT A.ID, A.NAME, B.NAME FROM T_P A, T_C B WHERE A.ID = B.FID AND A.ID = 880;执行计划
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 72 | 4320 | 87 (0)| 00:00:02 |
| 1 | NESTED LOOPS | | 72 | 4320 | 87 (0)| 00:00:02 |
| 2 | TABLE ACCESS BY INDEX ROWID| T_P | 1 | 30 | 0 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | T_P_ID_PK | 1 | | 0 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| T_C | 72 | 2160 | 87 (0)| 00:00:02 |
|* 5 | INDEX RANGE SCAN | IND_T_C_FID | 72 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------3 - access("A"."ID"=880)5 - access("B"."FID"=880)统计信息
----------------------------------------------------------0  recursive calls0  db block gets81  consistent gets0  physical reads0  redo size3602  bytes sent via SQL*Net to client459  bytes received via SQL*Net from client6  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)72  rows processed

侦探笔记:
ALTER TABLE … MOVE是一个极其危险的操作。操作规范:在执行MOVE后,必须立即REBUILD该表上的所有索引。
在这里插入图片描述

悬案三:外键索引失效引发的“幽灵锁”

案情简介:
如果说上一个案例是MOVE命令造成的直接性能伤害,那么这个案例,则是它更隐蔽、更阴险的“并发症”——锁等待。
现场勘查与证据收集:

-- 准备环境
drop table t_p cascade constraints purge;
drop table t_c cascade constraints purge;
CREATE TABLE T_P (ID NUMBER, NAME VARCHAR2(30));
ALTER TABLE T_P ADD CONSTRAINT T_P_ID_PK PRIMARY KEY (ID);
CREATE TABLE T_C (ID NUMBER, FID NUMBER, NAME VARCHAR2(30));
ALTER TABLE T_C ADD CONSTRAINT FK_T_C FOREIGN KEY (FID) REFERENCES T_P (ID);
INSERT INTO T_P SELECT ROWNUM, TABLE_NAME FROM ALL_TABLES;
INSERT INTO T_C SELECT ROWNUM, MOD(ROWNUM, 1000) + 1, OBJECT_NAME FROM ALL_OBJECTS;
COMMIT;
-- 注意,这里没有给外键列T_C(FID)创建索引-- 以下操作导致外键相关的索引失效(此处原文为move,但实际场景是未创建索引)
-- ALTER TABLE T_C MOVE;

并发测试:

-- 首先开启会话1
select sid from v$mystat where rownum=1;
DELETE T_C WHERE ID = 2;-- 接下来开启会话2,也就是开启一个新的连接
select sid from v$mystat where rownum=1;
DELETE T_P WHERE ID = 2000;
-- 居然发现卡住半天不动了!

破案分析:
这起“幽灵锁”的根源,在于外键约束的底层工作机制。当你操作主表(如DELETE T_P)时,Oracle必须确保子表中没有任何记录引用你将要删除的主表记录。

索引有效时: Oracle会通过外键列上的索引,快速检查子表T_C中是否存在FID = 2000的记录。
索引失效(或不存在)时: Oracle无法快速检查,只能在子表T_C上施加一个全表锁,然后慢慢地进行全表扫描。当会话1持有T_C的行级锁时,会话2想要获取全表锁自然会被阻塞。
侦探笔记:
外键列上必须建立索引,并保证其永远有效! 这不仅是查询性能的需要,更是保证高并发环境下系统稳定性的“生命线”。

悬案四:shrink操作后的“伪装者”

案情简介:
既然MOVE如此危险,那我们用更现代的SHRINK命令来收缩表空间,总该安全了吧?它确实能避免索引失效,但新的问题又来了:索引明明VALID,优化器为何依然弃之不用?

现场勘查与证据收集:

drop table t purge;
create table t as select * from dba_objects where object_id is not null;
alter table t modify object_id not null;
set autotrace off
insert into t select * from t;
insert into t select * from t;
commit;
create index idx_object_id on t(object_id);
set linesize 1000
set autotrace on
select count(*) from t;
set autotrace off
delete from t where rownum<=292000;
commit;
set autotrace on
alter table t enable row movement;
alter table t shrink space;
select count(*) from t;执行计划
------------------------------------------------------------------------------------
Plan hash value: 2966233522
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 5 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 740 | 5 (0)| 00:00:01 |
------------------------------------------------------------------------------------统计信息
----------------------------------------------------------0  recursive calls0  db block gets15  consistent gets0  physical reads0  redo size424  bytes sent via SQL*Net to client415  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1  rows processed

破案分析:
索引再次“失踪”!它明明状态VALID,为何被优化器无情抛弃?我们强制让它走索引(使用hint)看看。

select /*+index(t)*/ count(*) from t;执行计划
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
| 0 | SELECT STATEMENT | | 1 | 675 (1)| 00:00:09 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| IDX_OBJECT_ID | 740 | 675 (1)| 00:00:09 |
--------------------------------------------------------------------------------------
统计信息
----------------------------------------------------------0  recursive calls0  db block gets649  consistent gets0  physical reads0  redo size424  bytes sent via SQL*Net to client415  bytes received via SQL*Net from client2  SQL*Net roundtrips to/from client0  sorts (memory)0  sorts (disk)1  rows processed

真相大白!强制走索引的成本(Cost=675,逻辑读=649)远高于走全表扫描(Cost=5,逻辑读=15)。原因在于:SHRINK操作虽然收缩了表,但并未有效地收缩索引段的空间。 大量DELETE操作在索引中留下了许多“空洞”,导致索引变得稀疏而“臃肿”,扫描它反而得不偿失。

侦探笔记:
索引的VALID状态,仅仅是它的“准入证”,并不代表它的“健康证”。一个因大量删除而变得臃肿的索引,即便有效,也可能成为性能的拖累。在这种情况下,ALTER INDEX … REBUILD 才是整理索引碎片、恢复其紧凑结构和高性能的“特效药”。

总结

今天的调查到此告一段落。我们揭开了四个导致索引“失踪”或“失效”的经典悬案:

伪装者LIKE: 被前导通配符%所迷惑。
刺客MOVE: 无声地让索引状态变为UNUSABLE。
帮凶“无索引外键”: 在并发操作中制造“幽灵锁”。
“亚健康”的SHRINK后遗症: 索引虽在,却因臃肿而被优化器嫌弃。
然而,索引失踪之谜远未结束。在下一集中,我们将继续追踪另外四位“嫌疑人”,它们同样狡猾,同样致命。敬请期待《索引失踪之谜(下)》。

未完待续…
论索引影响性能的一面⑤:索引失踪之谜(下)

系列回顾

“大白话人工智能” 系列
“数据库拍案惊奇” 系列
“世事洞明皆学问” 系列

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

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

相关文章

java集合(九) ---- Stack 类

目录 九、Stack 类 9.1 位置 9.2 特点 9.3 栈 9.4 构造方法 9.5 常用方法 9.6 注意点&#xff1a;循环遍历 Stack 类 九、Stack 类 9.1 位置 Stack 类位于 java.util 包下 9.2 特点 Stack 类是 Vector 类的子类Stack 类对标于数据结构中的栈结构 9.3 栈 定义&…

ARXML可视化转换工具使用说明

ARXML可视化转换工具 | 详细使用指南与说明 &#x1f4dd; 前言 自上篇文章《聊聊ARXML解析工具&#xff1a;我们是如何摆脱昂贵商业软件的》发布以来&#xff0c;收到了众多朋友的关注和咨询&#xff0c;这让我倍感荣幸&#xff01; 新朋友请注意&#xff1a;如果您还没有阅…

松胜与奥佳华按摩椅:普惠科技与医疗级体验的碰撞

在智能健康设备快速普及的今天&#xff0c;按摩椅已从奢侈品转变为家庭健康管理的重要工具。面对市场上琳琅满目的品牌&#xff0c;松胜与奥佳华凭借截然不同的发展路径&#xff0c;各自开辟出特色鲜明的赛道&#xff1a;前者以“技术普惠”理念打破高端按摩椅的价格壁垒&#…

一起学习Web 后端——PHP(二):深入理解字符与函数的使用

一、前言 在上一讲中&#xff0c;我们主要讲PHP的相关知识。本节我们将继续深入&#xff0c;学习&#xff1a; PHP 中各种语法字符、符号的含义与用法&#xff1b; PHP 中常用函数的种类、定义方式与实际应用。 这些知识是构建 Web 后端逻辑的基础&#xff0c;对于后期编写…

【Bluedroid】蓝牙启动之 GAP_Init 流程源码解析

蓝牙 GAP(通用访问配置文件)模块是蓝牙协议栈的核心组件,负责设备发现、连接管理及基础属性暴露等关键功能。本文围绕 Android蓝牙协议栈 GAP 模块的初始化流程与连接管理实现展开,结合代码解析其核心函数(GAP_Init、gap_conn_init、gap_attr_db_init)的功能逻辑,以及关…

最新四六级写作好词好句锦囊(持续更新中)

完整版四六级备考攻略可见另一篇博客~~&#xff08;喜欢的留个点赞收藏再走呗~~&#xff09; ​​​​​​四六级备考攻略-CSDN博客 一、通用 1、词组 2、单词 3、句型 二、老龄化、老年人 三、学习、社交、社会实践 四、文化、习俗 五、数字素养、数字技能 六、资…

Java 通用实体验证框架:从业务需求到工程化实践【生产级 - 适用于订单合并前置校验】

Java 通用实体验证框架&#xff1a;从业务需求到工程化实践【适用于订单合并前置校验】 一、业务验证痛点与需求背景 1. 传统验证方式的困境 传统验证方式存在代码冗余、维护成本高和扩展性差等问题。相同的验证逻辑在不同模块重复编写&#xff0c;修改验证规则时需要同步修…

PyArk飘云阁出品的ARK工具

PyArk是由飘云阁&#xff08;PiaoYunGe&#xff09;开发的一款功能强大的系统安全分析工具&#xff0c;主要用于Windows环境下的内核级检测与分析。该工具集成了进程管理、驱动模块扫描、内核及应用层钩子检测、进程注入等核心功能&#xff0c;旨在帮助安全研究人员深入识别潜在…

【高中数学之复数】已知复数z的幅角为60°,且|z-1|是|z|和|z-2|的等比中项,求|z|?(2003高考数学全国卷,解答题首题,总第17题)

【问题】 已知复数z的幅角为60&#xff0c;且|z-1|是|z|和|z-2|的等比中项&#xff0c;求|z|? 【来源】 2003高考数学全国卷&#xff0c;解答题首题&#xff0c;总第17题。 【解答】 解&#xff1a; 由复数辐长辐角定义有 zr*(Cos60iSin60) 据等比中项定义有&#xff1…

观点 | 科技企业到了品牌建设的历史性窗口期

随着全球科技产业的飞速发展&#xff0c;科技型企业作为推动技术创新和经济发展的重要力量&#xff0c;正面临着前所未有的机遇与挑战。近年来&#xff0c;中国科技行业保持了快速增长的态势。根据国家统计局的数据&#xff0c;2023年全国研究与试验发展&#xff08;R&D&am…

影像组学5:Radiomics Score的计算

Rad-score&#xff08;全称 Radiomics score&#xff0c;影像组学评分&#xff09;是通过数学模型将影像组学提取的多个特征整合为一个综合性指标&#xff0c;从而简化临床分析与决策。 前文已介绍影像组学的病灶分割、特征提取及筛选流程&#xff0c;本节将重点阐述 Rad-scor…

使用Appium在iOS上实现自动化

安装 Appium npm install -g appium检测 Appium 是否安装成功 appium --version安装 Appium Doctor npm install appium-doctor -g安装 ios 测试驱动 appium driver install xcuitest检测 iOS 环境是否正常 appium-doctor --ios安装 ideviceinstaller brew install idevi…

JPA全面指南:使用步骤、语法详解与实战案例

一、JPA概述与核心概念 1.1 什么是JPA&#xff1f; Java Persistence API&#xff08;JPA&#xff09;是Java EE和Java SE平台上的ORM&#xff08;对象关系映射&#xff09;标准规范&#xff0c;它简化了Java应用程序与数据库的交互过程。JPA不是具体的实现&#xff0c;而是一…

Django框架认证系统默认在登录成功后尝试重定向到/accounts/profile/

这个404错误是因为Django的认证系统默认在登录成功后尝试重定向到/accounts/profile/,但你的项目中没有配置这个URL。以下是完整解决方案: 方法一:设置登录重定向路径(推荐) 在settings.py中添加以下配置: # settings.py LOGIN_REDIRECT_URL = /dashboard/ # 替换为你…

QT实现右键菜单栏

1.所需头文件 #include <QPoint> // QPoint 类型 #include <QWidget> // mapFromGlobal() 的父类 #include <QEvent> // event->globalPos() 的来源&#xff08;如 QMouseEvent&#xff09; #include <QContextMenuEvent> // 用于 QContex…

华为云Flexus+DeepSeek征文|华为云CCE容器高可用部署Dify LLM应用后的资源释放指南

目录 前言 1 高可用部署带来的资源特性 1.1 涉及的核心资源组件 1.2 高可用部署的代价 2 正确释放资源的重要性 3 使用资源编排释放资源 3.1 进入资源编排页面 3.2 两种删除方式解析 3.3 推荐操作流程 4 手动删除各类云资源 4.1 使用资源页面集中管理 4.2 分服务删…

yum查看历史操作

在 Red Hat/CentOS 系统中&#xff0c;可以使用 yum history 命令查看和管理 YUM/DNF 的历史操作记录。以下是详细使用方法&#xff1a; 1. 查看完整历史记录 sudo yum history list # 或简写 sudo yum history输出示例&#xff1a; ID | 命令行 | 日期与时间…

Python-Flask实现登录

Python-Flask实现登录 Python-Flask实现登录项目结构Flask蓝图路由项目代码 Python-Flask实现登录 项目结构 Flask蓝图路由 from flask import Blueprint, render_template, request, sessionac Blueprint(account, __name__)ac.route(/login, methods[GET, POST]) def logi…

libcuckoo 介绍和使用指南

文章目录 libcuckoo 介绍和使用指南什么是 libcuckoo&#xff1f;主要特点安装方法从源码安装 基本使用方法创建哈希表并发操作示例 高级功能自定义哈希函数和比较函数更新操作大小和统计信息 性能考虑适用场景注意事项 libcuckoo 介绍和使用指南 libcuckoo 是一个高性能、并发…

TIA Portal V20HMI仿真时数值无法写入虚拟plc解决教程

在博图 V20 中使用 S7-PLCSIM Advanced 仿真 S7-1500 Advanced V5.0 PLC&#xff0c;同时使用 WinCC Runtime Advanced 仿真 HMI 时出现“连接中断”且无法写入数值&#xff0c;而单独使用 S7-PLCSIM (Classic) 仿真 PLC 正常&#xff0c;这是一个非常典型且令人困扰的问题。问…