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

【Mysql】死锁问题详解

【Mysql】死锁问题详解

  • 【一】Mysql中锁分类和加锁情况
    • 【1】按锁的粒度分类
      • (1)全局锁
      • (2)表级锁
        • 1、表共享读锁(Table Read Lock)
        • 2、表独占写锁(Table Write Lock)
        • 3、元数据锁(MDL)
        • 3、意向锁(Intention Lock)
      • (3)行级锁
        • 1、记录锁(Record Lock)
        • 2、间隙锁(Gap Lock)
        • 3、临键锁(Next-Key Lock)
    • 【2】按锁的模式分类
      • (1)共享锁(S 锁)
      • (2)排他锁(X 锁)
  • 【二】加锁方式的影响因素
  • 【三】Mysql的死锁情况
    • 【1】事务交叉更新导致死锁
      • (1)情况描述
      • (2)死锁原因
      • (3)解决方案
    • 【2】索引使用不当导致死锁
      • (1)情况描述
      • (2)死锁原因
      • (3)解决方案
    • 【3】并发插入导致的死锁
      • (1)情况描述
      • (2)解决方案
    • 【4】外键约束引发的死锁
      • (1)情况描述
      • (2)解决方案
    • 【5】⭐️删除不存在的数据导致间隙锁
      • (1)情况描述
      • (2)背景信息
      • (3)mysql锁基本概念
      • (4)死锁原因
      • (5)案例扩展
      • (6)解决方案
    • 【6】同一个事务中多条update修改同一条记录
      • (1)情况描述
      • (2)死锁原因
      • (3)原因汇总
      • (4)解决方案
  • 【四】排查线上死锁问题
    • 【1】查看死锁日志
    • 【2】使用 SHOW ENGINE INNODB STATUS 命令
    • 【3】开启 innodb_print_all_deadlocks 参数
  • 【五】解决死锁问题
    • 【1】优化事务逻辑
    • 【2】减少事务持有锁的时间
    • 【3】调整隔离级别
    • 【4】优化索引

【一】Mysql中锁分类和加锁情况

【1】按锁的粒度分类

(1)全局锁

加锁情况:使用 FLUSH TABLES WITH READ LOCK 语句,它会对整个数据库实例加锁,使整个数据库处于只读状态。常用于全量备份等场景,确保备份期间数据的一致性。

示例:

FLUSH TABLES WITH READ LOCK;
-- 进行备份操作
UNLOCK TABLES;

(2)表级锁

1、表共享读锁(Table Read Lock)

(1)特点
1、允许多个事务同时对同一个表加共享读锁,即可以有多个事务同时读取该表的数据。
2、持有共享读锁的事务只能对表进行读操作,不能进行写操作。并且在持有该锁期间,不能访问其他未被锁定的表。
3、其他事务也可以读取该表,但如果要对该表进行写操作,则需要等待所有共享读锁释放。

(2)加锁情况
使用 LOCK TABLES table_name READ 语句,其他事务可以读取该表,但不能写入,当前持有读锁的事务也不能写入其他表。常用于多个事务同时读取同一表,且不允许有写操作的场景。

示例:

-- 会话 1
LOCK TABLES users READ;
SELECT * FROM users;
-- 若尝试写入,会报错
-- UPDATE users SET name = 'new_name' WHERE id = 1; 
UNLOCK TABLES;-- 会话 2
SELECT * FROM users; -- 可以正常读取
2、表独占写锁(Table Write Lock)

(1)特点
1、同一时间只有一个事务能对表加独占写锁。
2、持有该锁的事务可以对表进行读写操作,在其释放锁之前,其他事务无法对该表进行任何读写操作。

(2)加锁情况
使用 LOCK TABLES table_name WRITE 语句,持有该锁的事务可以对表进行读写操作,其他事务不能对该表进行读写,直到锁释放。用于对表进行数据修改,需要保证数据一致性的场景。

示例:

-- 会话 1
LOCK TABLES users WRITE;
SELECT * FROM users;
UPDATE users SET name = 'new_name' WHERE id = 1;
UNLOCK TABLES;-- 会话 2
-- 若在会话 1 持有写锁期间尝试读写,会被阻塞
SELECT * FROM users; 
3、元数据锁(MDL)

(1)特点
1、分为共享元数据锁(Shared MDL)和排他元数据锁(Exclusive MDL)。当对表进行 SELECT、INSERT、UPDATE、DELETE 等操作时,会自动加共享 MDL 锁;当对表结构进行修改(如 ALTER TABLE)时,会加排他 MDL 锁。
2、共享 MDL 锁之间可以共存,即多个事务可以同时对同一个表加共享 MDL 锁进行读写操作。但排他 MDL 锁与其他任何类型的 MDL 锁都互斥,也就是说,当一个事务持有排他 MDL 锁时,其他事务无法对该表进行任何操作,直到排他 MDL 锁释放。

(2)加锁情况
当对表进行 SELECT、INSERT、UPDATE、DELETE 等操作时,会自动加共享 MDL 锁;当对表结构进行修改(如 ALTER TABLE)时,会加排他 MDL 锁。目的是保证在表结构修改时,不会有其他事务对表进行读写操作,避免数据不一致。

-- 会话 1
START TRANSACTION;
SELECT * FROM users; -- 自动加共享 MDL 锁
-- 此时会话 2 可以进行读操作,但不能进行表结构修改-- 会话 2
-- 可以正常读取
SELECT * FROM users; 
-- 若执行 ALTER TABLE 会被阻塞
-- ALTER TABLE users ADD COLUMN new_column VARCHAR(255); -- 会话 1 提交事务释放共享 MDL 锁
COMMIT;
3、意向锁(Intention Lock)

(1)加锁方式
是一种表级别的锁,在使用行级锁时会自动添加相应的意向锁。

