当前位置: 首页 > news >正文

63 mysql 的 行锁

前言

我们这里来说的就是 我们在 mysql 这边常见的 几种锁

行共享锁, 行排他锁, 表意向共享锁, 表意向排他锁, 表共享锁, 表排他锁

意向共享锁, 意向排他锁, 主要是 为了表粒度的锁获取的同步判断, 提升效率 

意向共享锁, 意向排他锁 这边主要的逻辑意义是数据表中是否有任意一行的行共享锁, 行排他锁被获取 

 

假设如下sql “select * from t_user_02 for update;”, 会首先会先尝试获取 t_user_02 的表意向排他锁, 然后再遍历符合条件的每一行记录, 获取每一行记录的 行排他锁

假设如下sql “select * from t_user_02 where id = ‘1’ for update;”, 会首先会先尝试获取 t_user_02 的表意向排他锁, 然后获取 id 为 ‘1’ 的记录的行排他锁

差距就在于 扫描表的记录, 前者需要扫描全表, 后者 只需要扫描 id = ‘1’ 的数据行 

之后 我们还会有一个锁粒度 的调试 

当然 这里需要区分一些情况, 比如 “select * from t_user_02;” 的查询是无锁查询, “select * from t_user_02 for update;” 是申请排他锁查询, “select * from t_user_02 lock in share mode;” 是申请共享锁查询 

在无锁查询的情况下, 是不会去尝试获取 表锁, 行锁 的, 是一直可以查询的 

 

测试数据表如下 

CREATE TABLE `t_user_02` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(24) DEFAULT NULL,`age` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8

 

t_user_02 的数据列表如下  

bd8be40e8b125f0914f17a5700af6362.png

 

 

表意向排他锁

我们这里调试 sql 如下 这里会先获取 t_user_02 的表意向排他锁, 然后再读取的时候在获取 id 为 2 的记录的行排他锁, 我们这里先看 表意向排他锁, 再看行排他锁 

begin;
select * from t_user_02 where id = '2' for update;
commit;

 

获取表意向锁这边是在 row_search_mvcc 中, 这里是属于读取记录之前, 会先尝试获取 t_user_02 的 表意向共享锁 或者 表意向独占锁

获取成功之后会继续往下走获取记录的相关业务流程

获取失败之后, 会挂起当前线程, 等待目标锁可以争取 然后再次尝试获取目标锁

在我们这里只存在 表意向共享锁, 表意向排他锁, 行共享锁, 行排他锁 的场景下面, 获取 表意向共享锁, 表意向排他锁 是恒成功的

73405b3cf1462babb67210241372ab61.png

 

获取锁这边处理如下, 判断是否有已经占用的 t_user_02 的表的锁 

如果没有可以直接获取给定的 表意向共享锁, 表意向独占锁

如果有判断已经持有的锁是否 和 当前请求的锁兼容, 在我们这里的场景下只考虑 表意向共享锁, 表意向独占锁, 行共享锁, 行独占锁 这里是几种都兼容 

表意向共享锁, 表意向排他锁主要是用于 表共享锁, 表排他锁的相关地方的提升效率的处理 

500f8ef6359b36de910a0462c9fb0017.png

 

然后 第一次迭代以后的迭代是不需要再获取 表意向共享锁, 表意向排他锁 了, 处理的地方如下 

第二次 以及以后的迭代, 走的是 ”if(!prebuilt->sql_stat_start)” 中的相关的流程了 

9f239e0b429ce3909fb26e2b83a2621c.png

 

 

表意向共享

我们这里调试 sql 如下 这里会先获取 t_user_02 的表意向共享锁, 然后再读取的时候在获取 id 为 2 的记录的行共享锁, 我们这里先看 表意向共享锁, 再看行共享锁 

begin;
select * from t_user_02 where id = '1' lock in share mode;
commit;

 

和上面获取 表意向共享锁 类似的流程, 只是这里获取的 表意向排他锁

在我们这里只存在 表意向共享锁, 表意向排他锁, 行共享锁, 行排他锁 的场景下面, 获取 表意向共享锁, 表意向排他锁 是恒成功的

