MySQL 事务原理:锁机制

news2024/11/24 15:33:20

文章目录

  • 一、锁类型
    • 1.1 全局锁
    • 1.2 表级锁
      • 1.2.1 表锁
      • 1.2.2 元数据锁
      • 1.2.3 意向锁
      • 1.2.4 自增锁
    • 1.3 行级锁
      • 1.3.1 记录锁
      • 1.3.2 间隙锁
      • 1.3.3 临键锁
      • 1.3.4 插入意向锁
    • 1.4 锁的兼容性
  • 二、锁的CUDP
    • 2.1 查询
    • 2.2 删除、更新
    • 2.3 插入
  • 三、锁的对象
  • 四、并发死锁
    • 4.1 相反加锁顺序导致死锁
    • 4.2 锁冲突导致死锁
    • 4.3 如何避免死锁
    • 4.4 例子

锁机制用于管理对共享资源的并发访问,实现事务的隔离级别 。

一、锁类型

MySQL当中事务采用的是粒度锁:针对表(B+树)、页(B+树叶子节点)、行(B+树叶子节点当中某一段记录行)三种粒度加锁。

因此可分为全局锁、表级锁和行级锁。全局锁是针对数据库加锁,表级锁是针对表或页进行加锁;行级锁是针对表的索引加锁。

1.1 全局锁

全局锁(Global Lock)是一种数据库锁机制,它可以锁定整个数据库系统,阻止其他事务对数据库进行写入或修改操作。当一个事务获取到全局锁时,其他事务将无法执行任何对数据库写入的操作,直到全局锁被释放。

-- 全局锁,整个数据库处于只读状态,其他操作均阻塞
FLUSH TABLES WITH READ LOCK

-- 释放全局锁
UNLOCK TABLES

全局锁用于全库逻辑备份。这样在备份数据库期间,不会因为数据或表结构的更新,而出现备份文件的数据与预期的不一样。

但是在备份期间,业务只能读数据,不能更新数据, 造成业务停滞。

1.2 表级锁

表级锁又分为表锁、元数据锁、意向锁、自增锁。

1.2.1 表锁

表锁(Table-level lock)用于锁定整个表,控制对表的并发访问。当一个事务获取到表级锁时,其他事务将被阻塞,无法同时对该表进行写操作或修改操作。

表锁是一种粗粒度的锁,适用于需要对整张表进行操作的场景。

LOCK TABLES 表名 READ|WRITE

UNLOCK TABLES

1.2.2 元数据锁

元数据锁(MetaData Lock)用于保护数据库对象的元数据(如表结构、索引信息等)。当一个事务获取到元数据锁后,其他事务将无法修改该元数据,直到锁被释放。元数据锁将阻止并发事务对元数据的并行修改,防止出现不一致或损坏的元数据状态。

1.2.3 意向锁

意向锁(Intention Lock)用于快速判断表里是否有记录加锁。

当一个事务要获取一个表中某些行的排他锁或共享锁时,它需要首先获取该表的意向锁,可以快速判断表里是否有行记录加锁,从而避免全表扫描查询是否有记录被加锁了。

意向锁分为两种类型:

∘ \circ 意向共享锁(Intent Shared Lock,IS):一个事务要获取某表中某些行的共享锁时,需要先获取该表的意向共享锁。意向共享锁不互斥,多个事务可以同时获取。

∘ \circ 意向排他锁(Intent Exclusive Lock,IX):一个事务要获取某表中某些行的排他锁时,需要先获取该表的意向排他锁。意向排他锁与意向共享锁互斥,当一个事务持有意向排他锁时,其他事务无法获取该表的意向排他锁或意向共享锁。目的:为了告诉其他事务,此时这条表被一个事务在访问;作用:排除表级别读写锁 (全面扫描加锁)

例如,当一个事务要获取某表的排他锁时,它可以先检查是否已经有其他事务持有意向共享锁,如果有,则可以知道在表级上可能存在其他事务正在读取,而不必尝试获取排他锁。

1.2.4 自增锁

自增锁(Auto-Increment Lock)实现自增约束,当往表插入数据时会使用auto-inc锁来加锁,语句结束后释放锁,而不是在事务结束时释放(这和行级锁有区别,行级锁是在事务结束才释放锁)。