(2)特点
1、分为意向共享锁(IS)和意向排他锁(IX)。当事务要对表中的某一行加共享锁时,会先对表加意向共享锁;当事务要对表中的某一行加排他锁时,会先对表加意向排他锁。
2、意向锁的作用是表明某个事务正在对表中的行进行加锁操作,这样在对表加更高级别的锁(如表级共享锁或表级排他锁)时,可以快速判断表中是否有行被锁定,从而避免全表扫描。

(3)示例

-- 会话 1
START TRANSACTION;
SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 自动对表加意向排他锁
-- 会话 2 尝试对表加表级共享读锁会被阻塞
-- LOCK TABLES users READ; 
COMMIT;

(3)行级锁

1、记录锁(Record Lock)

(1)定义
记录锁是对索引记录的锁定,也就是对表中某一行数据的索引项加锁。需要注意的是,记录锁总是会锁定索引记录,如果表没有设置索引,MySQL 会自动创建一个隐藏的聚簇索引来使用。

(2)加锁情况
在可重复读或串行化隔离级别下,使用 SELECT … FOR UPDATE 或 UPDATE、DELETE 等语句对满足条件的行记录加锁。例如:

SELECT * FROM table_name WHERE id = 1 FOR UPDATE;

执行 UPDATE、DELETE 语句时,也会对操作的行记录加记录锁。示例如下:

UPDATE users SET name = 'John' WHERE id = 1;
DELETE FROM users WHERE id = 1;
2、间隙锁(Gap Lock)

(1)定义
间隙锁锁定的是索引记录之间的间隙,其目的在于防止其他事务在该间隙插入新记录,从而避免幻读问题。

(2)加锁情况
在可重复读隔离级别下,当使用范围查询(如 WHERE id BETWEEN 1 AND 10)时,为了防止幻读,会对查询范围的间隙加锁。例如:

SELECT * FROM table_name WHERE id BETWEEN 1 AND 10 FOR UPDATE;

此语句不仅会对 id 在 1 到 10 之间的行记录加锁,还会对这些记录之间的间隙加锁,防止其他事务插入新的 id 在这个范围内的记录。

3、临键锁(Next-Key Lock)

(1)定义
临键锁是记录锁和间隙锁的组合,它会锁定索引记录本身以及该记录前面的间隙。

(2)加锁情况
是记录锁和间隙锁的组合,在可重复读隔离级别下,对索引记录和其前面的间隙加锁。常用于范围查询和唯一性检查,防止幻读和插入异常。

SELECT * FROM users WHERE id > 10 FOR UPDATE;

这个语句会对 id 大于 10 的行记录及其前面的间隙加临键锁。

【2】按锁的模式分类

(1)共享锁(S 锁)

加锁情况:使用 SELECT … LOCK IN SHARE MODE 语句对读取的行记录加共享锁,多个事务可以同时对同一行记录加共享锁,但不能同时加排他锁。例如:

SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE;

(2)排他锁(X 锁)

加锁情况:使用 SELECT … FOR UPDATE、UPDATE、DELETE 等语句对操作的行记录加排他锁,一旦某事务对行记录加了排他锁,其他事务既不能读取也不能修改该行记录,直到排他锁被释放。

【二】加锁方式的影响因素

(1)隔离级别(Innodb默认:可重复读-REPEATABLE READ)
不同的隔离级别对锁的使用和加锁范围有影响。
例如,可重复读隔离级别会使用间隙锁和临键锁来防止幻读,而读提交隔离级别则不会。

(2)查询语句
查询条件、索引使用情况等会影响加锁的范围和粒度。如果使用索引进行精确匹配,可能只对匹配的行记录加锁;如果是范围查询,可能会加间隙锁或临键锁。

(3)事务操作
不同的事务操作(如 SELECT、INSERT、UPDATE、DELETE)会触发不同类型的锁。例如,INSERT 操作可能会对插入位置的间隙加锁,UPDATE 和 DELETE 操作会对操作的行记录加排他锁。

【三】Mysql的死锁情况

【1】事务交叉更新导致死锁

(1)情况描述

假设有两个事务 T1 和 T2,以及一个表 accounts 包含 id 和 balance 两列。

-- 事务 T1
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE id = 1;
UPDATE accounts SET balance = balance - 100 WHERE id = 2;
COMMIT;-- 事务 T2
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

(2)死锁原因

死锁原因
(1)T1 先对 id = 1 的行加排他锁,然后尝试对 id = 2 的行加排他锁;
(2)T2 先对 id = 2 的行加排他锁,然后尝试对 id = 1 的行加排他锁。
(3)此时,T1 等待 T2 释放 id = 2 的锁,而 T2 等待 T1 释放 id = 1 的锁,从而形成死锁。

(3)解决方案

【2】索引使用不当导致死锁

(1)情况描述

有一个表 orders 包含 order_id 和 product_id 两列,product_id 上有索引。

-- 事务 T1
START TRANSACTION;
UPDATE orders SET status = 'paid' WHERE product_id = 1;
UPDATE orders SET status = 'shipped' WHERE product_id = 2;
COMMIT;-- 事务 T2
START TRANSACTION;
UPDATE orders SET status = 'paid' WHERE product_id = 2;
UPDATE orders SET status = 'shipped' WHERE product_id = 1;
COMMIT;

(2)死锁原因

死锁原因:
由于 product_id 上有索引,更新操作会对索引记录和间隙加锁。T1 和 T2 按照不同的顺序对 product_id 进行更新,导致锁的获取顺序不一致,从而可能形成死锁。

(3)解决方案

【3】并发插入导致的死锁

(1)情况描述

在可重复读隔离级别下,两个事务同时向一张有唯一索引的表中插入数据,且插入的数据在唯一索引列上有冲突。InnoDB 为了保证数据的一致性,会使用间隙锁,这可能导致死锁。