9853db835ffd9a506efc49a656c56096.png

 

 

行排他锁

我们这里调试 sql 如下 这里会先获取 t_user_02 的表意向排他锁, 然后再读取的时候在获取 id 为 2 的记录的行排他锁, 我们这里来看 行排他锁 

begin;
select * from t_user_02 where id = '2' for update;
commit;

 

行共享锁的获取是在 获取了当前行的数据之后, 再来获取的 

select 中没有 “for update;”, “lock in share mode;” 的场景是 “prebuilt->select_lock_type == LOCK_NONE” 的场景 

我们这里是带 “for update”, 获取 行排他锁

1aa3138371aabdf9bb760ac09f6a4ed3.png

 

然后 其次就是尝试获取锁之后的处理, 获取成功之后 移动游标, 调用栈返回 

如果获取 行排他锁 不成功, 走 lock_wait_or_error, 等待目标锁可以争取 然后再次尝试获取目标锁

03e2be72acf38a978debc3dbb0b34a1e.png

 

行锁的实际这边如下, 分为 fastpath 和 slowpath

行锁这边是以 page 为单位的, 一个 page 公用一把锁, lock 中有 bitmap 来维护每一条记录的锁是否被占用, 以及其他信息 

“if(lock == null)” 这里是目标页还没有任何锁的情况, 直接创建锁, 获取锁, 这里可以直接响应 LOCK_REC_SUCCESS_CREATED, 是因为外层 lock_clust_rec_read_check_and_lock 有一个 lock_mutex_enter 有一个全局的同步 

然后下面就是 “else if(!impl)” 的处理是如果目标锁是关联当前事务, 尝试直接获取给定的记录的行锁, “lock_rec_set_nth_bit(lock, heap_no)” 就是获取目标记录的行锁

2b762881a9b5c745436fa2a82fb45700.png

 

我们再来看一下 slowpath

slowpath 这边主要是 fastpath 尝试获取锁失败的场景下面

如果目标记录已经被其他事务持有和当前目标锁冲突的锁, 则 DB_LOCK_WAIT, 上游 row_search_mvcc 走 wait 的流程 

否则 可以尝试获取目标锁, 然后响应给上游 获取锁成功

00fd7315fb23f1e9259469d600c1c987.png

 

行共享锁, 和 行排他锁这边的冲突规则主要如下

行共享锁 兼容于行共享锁, 行共享锁 不兼容于 行排他锁

行排他锁 不兼容于 行共享锁, 行排他锁 不兼容于 行排他锁

2e9613f68793704b2f09646b8a7613b1.png

 

 

共享

我们这里调试 sql 如下 这里会先获取 t_user_02 的表意向共享锁, 然后再读取的时候在获取 id 为 2 的记录的行共享锁, 我们这里看 行共享锁 

begin;
select * from t_user_02 where id = '1' lock in share mode;
commit;

 

和获取 行排他锁的流程基本上一致, 这里不多赘述 

f834e338650362795c0ecb7ef4ab5298.png

 

 

行锁锁索引字段的情况

我们这里更新测试表结构如下 