当一个事务要插入新数据并获取下一个自增值时,它首先会获取一个自增锁。一旦事务获得了自增锁,它就可以安全地执行插入操作,并确保每次插入都会得到一个唯一且连续的自增值。其他事务在等待自增锁被释放之前,无法获取下一个自增值,从而避免了冲突和重复。

但是,自增锁在对大量数据进行插入操作时,阻塞其他事务的插入操作,影响性能。因此, 在 Mysql 5.1.22 版本后仅对 AUTO_INCREMENT字段加上轻量级锁,当字段自增后,立即释放锁,而不需要等待整个插入语句执行完后才释放锁。

1.3 行级锁

行级锁的类型有

  • 记录锁,也就是仅仅把一条(行)记录锁上;
  • 间隙锁,锁定一个范围,但是不包含记录本身;
  • 临键锁:记录锁 + 间隙锁的组合,锁定一个范围,并且锁定记录本身。

1.3.1 记录锁

记录锁(record lock),所以一行记录,可分为:
共享锁
共享锁(Shared Lock,也称为读锁、S锁):多个事务可以同时获取同一行的共享锁,用于读取数据。共享锁之间不互斥,多个事务可以同时持有共享锁并进行读操作,但无法同时持有排他锁或修改锁。

1)在 SERIALIZABLE 隔离级别下,默认帮读操作加共享锁;
2)在 REPEATABLE READ 隔离级别下,需手动加共享锁,可解决幻读问题;
3)在 READ COMMITTED 隔离级别下,没必要加共享锁,采用的是 MVCC;
4)在 READ UNCOMMITTED 隔离级别下,既没有加锁也没有使用MVCC;

排他锁

排他锁(Exclusive Lock,也称为写锁、X锁):只有一个事务可以获取同一行的排他锁,用于修改数据。排他锁与共享锁和其他排他锁互斥,一个事务持有排他锁时,其他事务无法获取共享锁或排他锁。

在4种隔离级别下,都添加了排他锁,事务提交或事务回滚后释放锁。

1.3.2 间隙锁

间隙锁(Gap Lock)用于锁定两个索引键之间的空隙(即不存在的值)。主要是锁范围,但不包含记录本身,是全开区间。RR级别及以上支持。

当一个事务执行范围查询时,可能存在其他事务在查询结果范围内插入新行或修改已有行。为了防止幻读(Phantom Read)的情况发生,间隙锁可以锁定这些空隙,以确保数据的一致性。
(解决了快照读的幻读问题。但对于当前读,仍需要手动加锁 )

1.3.3 临键锁

临键锁(Next-Key Lock),记录锁 与 间隙锁的组合,用于锁范围和记录。包含记录本身是左开右闭区间。RR级别及以上支持,解决了幻读问题。

1.3.4 插入意向锁

插入意向锁(Insert Intention Lock)是一种间隙锁形式的意向锁,在insert 操作的时候产生。在多事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。

假设有一个记录索引包含键值 4 和 7,两个不同的事务分别插入5 和 6,每个事务都会产生一个加在 4-7 之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。

1.4 锁的兼容性

1.4.1 兼容性1

SXISIXAI
S兼容冲突兼容冲突冲突
X冲突冲突冲突冲突冲突
IS兼容冲突兼容兼容兼容
IX冲突冲突兼容兼容兼容
AI冲突冲突兼容兼容冲突

由于 innodb 支持的是行级别的锁,意向锁并不会阻塞除了全表扫描以外的任何请求。
1)意向锁之间是互相兼容的。
2)IS 只对排他锁不兼容。
3)当想为某一行添加 S 锁,先自动为所在的页和表添加意向锁IS,再为该行添加 S 锁。
4)当想为某一行添加 X 锁,先自动为所在的页和表添加意向锁IX,再为该行添加 X 锁。
5)当事务试图读或写某一条记录时,会先在表上加上意向锁,然后才在要操作的记录上加上读锁或写锁。这样判断表中是否有记录加锁就很简单了,只要看下表上是否有意向锁就行了。意向锁之间是不会产生冲突的,也不和 AUTO_INC 表锁冲突,它只会阻塞表级读锁或表级写锁,另外,意向锁也不会和行锁冲突,行锁只会和行锁冲突。

1.4.2 兼容性2