-- 事务T1
START TRANSACTION;
INSERT INTO unique_table (id, value) VALUES (1, 'value1');-- 事务T2
START TRANSACTION;
INSERT INTO unique_table (id, value) VALUES (1, 'value2');

(2)解决方案

可以考虑将隔离级别调整为读提交,但需要注意这可能会导致幻读问题。

或者在插入数据前,先进行唯一性检查,避免插入冲突的数据。

【4】外键约束引发的死锁

(1)情况描述

有两张表,主表 A 和从表 B,从表 B 有外键关联到主表 A。当两个事务分别对主表和从表进行插入和删除操作时,由于外键约束的检查,可能会导致死锁。

示例代码:

-- 事务T1
START TRANSACTION;
INSERT INTO tableA (id, name) VALUES (1, 'name1');
-- 假设这里有一些耗时的操作
DELETE FROM tableB WHERE id = 1;-- 事务T2
START TRANSACTION;
INSERT INTO tableB (id, a_id, value) VALUES (1, 1, 'value1');
-- 假设这里有一些耗时的操作
DELETE FROM tableA WHERE id = 1;

(2)解决方案

确保在进行涉及外键关系的操作时,按照主表和从表的正确顺序进行操作,或者使用级联操作来简化事务中的操作,减少锁的竞争。

【5】⭐️删除不存在的数据导致间隙锁

(1)情况描述

⭐️先delete,再insert,导致死锁

实例的日志记录表,实例在重跑的时候,会先根据instanceId去delete该实例关联的全部旧的记录信息,然后再陆续插入新的记录信息,instanceId有索引,出现锁超时的情况。在删除的时候根据实例id删除,但是记录可能不存在,如果删除的记录在数据库中存在,那么产生的就是普通的行锁;当删除的这条记录不存在,会在删除记录所在的区间加间隙锁。

(2)背景信息

MySQL版本:Percona MySQL Server 5.7.19
隔离级别:可重复读(RR)
业务逻辑:并发下按某个索引字段先delete记录,再insert记录

begin;
delete from tb where order_id = xxx;
insert into tb(order_id) values(xxx);
commit;

(3)mysql锁基本概念

S:共享锁(行级锁)
X:排他锁(行级锁)
IS:意向共享锁(表级锁),使用行级锁时会自动添加相应的意向锁
IX:意向排他锁(表级锁),使用行级锁时会自动添加相应的意向锁

锁模式兼容性表
gap锁与gap锁之间不冲突
rec insert intention(插入意向锁)与gap锁冲突。

(4)死锁原因

打开参数,从innodb status获取更多的锁信息。
set GLOBAL innodb_status_output_locks=ON;