CREATE TABLE `tz_test_04` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`field1` varchar(128) DEFAULT NULL,`field2` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,KEY `field_1_2` (`field1`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

 

然后执行 sql 如下 “select * from tz_test_04 where field1 = 'field5' for update;”

从 row_search_mvcc 这里的上下文可以看出当前 rec 是一条索引记录, 然后 后面走的流程 锁定的也是这部分满足条件的索引记录

因此 后面需要尝试获取索引记录的锁的情况, 如果锁不兼容, 则会阻塞 

这是在索引匹配 field1 = ‘field5’ 的记录上面增加的索引的 行临键锁, 然后 它还会在具体的数据记录上面增加 行排他锁, 在下一个索引记录上面增加 间隙锁

8a46afb99b01947742a59df0db812e43.png

 

在数据记录上面增加 行排他锁, 这里的 clust_rec 表示的是具体的数据记录, rec 表示是当前索引记录 

2d6fcb2407018bfc657fcab185e33464.png

 

遍历到不匹配 field1 = ‘field5’ 的第一个索引的地方, 在该记录上面增加了一个 间隙锁

c8381254fa12fe7ab9df47194c84cf67.png

 

因此如下 三个 sql 都会阻塞, 第一行是 获取索引的行排他锁 冲突, 第二行是 获取数据的行排他锁冲突, 第三行是与在索引 field1=’field9’ 上面的间隙锁冲突 

第四行, 第五行是与在索引 field1=’field5’ 上面的临键锁冲突 

select * from tz_test_04 where field1 = 'field5' for update;
select * from tz_test_04 where id = 5 for update;
INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (15, 'field8', '8');
INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (8, 'field3', '3');
INSERT INTO `test_02`.`tz_test_04`(`id`, `field1`, `field2`) VALUES (8, 'field5', '5');

 

 

行锁锁索引字段的情况

我们这里更新测试表结构如下 

CREATE TABLE `tz_test_04` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`field1` varchar(128) DEFAULT NULL,`field2` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,KEY `field_1_2` (`field1`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

 

然后执行 sql 如下 “select * from tz_test_04 where field2 = '5' for update;”

如果是根据 非索引字段查询, 则会进行全表扫描, 会在所有的行上面增加 行临键锁

9f78054aebf05d76e451a46bbb88fd32.png

 

在 row_search_mvcc 中输出各个断点位置的 rec 信息如下, 即为各个 记录的信息

00a6e95cbf740638e11957947aae68cf.png

 

 

行排他锁阻塞的 N 种方式

假设我们这里尝试模拟 各种阻塞的方式, 事务1先进行执行, 然后事务2尝试获取行排他锁, 产生阻塞 

事务2 这边执行固定的 sql 语句如下 

begin;
select * from t_user_02 where id = '2' for update;
-- sleep 10min
commit;

 

事务1获取 表共享锁 导致 事务2 获取 MDL元数据锁 阻塞 

begin;
lock tables t_user_02 read;
-- sleep 10min
unlock tables;
commit;

 

事务1获取 表排他锁 导致 事务2 获取 MDL元数据锁 阻塞 

begin;
lock tables t_user_02 write;
-- sleep 10min
unlock tables;
commit;

 

事务1 获取行共享锁 导致 事务2 获取 行排他锁 阻塞

begin;
select * from t_user_02 where id = '2' lock in share mode;
-- sleep 10min
commit;

 

事务1 获取行排他锁 导致 事务2 获取 行排他锁 阻塞

begin;
select * from t_user_02 where id = '2' for update;
-- sleep 10min
commit;

 

 

共享锁阻塞的 N 种方式

假设我们这里尝试模拟 各种阻塞的方式, 事务1先进行执行, 然后事务2尝试获取行共享锁, 产生阻塞 

事务2 这边执行固定的 sql 语句如下 

begin;
select * from t_user_02 where id = '2' lock in share mode;
commit;

 

事务1获取 表排他锁 导致 事务2 获取 MDL元数据锁 阻塞 

begin;
lock tables t_user_02 write;
-- sleep 10min
unlock tables;
commit;

 

事务1 获取行排他锁 导致 事务2 获取 行排他锁 阻塞

begin;
select * from t_user_02 where id = '2' for update;
-- sleep 10min
commit;

 

 

where 1 = 1 for update 和 where id = 1 for update 的区别 

首先是都会获取 表意向排他锁

首先来看一下 “where 1 = 1 for update” 情况下的一个获取锁的处理

然后 我们这里讨论的主要是 行排他锁 的获取的差异

是会走 “else if (!impl)” 然后里面的这个 get + set

比如这里获取的是 第三条记录的 行排他锁, 会设置 bitmap 的第三位, 更新之后 (lock+1) 会变成 0x04 | 0x08 = 0x0c

这个获取了所有记录的行锁 逻辑意义上 等价于获取了表锁, 但是 实际的实现二者又是有一定的区别的

cc2fcf347a086d7c3be9833b1f012f59.png

 

同理, 这里是获取 第四条记录的 行排他锁 

这里从当前状态可以看到 (lock+1) 为 0x0c, 表示获取了 第二条记录, 第三条记录 的 行排他锁

本次更新调整了之后, (lock + 1) 会变成 0x0c | 0x10 = 0x1c

8a565ad5d5d7c3e7c7ebb48204df23cf.png

 

获取第五条记录的 行排他锁 的情况如下, 这里就不在继续 向下赘述了

631fa8948226edb86ee731b920ec58e4.png

 

 

再来看一下 “where id = 1 for update” 情况下的一个获取锁的处理

这里限定的是 id, 只会获取到一条记录, 因此这里只会走 RecLock 初始化的这部分处理

04a29159aa990bed9703ac650c4e3d5c.png

 

 

 

 

 

相关文章:

63 mysql 的 行锁

前言 我们这里来说的就是 我们在 mysql 这边常见的 几种锁 行共享锁, 行排他锁, 表意向共享锁, 表意向排他锁, 表共享锁, 表排他锁 意向共享锁, 意向排他锁, 主要是 为了表粒度的锁获取的同步判断, 提升效率 意向共享锁, 意向排他锁 这边主要的逻辑意义是数据表中是否有任…...

ubuntu文件编辑操作

Vim 基本操作指南 在 vim 中打开文件后,可以按照以下步骤进行编辑和保存: 进入插入模式 打开文件后,默认情况下 vim 处于命令模式,无法直接输入文本。按下 i 键进入插入模式(会看到左下角显示 -- INSERT --&#xff0…...

Nuxt.js 应用中的 nitro:config 事件钩子详解

title: Nuxt.js 应用中的 nitro:config 事件钩子详解 date: 2024/11/2 updated: 2024/11/2 author: cmdragon excerpt: nitro:config 是 Nuxt 3 中的一个生命周期钩子,允许开发者在初始化 Nitro 之前自定义 Nitro 的配置。Nitro 是 Nuxt 3 的服务器引擎,负责处理请求、渲…...

【前端】项目中遇到的问题汇总(长期更新)

一、联调交互类 1、出现一个数据在当前页面进行了修改,另外一个页面的同一数据并未同步更改 当前的数据经过调用接口修改更新以后,if(code 200) 将当前数据存入store.dispatch, 然后另一个地方获取该数据,直接获取存入的数据,这…...

DAY73WEB 攻防-支付逻辑篇篡改属性值并发签约越权盗用算法溢出替换对冲

知识点: 1、支付逻辑-商品本身-修改-数量&价格&属性等 2、支付逻辑-营销折扣-优惠券&积分&签约&试用等 3、支付逻辑-订单接口-替换&并发&状态值&越权支付等 支付逻辑常见测试: 熟悉常见支付流程:选择商品…...

2024 Rust现代实用教程:Ownership与结构体、枚举

文章目录 一、Rust的内存管理模型1.GC(Stop the world)2.C/C内存错误大全3.Rust的内存管理模型 二、String与&str1.String与&str如何选择2.Example 三、枚举与匹配模式1.常见的枚举类型:Option和Result2.匹配模式 四、结构体、方法、…...

MMed-RAG:专为医学视觉语言模型设计的多功能多模态系统

MMed-RAG:专为医学视觉语言模型设计的多功能多模态系统 论文大纲提出背景全流程优化空雨伞分析空:观察现象层雨:分析原因层伞:解决方案层 三问分析WHAT - 问题是什么?WHY - 原因是什么?HOW - 如何解决&…...

数据采集(全量采集和增量采集)

全量采集:采集全部数据 3、全量采集 vim students_all.json {"job": {"setting": {"speed": {"channel": 1},"errorLimit": {"record": 0,"percentage": 0.02}},"content": [{…...

GPT-Sovits-1-数据处理

1.1 切割音频 将音频切割为多个10s内的片段 1.2 降噪 这一步用的是modelscope的pipeline 如果要去除背景音,可以用傅立叶转为为频谱,去除低频部分后再转回来 1.3 提取音频特征 这里用到了 funasr 库 这一步目的是输出音频样本的《文本标签文件》&am…...

web前端多媒体标签设置(图片,视频,音频)以及图片热区(usemap)的设置

多媒体标签运用 在HTML中有以下常见多媒体标签&#xff1a; <img> &#xff08;图像标签&#xff09; - 作用&#xff1a;用于在网页中嵌入图像。 - 示例&#xff1a; <img src"image.jpg" alt"这是一张图片"> 。其中 src 属性指定图像的…...

尚硅谷react教程_扩展_stateHook

1.类式组件写 import React, {Component} from react;export default class Demo extends Component {state {count:0}add () > {this.setState(state>({count:state.count1}))}render() {return (<div><h2>当前求和为{this.state.count}</h2><b…...

专线物流公共服务平台:数据驱动,标准引领,共创金融双赢新时代

专线物流公共服务平台&#xff1a;数据驱动&#xff0c;标准引领&#xff0c;共创金融双赢新时代 在当今这个数据驱动、标准引领、金融赋能的经济发展新时代&#xff0c;专线物流作为商贸流通领域的重要一环&#xff0c;正面临着前所未有的机遇与挑战。为应对复杂多变的市场环…...

界面控件DevExpress JS ASP.NET Core v24.1亮点 - 支持Angular 18

DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#xff0c;使您可以利用现代Web开发堆栈&#xff08;包括React&#xff0c;Angular&#xff0c;ASP.NET Core&#xff0c;jQuery&#xff0c;Knockout等&#xff09;构建交互式的Web应用程序。从Angular和Reac&#xff0c…...

Spring之依赖注入(DI)和控制反转(IoC)——配置文件、纯注解

依赖注入 依赖注入(Dependency Injection&#xff0c;简称 DI)与控制反转(loC)的含义相同&#xff0c;只不过这两 个称呼是从两个角度描述的同一个概念。对于一个 Spring 初学者来说&#xff0c;这两种称呼很难理解, 下面我们将通过简单的语言来描述这两个概念。 当Java对象&…...

基于SpringBoot的宠物健康咨询系统的设计与实现

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;宠物健康知识信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不…...

Lucene的使用方法与Luke工具(2)

文章目录 第2章 Lucene快速入门2.1 项目搭建2.1.1 SQL语句2.1.2 maven依赖2.1.3 实体类&#xff1a;2.1.4 编写DAO&#xff1a; 2.2 建立索引2.2.1 步骤&#xff1a;2.2.2 实现代码&#xff1a; 2.3 Luke工具2.3.1 运行界面介绍&#xff1a;1&#xff09;主界面2&#xff09;文…...

【客户端开发】electron 中无法使用 js-cookie 的问题

产生问题的原因 谷歌浏览器升级之后&#xff0c;出于安全考虑&#xff0c;cookie的SameSite属性默认值由None变为Lax&#xff0c;对于跨域的请求&#xff0c;禁止携带cookie。electron内核是chromium内核,所以也会有这个限制。 Cookie的SameSite属性用来限制第三方 Cookie&…...

kafka客户端消费者吞吐量优化

问题背景 业务场景 mq消息消费实时性要求不高&#xff0c;期望可以牺牲一部分实时性&#xff0c;换取吞吐量&#xff0c;例如&#xff1a;数据库单条insert优化为batchInsert。优化后结果不符合预期&#xff1a;消费者消费消息的batchSize远小于实际配置的max.poll.records&a…...

电子工程师-高质量工具包

目录 来源 高质量工具包介绍 总体框架如下 ZL01-各类元器件相关资料 ZL02-电源设计资料 ZL03-大厂参考资料 ZL04-开发工具 ZL05-仿真工具 ZL06-各类电路接口设计指南 ZL07-付费专栏全集 ZL08-优质电子书 ZL09-硬件工程师 ZL10FPGA工程师教程 ZL10-PCB设计教程 Z…...

简单认识redis - 12 redis锁

在斜体样式**redis中&#xff0c;不同的问题有不一样的解决办法&#xff0c;那么锁也有不同的锁来解决不一样的问题&#xff0c;下面将举出几个常用的redis锁。 1. SETNX锁&#xff08;简单独占锁&#xff09; 原理&#xff1a; SETNX&#xff08;SET if Not eXists&#xff…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测

uniapp 中配置 配置manifest 文档&#xff1a;manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号&#xff1a;4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...