Gap 持有Insert Intention 持有Record 持有Next-key 持有
Gap 请求兼容兼容兼容兼容
Insert Intention 请求冲突兼容兼容冲突
Record 请求兼容兼容冲突冲突
Next-key 请求兼容兼容冲突冲突

横向:表示已经持有的锁;纵向:表示正在请求的锁;

一个事务已经获取了插入意向锁,对其他事务是没有任何影响的;

一个事务想要获取插入意向锁,如果有其他事务已经加了 gap lock 或 Next-key lock 则会阻塞;这个是重死锁之源;

二、锁的CUDP

2.1 查询

  • MVCC:undo log 实现历史版本记录
  • S 锁:lock in share mode
  • X 锁:for update
  • 不做任何处理:读未提交隔离级别使用的策略

2.2 删除、更新

自动添加 X 锁

2.3 插入

  • 插入意向锁:特殊的间隙锁,同时会使用 X 锁。
  • 自增锁:特殊表锁实现

三、锁的对象

行级锁是针对表的索引加锁,索引包括聚集索引和辅助索引。

表级锁是针对页或表进行加锁。

重点讨论 InnoDB 在 read committed 和 repeatable read 级别下锁的情况。

假设存在如下的students 表作为实例,其中 id 为主键,no(学号)为辅助唯一索引,name(姓名)和 age(年龄)为二级非唯一索引,score(学分)无索引。

idnonameagescore
15S0001Bob2534
18S0002Alice2477
20S0003Jim245
30S0004Eric2391
37S0005Tom2222
49S0006Tom2583
50S0007Rose2389

1)聚集索引,查询命中: UPDATE students SET score = 100 WHERE id = 15;
命中就进行写操作,也就是加X锁
在这里插入图片描述
2)聚集索引,查询未命中: UPDATE students SET score = 100 WHERE id = 16;
未命中,加gap锁。RC级别没有gap锁,不加。
RR级别加在(15,18)间加gap锁,阻止其他事务在这个区间操作(修改、插入),避免幻读。
在这里插入图片描述
3)辅助唯一索引,查询命中: UPDATE students SET score =100 WHERE no = ‘S0003’;
在这里插入图片描述
4)辅助唯一索引,查询未命中: UPDATE students SET score = 100 WHERE no = ‘S0008’;
在这里插入图片描述
5)辅助非唯一索引,查询命中: UPDATE students SET score = 100 WHERE name = ‘Tom’;

RR级别下,可重复读,因此需要加gap锁,阻止其他事务再插入Tom
在这里插入图片描述
6)辅助非唯一索引,查询未命中: UPDATE students SET score = 100 WHERE name = ‘John’;
RR级别,按字典顺序,在JIm和Rose间插入gap锁
在这里插入图片描述
7)无索引: UPDATE students SET score = 100 WHERE score = 22;
在无索引的情况下,全表查询,按扫描顺序,逐行加锁,效率最低。
在这里插入图片描述

8)聚集索引,范围查询: UPDATE students SET score = 100 WHERE id <= 20;
在这里插入图片描述

9)辅助索引,范围查询: UPDATE students SET score = 100 WHERE age <= 23;
在这里插入图片描述
注意:事务对聚集索引 B+ 树的范围查询是按序的,不会有死锁。但是对于辅助索引 B+ 树的修改却不一定有序,可能会导致死锁。比如事务A加锁顺序1、2,事务B加锁顺序2、1

10)修改索引值: UPDATE students SET name = ‘John’ WHERE id = 15;
在这里插入图片描述

四、并发死锁

死锁:两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。
MySQL 中采用 wait-for graph(等待图-采用非递归深度优先的图算法实现)的方式来进行死锁检测。

4.1 相反加锁顺序导致死锁

这种情况下有两种原因造成死锁:
1)不同表的加锁顺序相反

2)相同表不同行加锁顺序相反
相同表不同行加锁顺序相反造成死锁有很多变种,其中容易忽略的是给辅助索引行加锁的时候,同时会给聚集索引行加锁;同时还可能出现在外键索引时,给父表加锁,同时隐含给子表加锁;触发器同样如此,这些都需要视情况分析。

解决办法是:调整加锁顺序;

4.2 锁冲突导致死锁

innodb 在 RR 隔离级别下,最常见的是插入意向锁与 gap 锁冲突造成死锁;主要原理为:一个事务想要获取插入意向锁,如果有其他事务已经加了 gap lock 或 Next-key lock 则会阻塞;