表结构:

 CREATE TABLE `tb` (`order_id` int(11) DEFAULT NULL,KEY `idx_order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

表中数据:

mysql> select * from tb;
+----------+
| order_id |
+----------+
|       10 |
|       20 |
+----------+
2 rows in set (0.00 sec)

事务执行步骤:
(1)开启两个事务
(2)两个事务分别删除两个个不存在的记录
(3)两个事务分别插入该记录
在这里插入图片描述
当session1执行delete from tb where order_id=15;,由于条件order_id=15的记录不存在,session1 获得2个锁结构,分别是意向排他锁IX(表级锁)、gap锁(行级锁),如下:

---TRANSACTION 1055191443, ACTIVE 20 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 315642, OS thread handle 139960342456064, query id 150462030 localhost root
TABLE LOCK table `db`.`tb` trx id 1055191443 lock mode IX
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191443 lock_mode X locks gap before rec

当session2执行delete from tb where order_id=15;,同样由于order_id=15的记录不存在,session2 也获得2个锁结构,分别是意向排他锁IX(表级锁)、gap锁(行级锁),如下:

---TRANSACTION 1055191444, ACTIVE 3 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 315336, OS thread handle 139960562685696, query id 150462412 localhost root
TABLE LOCK table `db`.`tb` trx id 1055191444 lock mode IX
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec

当session2执行insert into tb select 15;, session2 已经获取到IX锁,gap锁,等待 rec insert intention(插入意向锁)

---TRANSACTION 1055191444, ACTIVE 68 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 315336, OS thread handle 139960562685696, query id 150462778 localhost root executing
insert into tb select 15
------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec insert intention waiting
------------------
TABLE LOCK table `db`.`tb` trx id 1055191444 lock mode IX
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec insert intention waiting

当session1执行insert into tb select 15;,session1 已获取到IX锁,gap锁, 等待rec insert intention(插入意向锁), session1, session2 都在等待插入意向锁, 插入意向锁与gap锁冲突,双方都没有释放gap锁,又都在等待插入意向锁,死锁发生。

LATEST DETECTED DEADLOCK
------------------------
2018-11-03 17:15:11 0x7f4b0e7ea700
*** (1) TRANSACTION:
TRANSACTION 1055191444, ACTIVE 135 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 315336, OS thread handle 139960562685696, query id 150462778 localhost root executing
insert into tb select 15
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191444 lock_mode X locks gap before rec insert intention waiting
*** (2) TRANSACTION:
TRANSACTION 1055191443, ACTIVE 201 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 315642, OS thread handle 139960342456064, query id 150463172 localhost root executing
insert into tb select 15
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191443 lock_mode X locks gap before rec
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191443 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (2)

(5)案例扩展

以上死锁案例,业务代码逻辑是多线程并发下,有可能多个线程会执行相同order_id的job,比如两个线程执行的order_id 都是15。
另外一种情况,多个线程间,不会执行到相同order_id的情况,也可能发生死锁。比如一个线程order_id=15,另外一个线程order_id=16,如下所示:
在这里插入图片描述锁情况与上述相同,不再赘述,死锁信息如下:

LATEST DETECTED DEADLOCK
------------------------
2018-11-03 17:28:30 0x7f4b0e667700
*** (1) TRANSACTION:
TRANSACTION 1055191450, ACTIVE 18 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 316221, OS thread handle 139960338228992, query id 150467652 localhost root executing
insert into tb select 16
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191450 lock_mode X locks gap before rec insert intention waiting
*** (2) TRANSACTION:
TRANSACTION 1055191449, ACTIVE 28 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 316222, OS thread handle 139960340870912, query id 150467681 localhost root executing
insert into tb select 15
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191449 lock_mode X locks gap before rec
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1337 page no 4 n bits 72 index idx_order_id of table `db`.`tb` trx id 1055191449 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (2)

(6)解决方案

1-修改隔离级别为提交读(RC)
2-修改业务代码逻辑,删除记录之前,先select,确认该记录存在,再执行delete删除该记录。

【6】同一个事务中多条update修改同一条记录

(1)情况描述

数据库是Mysql 5.7,引擎是InnoDB,事务隔离级别是读提交(READ-COMMITED)。

死锁日志

Transactions deadlock detected, dumping detailed information.2019-03-19T21:44:23.516263+08:00 5877341 [Note] InnoDB: 
*** (1) TRANSACTION:TRANSACTION 173268495, ACTIVE 0 sec fetching rowsmysql tables in use 1, locked 1LOCK WAIT 304 lock struct(s), heap size 41168, 6 row lock(s), undo log entries 1MySQL thread id 5877358, OS thread handle 47356539049728, query id 557970181 11.183.244.150 fin_instant_app updating
update `fund_transfer_stream` set `gmt_modified` = NOW(), `state` = 'PROCESSING' where ((`state` = 'NEW') AND (`seller_id` = '38921111') AND (`fund_transfer_order_no` = '99010015000805619031958363857'))2019-03-19T21:44:23.516321+08:00 5877341 [Note] InnoDB: 
*** (1) HOLDS THE LOCK(S):RECORD LOCKS space id 173 page no 13726 n bits 248 index idx_seller_transNo of table `xxx`.`fund_transfer_stream` trx id 173268495 lock_mode X locks rec but not gapRecord lock, heap no 168 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
2019-03-19T21:44:23.516565+08:00 5877341 [Note] InnoDB: 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 173 page no 12416 n bits 128 index PRIMARY of table `xxx`.`fund_transfer_stream` trx id 173268495 lock_mode X locks rec but not gap waitingRecord lock, heap no 56 PHYSICAL RECORD: n_fields 17; compact format; info bits 02019-03-19T21:44:23.517793+08:00 5877341 [Note] InnoDB: 
*** (2) TRANSACTION:TRANSACTION 173268500, ACTIVE 0 sec fetching rows, thread declared inside InnoDB 81mysql tables in use 1, locked 1302 lock struct(s), heap size 41168, 2 row lock(s), undo log entries 1MySQL thread id 5877341, OS thread handle 47362313119488, query id 557970189 11.131.81.107 fin_instant_app updating
update `fund_transfer_stream_0056` set `gmt_modified` = NOW(), `state` = 'PROCESSING' where ((`state` = 'NEW') AND (`seller_id` = '38921111') AND (`fund_transfer_order_no` = '99010015000805619031957477256'))2019-03-19T21:44:23.517855+08:00 5877341 [Note] InnoDB: 
*** (2) HOLDS THE LOCK(S):RECORD LOCKS space id 173 page no 12416 n bits 128 index PRIMARY of table `fin_instant_0003`.`fund_transfer_stream_0056` trx id 173268500 lock_mode X locks rec but not gapRecord lock, heap no 56 PHYSICAL RECORD: n_fields 17; compact format; info bits 0
2019-03-19T21:44:23.519053+08:00 5877341 [Note] InnoDB: 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 173 page no 13726 n bits 248 index idx_seller_transNo of table `fin_instant_0003`.`fund_transfer_stream_0056` trx id 173268500 lock_mode X locks rec but not gap waitingRecord lock, heap no 168 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
2019-03-19T21:44:23.519297+08:00 5877341 [Note] InnoDB: *** WE ROLL BACK TRANSACTION (2)

定位导致死锁的两条sql

update `fund_transfer_stream_0056` set `gmt_modified` = NOW(), `state` = 'PROCESSING' where ((`state` = 'NEW') AND (`seller_id` = '38921111') AND (`fund_transfer_order_no` = '99010015000805619031957477256'))update `fund_transfer_stream_0056` set `gmt_modified` = NOW(), `state` = 'PROCESSING' where ((`state` = 'NEW') AND (`seller_id` = '38921111') AND (`fund_transfer_order_no` = '99010015000805619031958363857'))

索引情况如下

KEY `idx_seller` (`seller_id`),
KEY `idx_seller_transNo` (`seller_id`,`fund_transfer_order_no`(20))

(1)事务1,持有索引idx_seller_transNo的锁,在等待获取PRIMARY的锁。
(2)事务2,持有PRIMARY的锁,在等待获取idx_seller_transNo的锁。
(3)因事务1和事务2之间发生循环等待,故发生死锁。

事务1和事务2当前持有的锁均为:lock_mode X locks rec but not gap,两个事务对记录加的都是X 锁,No Gap锁,即对当行记录加锁,并未加间隙锁。

(2)死锁原因

首先,此次死锁一定是和Gap锁以及Next-Key Lock没有关系的。因为我们的数据库隔离级别是读提交(READ-COMMITED)的,这种隔离级别是不会添加Gap锁的,gap锁只有在读未提交会用。前面的死锁日志也提到这一点。

翻看代码

@Transactional(rollbackFor = Exception.class)public int doProcessing(String sellerId, Long id, String fundTransferOrderNo) {    fundTreansferStreamDAO.updateFundStreamId(sellerId, id, fundTransferOrderNo);return fundTreansferStreamDAO.updateStatus(sellerId, fundTransferOrderNo, FundTransferStreamState.PROCESSING.name());
}

该代码的目的是先后修改同一条记录的两个不同字段,updateFundStreamId SQL:

update fund_transfer_stream        set gmt_modified=now(),fund_transfer_order_no = #{fundTransferOrderNo}        where id = #{id} and seller_id = #{sellerId}
update fund_transfer_stream    set gmt_modified=now(),state = #{state}    where fund_transfer_order_no = #{fundTransferOrderNo} and seller_id = #{sellerId}    and state = 'NEW'

可以看到,我们的同一个事务中执行了两条Update语句,这里分别查看下两条SQL的执行计划:

在这里插入图片描述updateFundStreamId执行的时候使用到的是PRIMARY索引。

在这里插入图片描述
updateStatus执行的时候使用到的是idx_seller_transNo索引。

主要问题出在我们的idx_seller_transNo索引上面

索引创建语句中,我们使用了前缀索引,为了节约索引空间,提高索引效率,我们只选择了fund_transfer_order_no字段的前20位作为索引值。

因为fund_transfer_order_no只是普通索引,而非唯一性索引。又因为在一种特殊情况下,会有同一个用户的两个fund_transfer_order_no的前20位相同,这就导致两条不同的记录的索引值一样(因为seller_id 和fund_transfer_order_no(20)都相同 )。

就如本文中的例子,发生死锁的两条记录的fund_transfer_order_no字段的值:99010015000805619031958363857和99010015000805619031957477256这两个就是前20位相同的。

(3)原因汇总

在MySQL中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。

(1)事务1执行update1占用PRIMARY = 1的锁
(2)事务2执行update1 占有PRIMARY = 2的锁;
(3)事务1执行update2占有idx_seller_transNo = (3111095611,99010015000805619031)的锁,尝试占有PRIMARY = 2锁失败(阻塞);
(4)事务2执行update2尝试占有idx_seller_transNo = (3111095611,99010015000805619031)的锁失败(死锁);

(4)解决方案

(1)修改索引:只要我们把前缀索引 idx_seller_transNo中fund_transfer_order_no的前缀长度修改下就可以了。比如改成50。即可避免死锁。

(2)解决办法就是改代码
所有update都通过主键ID进行。
在同一个事务中,避免出现多条update语句修改同一条记录。

【四】排查线上死锁问题

【1】查看死锁日志

MySQL 会将死锁信息记录在错误日志中,可以通过查看错误日志找到死锁的详细信息,包括死锁发生的时间、涉及的事务和 SQL 语句等。

【2】使用 SHOW ENGINE INNODB STATUS 命令

该命令可以显示 InnoDB 存储引擎的状态信息,其中包含最近一次死锁的详细信息,如死锁的事务 ID、持有和等待的锁等。
sql
SHOW ENGINE INNODB STATUS;

【3】开启 innodb_print_all_deadlocks 参数

将该参数设置为 ON,可以让 MySQL 记录所有的死锁信息到错误日志中,方便后续分析。
sql
SET GLOBAL innodb_print_all_deadlocks = ON;

【五】解决死锁问题

【1】优化事务逻辑

确保事务按照相同的顺序访问资源,避免交叉更新。例如,将上述事务 T1 和 T2 都按照 id 从小到大的顺序进行更新:

-- 事务 T1
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE id = 1;
UPDATE accounts SET balance = balance - 100 WHERE id = 2;
COMMIT;-- 事务 T2
START TRANSACTION;
UPDATE accounts SET balance = balance + 100 WHERE id = 1;
UPDATE accounts SET balance = balance - 100 WHERE id = 2;
COMMIT;

【2】减少事务持有锁的时间

尽量缩短事务的执行时间,减少锁的持有时间,降低死锁的概率。例如,将大事务拆分成多个小事务。

【3】调整隔离级别

如果业务允许,可以将隔离级别从可重复读调整为读提交,减少间隙锁和临键锁的使用,降低死锁的可能性。

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

【4】优化索引

确保 SQL 语句使用合适的索引,避免全表扫描和范围扫描,减少锁的范围和粒度。例如,为经常用于查询和更新的列添加索引。

相关文章:

【Mysql】死锁问题详解

【Mysql】死锁问题详解 【一】Mysql中锁分类和加锁情况【1】按锁的粒度分类(1)全局锁(2)表级锁1、表共享读锁(Table Read Lock)2、表独占写锁(Table Write Lock)3、元数据锁&#xf…...

C语言实现用户管理系统

以下是一个简单的C语言用户管理系统示例&#xff0c;它实现了用户信息的添加、删除、修改和查询功能。代码中包含了详细的注释和解释&#xff0c;帮助你理解每个部分的作用。 #include <stdio.h> #include <stdlib.h> #include <string.h>#define MAX_USERS…...

SAP BDC:企业数据管理的新纪元

2025年4月&#xff0c;SAP在纽约发布了其全新企业数据平台——Business Data Cloud&#xff08;BDC&#xff09;&#xff0c;标志着企业数据管理和AI集成战略的重大升级。BDC不仅整合了SAP内部和外部的结构化与非结构化数据&#xff0c;还借助与Databricks的合作&#xff0c;推…...

数学建模学习资料免费分享:历年赛题与优秀论文、算法课程、数学软件等

本文介绍并分享自己当初准备数学建模比赛时&#xff0c;收集的所有资料&#xff0c;包括历年赛题与论文、排版模板、算法讲解课程与书籍、评分标准、数学建模软件等各类资料。 最近&#xff0c;准备将自己在学习过程中&#xff0c;到处收集到的各类资料都整理一下&#xff0c;并…...

计算机的运算方式

1. 计算机运算的基本概念 计算机的运算由 算术逻辑单元&#xff08;ALU&#xff09; 执行&#xff0c;其核心功能是完成 算术运算 和 逻辑运算。所有运算均基于二进制编码&#xff0c;通过硬件电路实现高速计算。 运算对象&#xff1a;二进制数&#xff08;定点数、浮点数&am…...

Tkinter事件与绑定

在Tkinter中,事件和事件绑定是实现用户交互的核心机制。通过事件机制,您可以捕捉用户的操作(例如鼠标点击、键盘输入等)并执行相应的回调函数。事件绑定是将事件与处理该事件的函数(或方法)关联起来。掌握事件和绑定技术是开发交互式应用程序的关键。 5.1 事件概述 事件…...

CAD 像素点显示图片——CAD二次开发 OpenCV实现

效果如下&#xff1a; 部分代码如下&#xff1a; public class Opencv1{[CommandMethod("xx1")]public void Opencv(){Document doc Application.DocumentManager.MdiActiveDocument;Database db doc.Database;Editor ed doc.Editor;// 设置采样精度&#xff0c;这…...

即梦+剪映:三国演义变中国好声音制作详解!

最近在刷抖音时&#xff0c;发现这种电影人物唱歌视频比较火热&#xff0c;今天手把手教大家如何制作这种让电影人物唱歌的视频&#xff01; 一、素材准备 1、准备好视频或人物图片素材 这里需要准备一张人物截图或者电影视频片段&#xff0c;大家可以去各大视频网站找原始素…...

04-线程

一、线程的概念 1、进程是系统分配资源的最少单位,操作系统会为每一个进程分配一块虚拟内存空间&#xff01; 线程是系统调度最少的单位,操作系统分配时间片的过程&#xff0c;就是系统调度&#xff01; 线程也会占用时间片&#xff01; 2、线程的内存资源 线程的内存资源是…...

7.渐入佳境 -- 优雅的断开套接字连接

前言 本章将讨论如何优雅地断开相互连接的套接字。之前用的方法不够优雅是因为&#xff0c;我们是调用close或closesocket函数单方面断开连接的。 一、基于TCP的半关闭 TCP中的断开连接过程比建立连接过程更重要&#xff0c;因为连接过程中一般不会出现大的变数&#xff0c;…...

Django3 - 开启Django Hello World

一、开启Django Hello World 要学习Django首先需要了解Django的操作指令&#xff0c;了解了每个指令的作用&#xff0c;才能在MyDjango项目里编写Hello World网页&#xff0c;然后通过该网页我们可以简单了解Django的开发过程。 1.1 Django的操作指令 无论是创建项目还是创建项…...

JavaScript 基础特性

一、变量特性 1.1 变量提升 console.log(temp); // undefined&#xff08;变量提升但未初始化&#xff09; var temp hello; 现象&#xff1a;var声明的变量会提升至作用域顶部&#xff0c;但赋值不提升 建议&#xff1a;改用 let/const 避免变量提升问题 1.2 变量泄露 fo…...

MATLAB遇到内部问题,需要关闭,Crash Decoding : Disabled - No sandbox or build area path

1.故障界面 MATLAB运行时突然中断&#xff0c;停止运行。故障界面如图&#xff1a; MATLAB Log File: C:\Users\wei\AppData\Local\Temp\matlab_crash_dump.21720-1 ------------------------------------------------ MATLAB Log File -----------------------------------…...

L1-5 吉老师的回归

题目 L1-078 吉老师的回归&#xff08;15分&#xff09; 曾经在天梯赛大杀四方的吉老师决定回归天梯赛赛场啦&#xff01; 为了简化题目&#xff0c;我们不妨假设天梯赛的每道题目可以用一个不超过 500 的、只包括可打印符号的字符串描述出来&#xff0c;如&#xff1a;Probl…...

0413-多态、Object类方法、访问权限修饰符、装箱拆箱、128陷阱

1:A and A 2:A and A 3: A and D 4: B and A 5: B and A 6:A and D 7:B and A 8: B and A 9:A and D package 第四章对象和类;public class ForthThir {//多态&#xff1a;父类的引用指向子类的对象,只能调父类的方法和子类重写的方法&#xff0c;不能调子类独有的方法&…...

Kubernetes控制平面组件:APIServer 准入控制机制详解

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…...

苍穹外卖day02

菜品相关接口开发 图片上传-阿里云OSS 依赖注入 <dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>${aliyun.sdk.oss}</version> </dependency> ​ 配置密钥 sky:alioss:end…...

SpringBoot 自定义输出控制台图标

对于控制台输出的这个图标大家都不陌生吧&#xff0c;不仅在SpringBoot中有这种图标&#xff0c;在docker 、 nginx 启动时都有自身独特的图标&#xff0c;这是怎么实现的呢。 需要利用一个网站生成 banner 图标&#xff08;首页-bootschool.net&#xff09;&#xff0c; 将图标…...

联想电脑开机出现Defalut Boot Device Missing or Boot Failed怎么办

目录 一、恢复bios默认设置 二、关机重启 三、“物理”方法 在图书馆敲代码时&#xff0c;去吃了午饭回来发现刚开机就出现了下图的问题&#xff08;崩溃&#xff09;&#xff0c;想起之前也发生过一次 这样的问题&#xff0c;现在把我用到的方法写在下面&#xff0c;可能对…...

CST1020.基于Spring Boot+Vue汽车租赁管理系统

计算机/JAVA毕业设计 【CST1020.基于Spring BootVue汽车租赁管理系统】 【项目介绍】 汽车租赁管理系统&#xff0c;基于 Spring Boot Vue 实现&#xff0c;功能丰富、界面精美 【业务模块】 客户管理&#xff1a;查询客户信息、根据条件精准检索、添加客户信息、身份证号校验…...

ArkTS基础语法:从声明到类型的深度解析

# ArkTS基础语法&#xff1a;从声明到类型的深度解析 在鸿蒙应用开发的领域中&#xff0c;ArkTS作为重要的编程语言&#xff0c;其基础语法是开发者们必须掌握的关键内容。今天&#xff0c;我们就围绕ArkTS的声明和类型相关知识展开深入探讨&#xff0c;帮助大家更好地理解和运…...

Day15:关于MySQL的编程技术——基础知识

前言&#xff1a;先创建一个练习的数据库和数据 1.创建数据库并创建数据表的基本结构 -- 创建练习数据库 CREATE DATABASE db_programming; USE db_programming;-- 创建员工表&#xff08;包含各种数据类型&#xff09; CREATE TABLE employees (emp_id INT PRIMARY KEY AUTO…...

wsl下编译eXosip和osip库(Ubuntu 22.04)

1.下载eXosip和osip osip下载路径 Index of /mirror/gnu.org/savannah/osip eXosip下载路径 Index of /nongnu/exosip 我选的osip和eXosip版本为 5.2.0 2.编译osip库 tar -zxvf libosip2-5.2.0.tar.gz cd libosip2-5.2.0 ./configure make make install 在编译…...

《轨道力学导论》——第九章:轨道确定与导航

第九章 轨道确定与导航 引言 轨道确定与导航是轨道力学中最为核心的实践领域之一&#xff0c;它将理论与实际应用紧密结合&#xff0c;解决了"我们在哪里"以及"我们将去向何方"这两个航天活动中最基本的问题。无论是地球轨道上的人造卫星、飞向深空的探测…...

几何与游标

在arcgis中,数据组织方式如下 数据库(datasets): 要素类(feature class): 几何(geometry) 属性(attribute) 元数据(metadata) 游标: 查询游标:用于对数据进行查询的游标 arcpy.da.SearchCursor() 作用&#xff1a;用于对数据进行只读查询操作。它可以帮助你逐行读取数据表或…...

【使用jenkins+docker自动化部署java项目】

背景: 有A(打包机129)&#xff0c;B(游戏服130) 2个机器&#xff0c;他们都安装有docker&#xff0c;请完成部署。 一、准备好java项目&#xff0c;写好Dockerfile # 基础镜像&#xff0c;使用包含 JDK 17 的 OpenJDK 镜像 FROM openjdk:17-jdk-slim# 设置工作目录 WORKDIR /…...

Vue3+Element Plus如何实现左树右表页面案例:即根据左边的树筛选右侧表功能实现

文章目录 一、最终效果二、源代码2.1 AddDataSource.vue2.2 LeftTree.vue2.3 FieldDrawer.vue2.4 RightTable.vue2.5 Emp.vue 三、代码解读3.1 AddDataSource.vue —— 数据源新增对话框3.2 LeftTree.vue —— 数据源树视图3.3 FieldDrawer.vue —— 字段详情抽屉3.4 RightTabl…...

Redisson的红锁,分段锁,公平锁,联锁。。。。。。

红锁&#xff1a;Redisson红锁可以防止主从集群锁丢失问题。Redisson红锁要求&#xff0c;必须要构建至少三个Redis主从集群&#xff0c;若一个请求要申请锁&#xff0c;必须向所有主从集群中提交key写入请求&#xff0c;只有当大多数集群&#xff08;过半集群&#xff09;锁写…...

system V 共享内存

system V是一种标准&#xff0c;linux内核支持这种标准&#xff0c;专门设计了一个ipc模板(通信的接口设计&#xff0c;原理&#xff0c;接口&#xff0c;相似性) 使用各自的虚拟地址访问物理内存 共享内存描述共享内存的内核数据结构它所对应的物理空间 进程间通信的本质&…...

ResNet改进(27):融合EfficientViT 高效混合网络设计

在计算机视觉领域,卷积神经网络(CNN)和视觉Transformer(ViT)各有优势。 今天分享的是一种将两者巧妙结合的方案——在ResNet18基础上引入轻量化ViT模块的设计思路。 整体架构概览 这个混合网络主要由三部分组成: ResNet18骨干网络:作为特征提取器,去掉了原模型的平均池化…...

字符串与栈和队列-算法小结

字符串 双指针 反转字符串(双指针) 力扣题目链接 void reverseString(vector<char>& s) {for (int i 0, j s.size() - 1; i < s.size()/2; i, j--) {swap(s[i],s[j]);} }反转字符串II 力扣题目链接 遍历字符串的过程中&#xff0c;只要让 i (2 * k)&#…...

go语言学习笔记:gin + gorm + mysql 用户增删改查案例入门

大家好&#xff0c;我是此林。 Golang 语言现在已经成为了编程的趋势&#xff0c;毕竟是大厂背书嘛&#xff0c;Google 研发的。 目前很多云原生项目都是基于 go 来编写的&#xff0c;比如&#xff1a; Kubernetes (K8s)​ 容器编排系统&#xff0c; Docker​ 容器化技术&…...

设计模式——建造者模式(生成器模式)总结

当我们需要创建一个非常复杂的对象时&#xff0c;可以使用建造者模式&#xff0c;分步骤建造一个对象&#xff0c;最后将完整的对象返回给客户端。 比如&#xff0c;我们要生成一个房子对象&#xff0c;建造一个房子&#xff0c;需要打地基、盖围墙、盖地板、安装门、安装窗户…...

Nginx代理Minio出现AccessDeniedAccessDenied

一、问题描述 AccessDeniedAccessDenied.sight.jpgmediafiles/mediafiles/sight.jpg1835E50603CB8FE0dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8 二、问题排查 &#xff08;1&#xff09;minio 和 nginx 是否正常启动 &#xff08;2&#xff09;检…...

在ArcGIS Pro中将栅格NoData值修改为特定值

目录 问题如下&#xff1a;栅格文件中NoData值为65535&#xff0c;要将该NoData值修改为-9999 步骤一&#xff1a;使用栅格计算器&#xff08;Raster Calculator&#xff09;输出具有新NoData值的栅格文件 步骤二&#xff1a;输出修改值后的栅格文件&#xff08;Export Rast…...

模糊表示学习 笔记

图表示学习通常从图的拓扑结构和高维节点属性中产生低维和清晰的表示。然而&#xff0c;节点或图的清晰表示实际上隐藏了特征的不确定性和可解释性。例如&#xff0c;在引文网络中&#xff0c;两篇论文之间的引用总是涉及表示相关度的future&#xff0c;也就是说&#xff0c;一…...

GitHub 趋势日报 (2025年04月12日)

本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ &#x1f4c8; 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1yeongpin/cursor-free-vip[Support 0.48.x]&#xff08;Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher…...

FreertosHAL库笔记

堆和栈 堆&#xff1a;一块内存空间&#xff0c;用于存储程序运行时动态申请的内存空间。在堆上分配内存可以根据程序的需要灵活地申请和释放不同大小的内存块。可用pvProMalllloc()和vPortFree()函数来开辟和释放 栈&#xff1a;也是一块内存空间&#xff0c;主要用于函数调用…...

迷你世界脚本之容器接口:WorldContainer

容器接口&#xff1a;WorldContainer 彼得兔 更新时间: 2023-04-26 10:21:02 具体函数名及描述如下: 序号 函数名 函数描述 1 addFurnace(...) 新增熔炉 2 removeFurnace(...) 移除熔炉 3 checkFurnace(...) 检测是否为熔炉 4 getFurnaceHeatPerce…...

springboot框架集成websocket依赖实现物联网设备、前端网页实时通信!

需求&#xff1a; 最近在对接一个物联网里设备&#xff0c;他的通信方式是 websocket 。所以我需要在 springboot框架中集成websocket 依赖&#xff0c;从而实现与设备实时通信&#xff01; 框架&#xff1a;springboot2.7 java版本&#xff1a;java8 好了&#xff0c;还是直接…...

【linux知识】web服务环境搭建(一):用户以及开发环境初始化

toc 创建用户组以及用户 以下是 创建用户组 wendao 和用户 wendao 并指定 GID、UID 及家目录 的完整操作指南&#xff1a; 一、创建用户组&#xff08;指定 GID&#xff09; sudo groupadd -g 1500 wendao # 创建组并指定 GID 为 1500• 注意&#xff1a;GID 需唯一&#…...

消息中间件——RocketMQ(一)

前言&#xff1a;此篇文章系本人学习过程中记录下来的笔记&#xff0c;里面难免会有不少欠缺的地方&#xff0c;诚心期待大家多多给予指教。 RocketMQ&#xff08;一&#xff09; 一、MQ出现的背景 在传统的单体应用架构中&#xff0c;系统的各个模块紧密耦合在一起。随着业务…...

[oeasy]python087_[词根溯源]suspend词源_append_depend

087_[词根溯源]suspend词源_append [词根溯源]suspend词源_append_depend 回忆上次内容 上次了解了 方法 和 函数的 不同之处 方法(method) 函数(function) 需要对象调用 无需对象调用 可以根据 名字调用 无需名字 直接调用 基于类的对象 独立的 需要self 不需要self…...

Ubuntu 安装 Cursor AppImage 到应用程序中

如果 Cursor AppImage 安装到 Ubuntu 系统中&#xff08;而不是每次手动运行 .AppImage 文件&#xff09;&#xff0c;可以按照以下方法操作&#xff1a; 方法 1&#xff1a;直接运行 AppImage&#xff08;最简单&#xff0c;但不完全“安装”&#xff09; 赋予执行权限chmod …...

二叉树 --- 堆(下)

今天我们来把堆完结了。 对于一个高度为 h 的满二叉树&#xff0c;有 F(h) 2 ^ 0 2 ^ 1 …… 2 ^ (h - 1) 2 ^ h - 1 h log2 (N1) 对于一个高度为 h 的完全二叉树&#xff0c;且最后一层只有一个 &#xff0c;则 F(h) 2 ^ 0 2 ^ 1 …… 2 ^ (h - 2) 1 2 ^ (h -…...

数组对象[object],五种如何去重方法 js

前言 数组有很多方法都可以实现去重。本章分享比较常用的。 准备工作 准备一组数组对象 let arr [{ id: "1", name: "张晓", age: 14 },{ id: "2", name: "张晓", age: 14 },{ id: "3", name: "张晓", age: 1…...

PyRoboPlan 库,给 panda 机械臂微分 IK 上大分,关节限位、碰撞全不怕

视频讲解&#xff1a; PyRoboPlan 库&#xff0c;给 panda 机械臂微分 IK 上大分&#xff0c;关节限位、碰撞全不怕 代码仓库&#xff1a;https://github.com/LitchiCheng/mujoco-learning 今天分享PyRoboPlan库&#xff0c;比之前的方式优点在于&#xff0c;这个库考虑了机械…...

【模态分解】EMD-经验模态分解

算法配置页面&#xff0c;也可以一键导出结果数据 报表自定义绘制 获取和下载【PHM学习软件PHM源码】的方式 获取方式&#xff1a;Docshttps://jcn362s9p4t8.feishu.cn/wiki/A0NXwPxY3ie1cGkOy08cru6vnvc...

Sentinel规则持久化pull模式核心源码解析

文章目录 前言一、服务端新增/修改规则1.1、repository.save1.2、publishRules 二、客户端接收规则三、持久化扩展3.1、持久化原理3.1.1、FileRefreshableDataSource3.1.1.1、super关键字3.1.1.2、firstLoad()方法 3.1.2、FlowRuleManager.register2Property 3.2、持久化实现 总…...

【go】--编译

go build -o [编译完成的可执行文件] [需要编译的.go文件]#例如 go build -o myapp main.go#确保编译的结果和当前运行环境相同 #查看arch uname -a在 Linux 中查看和修改 GOOS 和 GOARCH 环境变量&#xff1a; 1. 查看当前 Go 环境变量 # 查看所有Go相关的环境变量 go env# …...