解决办法是:更换语句或者降低隔离级别;

4.3 如何避免死锁

1)尽可能以相同顺序来访问索引记录和表

2)如果能确定幻读和不可重复读对应用影响不大,考虑将隔离级别降低为 RC

3)添加合理的索引,不走索引将会为每一行记录加锁,死锁概率非常大

4)尽量在一个事务中锁定所需要的所有资源,减小死锁概率

5)避免大事务,将大事务分拆成多个小事务;大事务占用资源多,耗时长,冲突概率变高

6)避免同一时间点运行多个对同一表进行读写的概率;

4.4 例子

例1

DROP TABLE IF EXISTS `account_t`;
CREATE TABLE `account_t` (
	`id` INT(11) NOT NULL,
	`name` VARCHAR(255) DEFAULT NULL,
	`money` INT(11) DEFAULT 0,
	PRIMARY KEY (`id`),
	KEY `idx_name` (`name`)
)ENGINE = INNODB AUTO_INCREMENT=0 DEFAULT CHARSET = utf8;


INSERT INTO `account_t` VALUES (1, 'C', 1000),(2, 'B', 1000),(3, 'A', 1000);

SELECT * FROM account_t;

-- 相反加锁顺序死锁1
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN
-- 死锁事务1
UPDATE `account_t` SET `money` = `money` - 100 WHERE `id` = 1;
-- 死锁事务2
-- UPDATE `account_t` SET `money` = `money` - 100 WHERE `id` = 2;
-- 死锁事务1
UPDATE `account_t` SET `money` = `money` + 100 WHERE `id` = 2;
-- 死锁事务2
-- UPDATE `account_t` SET `money` = `money` - 100 WHERE `id` = 1;

rollback

例2

DROP TABLE IF EXISTS `dl_mark_t`;
CREATE TABLE `dl_mark_t` (
  `a` INT(11) NOT NULL DEFAULT '0',
  `b` INT(11) DEFAULT NULL,
  PRIMARY KEY (`a`),
  UNIQUE KEY `uk_b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `dl_mark_t` VALUES (1,1),(5,4),(20,20),(25,12);

SELECT * from dl_mark_t;
-- 死锁情况 1
/*
  事务一在插入时由于跟事务二插入的记录唯一键冲突,所以对 b=10 这个唯一索引加 S 锁(Next-key)并处于锁等待,事务二再插入 b=9 这条记录,需要获取插入意向锁(lock_mode X locks gap before rec insert intention)和事务一持有的 Next-key 锁冲突,从而导致死锁。
*/
BEGIN
-- 死锁事务 2
insert into `dl_mark_t` values(26,10);  -- a=26不存在,插入的时候加了(26,+∞)的gap锁
-- 死锁事务 1
insert into `dl_mark_t` values(30,10);  -- 获取插入意向锁,与上面的gap锁冲突(记录唯一键冲突),对b加gap锁,进入锁等待,等待事务2释放gap锁
-- 死锁事务 2
insert into `dl_mark_t` values(40,9);   -- 获取插入意向锁,与事务1的gap锁冲突,对b加gap锁,进入锁等待,等待事务1释放gap锁


-- 死锁情况 2
/*
1. 三个事务依次执行 insert 语句,由于 b是唯一索引,所以后两个事务会出现唯一键冲突。
但此时要注意的是事务一还没有提交,所以并不会立即报错。事务二和事务三为了判断是否出现唯一键冲突,
必须进行一次当前读,加的锁是 Next-Key 锁,所以进入锁等待。要注意的是,就算在 RC 隔离级别下,一样会加 Next-Key 锁,所以说出现 GAP 锁不一定就是 RR 隔离级别;
3. 事务一回滚,此时事务二和事务三成功获取记录上的 S 锁;
4. 事务二和事务三继续执行插入操作,需要依次请求记录上的插入意向锁(插入意向锁和 GAP 锁冲突,所以事务二等待事务三,事务三等待事务二,形成死锁。
*/
BEGIN
-- 死锁事务 1
insert into `dl_mark_t` values(27, 29);
-- 死锁事务 2
insert into `dl_mark_t` values(28, 29);
-- 死锁事务 3
insert into `dl_mark_t` values(29, 29);
-- 死锁事务 1
ROLLBACK;

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

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

相关文章

监控Kafka的关键指标

Kafka 架构 上面绿色部分 PRODUCER&#xff08;生产者&#xff09;和下面紫色部分 CONSUMER&#xff08;消费者&#xff09;是业务程序&#xff0c;通常由研发人员埋点解决监控问题&#xff0c;如果是 Java 客户端也会暴露 JMX 指标。组件运维监控层面着重关注蓝色部分的 BROKE…

Redis 高频数据类型使用详解

目录 一、前言 二、Redis常用数据类型 2.1 常见的数据类型 三、String 类型 3.1 String 类型简介 3.2 String常用操作命令 3.2.1 String 操作命令实践 3.3 常用业务场景 3.3.1 session共享 3.3.2 登录失败计数器 3.3.3 限流 3.3.4 多线程安全控制 四、Hash类型 4…

【C++笔记】C++启航之为C语言填坑的语法

【C笔记】C启航之为C语言填坑的语法 一、命名空间1、为什么要引入命名空间&#xff1f;2、命名空间的基本用法3、展开命名空间4、命名空间的套娃5、命名空间的自动合并 二、缺省参数1、为什么要引入缺省参数&#xff1f;2、缺省参数的基本用法3、缺省的参数必须从右向左4、缺省…

为什么我们需要加快推进数字孪生技术?

数字孪生技术以其强大的潜力和应用前景&#xff0c;引起了各行各业的广泛关注和热切期待。那么&#xff0c;究竟为什么要加快推进数字孪生技术呢&#xff1f; 首先&#xff0c;数字孪生技术能够实现现实世界与虚拟世界的无缝连接&#xff0c;为各行业带来了前所未有的创新机遇…

Vue电商项目--VUE插件的使用及原理

图片懒加载 图片懒加载&#xff0c;就是图片延迟加载。只加载页面可视区域上的图片&#xff0c;等滚动到页面下面时&#xff0c;再加载对应视口上的图片 而在vue中有一个插件 vue-lazyload - npm (npmjs.com) npm i vue-lazyload 去使用他&#xff0c;这里我们引入了一张图片…

(九)人工智能应用--深度学习原理与实战--前馈神经网络实现MNST手写数字识别

目标: 识别手写体的数字,如图所示: 学习内容: 1、掌握MNIST数据集的加载和查看方法 2、熟练掌握Keras建立前馈神经网络的步骤【重点】 3、掌握模型的编译及拟合方法的使用,理解参数含义【重点】 4、掌握模型的评估方法 5、掌握模型的预测方法 6、掌握自定义图片的处理与预测 …

十分钟掌握使用 SolidJS 构建全栈 CRUD 应用程序

我们可以开始讨论 SolidJS&#xff0c;说它比React更好&#xff0c;但没有必要做这种比较。SolidJS只是众多前端框架之一&#xff0c;旨在在Web上快速创建数据驱动。那么&#xff0c;我们为什么要突出这个新孩子呢&#xff1f; 首先&#xff0c;我们不能忽视SolidJS不使用虚拟…

嗅探抓包工具,解决线上偶现问题来不及抓包的情况阅读目录

目录 背景 实现思路 具体实现 Python 抓包 总结 资料获取方法 背景 测试群里经常看到客户端的同学反馈发现了偶现Bug&#xff0c;但是来不及抓包&#xff0c;最后不了了之&#xff0c;最近出现得比较频繁&#xff0c;所以写个小脚本解决这个问题。 实现思路 之前写过一个…

免费实用的日记应用:Day One for Mac中文版

Day One for Mac是一款运行在Mac平台上的日记软件&#xff0c;你可以使用Day One for mac通过快速菜单栏条目、提醒系统和鼓舞人心的信息来编写更多内容&#xff0c;day one mac版还支持Dropbox同步功能&#xff0c;想要day one mac中文免费版的朋友赶紧来试试吧&#xff01; …

IPC之一:使用匿名管道进行父子进程间通信的例子

IPC 是 Linux 编程中一个重要的概念&#xff0c;IPC 有多种方式&#xff0c;本文主要介绍匿名管道(又称管道、半双工管道)&#xff0c;尽管很多人在编程中使用过管道&#xff0c;但一些特殊的用法还是鲜有文章涉及&#xff0c;本文给出了多个具体的实例&#xff0c;每个实例均附…

Leetcode.1316 不同的循环子字符串

题目链接 Leetcode.1316 不同的循环子字符串 rating : 1837 题目描述 给你一个字符串 text &#xff0c;请你返回满足下述条件的 不同 非空子字符串的数目&#xff1a; 可以写成某个字符串与其自身相连接的形式&#xff08;即&#xff0c;可以写为 a a&#xff0c;其中 a 是…

服务器感染了LockBit 3.0勒索病毒,如何确保数据文件完整恢复?

引言&#xff1a; 在数字时代&#xff0c;恶意软件的威胁变得愈发严峻&#xff0c;而LockBit 3.0勒索病毒则是其中的顶尖恶势力之一。其先进的加密技术和毫不留情的勒索手段&#xff0c;使无数人蒙受损失。然而&#xff0c;我们不应束手无策。本文91数据恢复将带您深入了解Loc…

AndroidStudio通过Profiler查找内存泄漏

Fragment内存泄漏&#xff1a; AndroidStudio --> Profiler --> 勾选 show nearest Gc root only&#xff0c;然后查看非weakreference的引用&#xff08;weakreference是不会导致内存泄漏的&#xff09;&#xff0c;往下就能找自己项目里写的代码&#xff0c;一般此处…

旷视科技AIoT软硬一体化走向深处,生态和大模型成为“两翼”?

齐奏AI交响曲的当下&#xff0c;赛道玩家各自精彩。其中&#xff0c;被称作AI四小龙的商汤科技、云从科技、依图科技、旷视科技已成长为业内标杆&#xff0c;并积极追赶新浪潮。无论是涌向二级市场还是布局最新风口大模型&#xff0c;AI四小龙谁都不甘其后。 以深耕AIoT软硬一…

ASCP系列电气防火限流式保护器在养老院的应用-安科瑞黄安南

摘要&#xff1a;2020年&#xff0c;我国65岁及以上老年人口数量为1.91亿&#xff0c;老龄化率达到13.5%。总体来看&#xff0c;大部分省市的养老机构数量还较少。养老设施的建设与民生息息相关&#xff0c;养老院的电气安全也非常重要。如果发生电气火灾&#xff0c;对于行动不…

【多模态】24、开放词汇学习到底是什么?

文章目录 一、什么是开放词汇学习二、开放词汇学习的测评和数据集三、开放词汇目标检测3.1 Region-Aware Training3.2 Pseudo-Labeling3.3 Knowledge Distillation-Based3.4 Transfer Learning-Based3.5 总结3.6 效果 参考论文&#xff1a;A Survey on Open-Vocabulary Detecti…

Vue3 事件处理简单应用

去官网学习→事件处理 | Vue.js 运行示例&#xff1a; 代码&#xff1a;HelloWorld.vue <template><div class"hello"><h1>Vue 事件处理</h1><button v-on:click"numb 1">点击加1-----{{ numb }}</button><br/&…

独家揭秘Linux内核栈:内核态的奇妙之处和与用户态的差异

理解Linux内核栈可以从以下几个方面来考虑&#xff1a;内核态与用户态&#xff1a;在阅读Linux内核及相关资料时&#xff0c;需要明确它所描述的是内核态还是用户态的内容。这有助于理解所讨论的是在哪个执行环境下进行的操作。进程与线程的描述&#xff1a;用户态的进程和线程…

Yield Guild Games:社区更新 — 2023 年第二季度

本文重点介绍了 Yield Guild Games (YGG) 2023 年第二季度社区更新中涵盖的关键主题&#xff0c;包括公会发展计划 (GAP) 第 3 季的总结、YGG 领导团队的新成员以及 YGG 的最新消息地区公会网络和广泛的游戏合作伙伴生态系统。 在 YGG 品牌焕然一新的基础上&#xff0c;第二季…

ArcGIS Pro基础:【按顺序编号】工具实现属性字段的编号自动赋值

本次介绍一个字段的自动排序编号赋值工具&#xff0c;基于arcgis 的字段计算器工具也可以实现类似功能&#xff0c;但是需要自己写一段代码实现&#xff0c; 相对而言不是很方便。 如下所示&#xff0c;该工具就是【编辑】下的【属性】下的【按顺序编号】工具。 其操作方法是…