故障分析 | 从 Insert 并发死锁分析 Insert 加锁源码逻辑

news2024/9/24 1:22:28

作者:李锡超

一个爱笑的江苏苏宁银行 数据库工程师,主要负责数据库日常运维、自动化建设、DMP平台运维。擅长MySQL、Python、Oracle,爱好骑行、研究技术。

本文来源:原创投稿

*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


一、前言

死锁,作为数据库一个常见的并发问题。此类问题:

1.触发原因往往与应用的逻辑相关,参与的事务可能是两个、三个、甚至更多;

2.由于不同数据库的锁实现机制几乎完全不同、实现逻辑复杂,还存在多种锁类型;

3.数据库发生死锁后,会立即终止部分事务,事后无法看到死锁前的等待状态。

即,死锁问题具有业务关联、机制复杂、类型多样等特点,导致当数据库发生死锁问题时,不是那么容易分析。

基于解决死锁问题存在的难点,本文以MySQL数据库一则并发Insert导致的死锁为例,从发现问题、重现问题、根因分析、解决问题4个步骤,期望能提供一套关于死锁的科学有效方案,供读者朋友参考。

二、问题现象

某系统在进行上线前压测时,发现应用日志存在如下日志提示触发死锁问题:

Deadlock found when trying to get lock; try restarting transaction

好在压测时,就发现了问题,避免上线后影响生产。

随后,执行 show engine innodb status,有如下内容(脱敏后):

------------------------
LATEST DETECTED DEADLOCK
------------------------
2023-03-24 19:07:50 140736694093568
*** (1) TRANSACTION:
TRANSACTION 56118, ACTIVE 6 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1192, 1 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 140736685700864, query id 57 localhost root update
insert into dl_tab(id,name) values(30,10)

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56118 lock mode S waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;  # 十进制: 10
 1: len 4; hex 8000001a; asc     ;;  # 十进制: 26


*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56118 lock mode S waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;;  # 十进制: 10
 1: len 4; hex 8000001a; asc     ;;  # 十进制: 26


*** (2) TRANSACTION:
TRANSACTION 56113, ACTIVE 12 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1192, 2 row lock(s), undo log entries 2
MySQL thread id 8, OS thread handle 140736952903424, query id 58 localhost root update
insert into dl_tab(id,name) values(40,8)

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56113 lock_mode X locks rec but not gap
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;; # 十进制: 10
 1: len 4; hex 8000001a; asc     ;; # 十进制: 26


*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 11 page no 5 n bits 72 index ua of table `testdb`.`dl_tab` trx id 56113 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 8000000a; asc     ;; # 十进制: 10
 1: len 4; hex 8000001a; asc     ;; # 十进制: 26

*** WE ROLL BACK TRANSACTION (1)
------------

1、死锁信息梳理

根据以上信息,发现是 dl_tab 执行insert操作导致死锁。初步梳理如下。

版本: 8.0.27

隔离级别: Read-Commited

表结构:

*************************** 1. row ***************************
       Table: dl_tab
Create Table: CREATE TABLE `dl_tab` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` int NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ua` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

注意,以上innodb status 输出,不同的数据库版本会有差异。主要有:

A.在MySQL 8.0.18及之后,输出结果包括两个事务各自持有的锁、等待的锁,对分析死锁问题很有帮助。在 8.0.18之前,只包括事务1等待的锁,事务2持有的锁、等待的锁,而不包括事务1持有的锁信息;

B.以上示例还包括具体的索引记录值(如{10,26}:第一字段为索引记录的值,第二个字段为对应的主键记录)。如果没有索引记录值,可能只有heap no,该编号作为内部实现锁机制非常关键,但无法和具体的索引记录对应起来。此外,找了其它几个MySQL版本发现原生版本≥5.7.21 、≥8.0有这个功能,Percona mysql 5.7.21 居然没有这个功能;

C.事务T1 等待的锁从输出结果看到的是 LOCK S, 但其实获取的锁是 lock s next key lock, 这点从后面的源码分析结果中会进一步说明。

innodb status 日志梳理:

事务事务T1事务T2
操作insert into dl_tab(id,name) values(40,8)insert into dl_tab(id,name) values(30,10)
关联的对象testdb.dl_tab 的唯一索引 uatestdb.dl_tab 的唯一索引 ua
持有的锁lock_mode X locks rec but not gap
heap no 6
10,26
lock mode S waiting
heap no 6
10(16进制为a) , 26(16进制为1a)
等待的锁lock_mode X locks gap before rec insert intention waiting
heap no 6
10,26
lock mode S waiting
heap no 6
10,26

从以上innodb status输出。可以看到死锁发生在唯一索引 ua 上。这的确也是在RC隔离级别配置下,比较常见的死锁场景。进一步梳理死锁过程:

A.首先事务T1获取到了ua中记录10的 lock x,rec not not gap

B.事务T2尝试获取ua中记录10的lock s, next key lock,由于T1持有了记录的独占锁,因此被T1堵塞

C.事务T1尝试获取ua中记录10的lock x, gap before rec,insert intention,但被堵塞

2、提出问题

除了以上现象外,无法从输出结果得到更多的信息,比如:

Q1: T1为什么会持有 ua中记录10 的锁?

Q2: T1既然持有了锁,为什么又会等待锁?

Q3: T2持有和等待相同的锁,到底是持有还是在等待?

Q4: 死锁到底是如何产生的?

为此,与研发同学沟通,了解死锁发生的业务场景,并对问题进行了再次复现。

三、重现问题

研发同学发现是在某业务的定时任务进行并发处理时触发,并很快在对应的开发环境复现了问题。问题复现后,同样在应用日志和innodb status输出看到对应日志,确认是同一问题。

建议读者朋友思考1分钟,如何进一步分析

1、尝试解决

本着解决问题优先的原则,在唯一索引ua并发时产生的,那是否可以将唯一索引改为普通索引?如果不可以,是否可以降低并发(或者直接改为单并发)?不过很快研发同学就进行了确认,uname的唯一索引是核心框架依赖的,改不了!该功能的实时性要求很高,上线后业务量比较大,不能降低或调小并发。既然无法避免,那只能进一步分析死锁发生的原因,并据此确认解决方案!

研发在复现问题后,除了能看到应用日志和innodb status输出,还是没有更多的信息。此外,研发是参考测试环境,造了一批数据,然后进行模拟复现的。即: 虽然能复现这个死锁,但无法回答最初提出的问题(Q1,Q2,Q3,Q4)。

2、跟踪死锁发生过程

随后找到Percona提供一篇文章(链接: How to deal with MySQL deadlocks),大致对死锁的问题的分析提供了思路:以应用日志和innodb status提供的数据为基础,结合events_statements_history、binlog(最好是statement格式)、 慢查询日志(slow log)、一般日志(general log)进行分析。

根据文章利用已有的功能(events_statements_history/slow log/general log),去找到数据库连接运行过那些SQL语句。随后,总结了如下脚本:

-- 将events_statements_history 中的启动时间转换为标准时间
create database IF NOT EXISTS perf_db;
use perf_db;
DELIMITER //
create function f_convert_timer_to_utc(pi_timer bigint) returns timestamp(6)
DETERMINISTIC
begin
    declare value_utc_time timestamp(6);
    select FROM_UNIXTIME( (unix_timestamp(sysdate()) - variable_value) + pi_timer/1000000000000 )  from performance_schema.global_status where variable_name = 'Uptime' into value_utc_time;
    return value_utc_time;
end;
//
DELIMITER ;

--innodb status 输出中,死锁信息中MySQL thread id,实际表示是PROCESSLIST ID。执行语句找到thread_id 与PROCESSLIST_ID的对应关系
select PROCESSLIST_ID,THREAD_ID,PROCESSLIST_INFO from performance_schema.threads where PROCESSLIST_ID in (8,10);

-- 找到上一步找到的线程ID找到运行过的SQL语句
select THREAD_ID,
 perf_db.f_convert_timer_to_utc(TIMER_START) run_start_time,
 perf_db.f_convert_timer_to_utc(TIMER_END) run_end_time,
 TIMER_WAIT/1000000000000 wait_time_s,
 'False' is_current,
 CURRENT_SCHEMA,
 SQL_TEXT
 from performance_schema.events_statements_history where thread_id=51
union 
select THREAD_ID,
 perf_db.f_convert_timer_to_utc(TIMER_START) run_start_time,
 perf_db.f_convert_timer_to_utc(TIMER_END) run_end_time,
 TIMER_WAIT/1000000000000 wait_time_s,
 'True' is_current,
 CURRENT_SCHEMA,
 SQL_TEXT
 from performance_schema.events_statements_current where thread_id=51
 and (THREAD_ID,EVENT_ID,END_EVENT_ID) not in (select THREAD_ID,EVENT_ID,END_EVENT_ID from performance_schema.events_statements_history )
order by run_start_time;

注:以上脚本中红色文字,需要根据实际情况替换。

整理脚本后,研发同学再次尝试进行了复现死锁。查询得到如下结果:

select PROCESSLIST_ID,THREAD_ID,PROCESSLIST_INFO from performance_schema.threads where PROCESSLIST_ID in (8,10);

+----------------+-----------+------------------+
| PROCESSLIST_ID | THREAD_ID | PROCESSLIST_INFO |
+----------------+-----------+------------------+
|              8 |        49 | NULL             |  
|             10 |        51 | NULL             | 
+----------------+-----------+------------------+

thread_id=49的sql运行情况:
|        49 | 2023-03-25 02:15:59.173352 | 2023-03-25 02:15:59.173612 |      0.0003 | False      | testdb         | begin                                     |
|        49 | 2023-03-25 02:16:08.349311 | 2023-03-25 02:16:08.350678 |      0.0014 | False      | testdb         | insert into dl_tab(id,name) values(26,10) |
|        49 | 2023-03-25 02:16:26.824176 | 2023-03-25 02:16:26.826121 |      0.0019 | False      | testdb         | insert into dl_tab(id,name) values(40,8)  |
+-----------+----------------------------+----------------------------+-------------+------------+----------------+-------------------------------------------+

thread_id=51 的sql运行情况:
|        51 | 2023-03-25 02:15:58.040749 | 2023-03-25 02:15:58.041057 |      0.0003 | False      | testdb         | begin                                     |
|        51 | 2023-03-25 02:16:17.408110 | 2023-03-25 02:16:26.828374 |      9.4203 | False      | testdb         | insert into dl_tab(id,name) values(30,10) |

梳理结果如下:

事务T1–thread 49事务T2–thread 51
begin;begin;
insert into dl_tab(id,name) values(26,10);
insert into dl_tab(id,name) values(30,10);
insert into dl_tab(id,name) values(40,8);

根据上述梳理结果,通过人工方式在开发环境执行上述SQL语句,再次发生死锁,且innodb status的死锁信息与测试环境基本相同。

至此,回答了最开始提出的问题Q1:

Q1: T1为什么会持有 ua中记录10 的锁?

答:因为该事务前面执行了语句如下语句,所以持有了记录(26,10)的锁:insert into dl_tab(id,name) values(26,10);

3、关于跟踪死锁额外的思考

从这个死锁的发生过程,刚好是innodb status死锁信息输出结果中的两个会话导致了死锁。但参与死锁的可能涉及3个、4个或者更多的事务,因此还有如下几个额外的问题:

Q5: 如果是3个或更多的事务参与死锁,如何跟踪?

Q6: 执行的SQL语句应该是导致死锁最直接的原因,其本质锁的是记录、锁类型及堵塞关系,如何查看?

Q7: 死锁发生后,由于MySQL死锁检测机制会自动发现死锁,并会挑选事务进行回退。事务被回退了之后,就破坏了死锁的第一现场。除了innodb status提供的最近一次死锁信息外(特别是8.0.18之前不包括事务1持有的锁信息),再无其它可用的分析数据。

综合以上3个问题,总结了如下补充方案,以采集相关的性能数据:

  • 针对Q7:在测试环境临时关闭死锁检测,然后再次复现:
innodb_deadlock_detect = off
innodb_lock_wait_timeout = 10
innodb_rollback_on_timeout = on
  • 针对Q5,Q6:结合MySQL已有的实时锁与锁等待性能数据,总结了如下脚本:
-- 创建工作目录
cd <path-to-dir>
mkdir deadlock_data
cd deadlock_data

-- 创建死锁数据保存表
mysql -uroot -S /tmp/mysql.sock
create database IF NOT EXISTS perf_db;
use perf_db
CREATE TABLE `tab_deadlock_info` (
  `id` int primary key auto_incrment,
  `curr_dt` datetime(6) NOT NULL,
  `thread_id` bigint unsigned DEFAULT NULL,
  `conn_id` bigint unsigned DEFAULT NULL,
  `trx_id` bigint unsigned DEFAULT NULL,
  `object_name` varchar(64) DEFAULT NULL,
  `INDEX_NAME` varchar(64) DEFAULT NULL,
  `lock_type` varchar(32) NOT NULL,
  `lock_mode` varchar(32) NOT NULL,
  `lock_status` varchar(32) NOT NULL,
  `LOCK_DATA` varchar(8192) CHARACTER SET utf8mb4 DEFAULT NULL,
  `blk_trx_id` bigint unsigned DEFAULT NULL,
  `blk_thd_id` bigint unsigned DEFAULT NULL,
  index idx_curr_dt(curr_dt)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 查看当前存在的锁及锁堵塞信息
-- data_locks/data_lock_waits 自MySQL 8.0.1提供,之前版本查询information_schema.innodb_locks/ information_schema.innodb_lock_waits获取类似信息
vi save_lock_data.sql
insert into tab_deadlock_info(curr_dt,thread_id,conn_id,trx_id,object_name,index_name,
     lock_type,lock_mode,lock_status,lock_data,blk_trx_id,blk_thd_id)
select NOW(6) curr_dt,a.thread_id,b.PROCESSLIST_ID conn_id,
  ENGINE_TRANSACTION_ID trx_id, object_name,
  INDEX_NAME,lock_type,lock_mode,lock_status,LOCK_DATA,
  c.BLOCKING_ENGINE_TRANSACTION_ID blk_trx_id,
  c.BLOCKING_THREAD_ID blk_thd_id
from performance_schema.data_locks a left join performance_schema.threads b 
  on a.thread_id=b.thread_id
left join performance_schema.data_lock_waits c 
     on a.ENGINE_TRANSACTION_ID=c.REQUESTING_ENGINE_TRANSACTION_ID and a.thread_id=c.REQUESTING_THREAD_ID
where a.thread_id=b.thread_id order by thread_id,trx_id;

-- 查询脚本
mysql -uroot -S /tmp/mysql.sock perf_db -e "source save_lock_data.sql"

-- 定时查询脚本
vi run_save_lock.sh
while true
do
sleep 0.1 # 指定查询间隔时间,结合实际需求调整
mysql -uroot -S /tmp/mysql.sock perf_db -e "source save_lock_data.sql" 2>>run_save_lock.err
done

-- 执行查询
sh run_save_lock.sh

说明:

A.配置上述innodb_deadlock_detect参数关闭死锁检测,innodb status将不在会继续输出最后LATEST DETECTED DEADLOCK信息;

B.应用日志看到的告警为锁超时告警:Lock wait timeout exceeded; try restarting transaction。可以据此找到锁超时发生时间。

再次复现后使用使用tab_deadlock_info查询锁数据如下:

同时,使用步骤2)查询的语句信息如下:

综合上述查询结果,梳理得到如下结果:

时间事务T1事务T2
2023-03-27 14:53:49begin;
2023-03-27 14:53:50begin;
2023-03-27 14:53:54insert into dl_tab(id,name) values(26,10);
2023-03-27 14:53:59持有: ua记录(10,26)的 Lock X, Rec_not_gapinsert into dl_tab(id,name) values(30,10);
等待:ua记录(10,26)的 Lock S
2023-03-27 14:54:04insert into dl_tab(id,name) values(40,8);
持有:ua记录(10,26)的Lock X, Rec_not_gap
等待:ua记录(10,26)的Lock X, gap, insert_intention

再次梳理出了死锁的发生过程。

四、根因分析

通过上述过程,可以看到死锁发生的过程,获取的锁及其属性信息。但要分析出为什么会发生死锁,还需要结合MySQL的锁实现机制。由于以上死锁场景,涉及唯一索引的插入实现逻辑,将结合源码进行解读。

1、单列唯一索引插入逻辑

下图中:

蓝色线表示T1第一次插入执行的逻辑;

紫色线表示T2第一次插入执行的逻辑;

黑色线表示T1第二次插入执行的逻辑;

与唯一索引插入记录相关锁操作,使用了红色短箭头标记;

竖线与红色短尖头交叉表示执行了函数,否则表示未执行。

2、最终死锁过程

以时间维度,结合以上的mysql加锁逻辑进行分析:

A. T1、T2开启了一个事务,随后T1执行了插入(26,10)的insert语句

B. T2执行了插入(30,10)的insert语句。进行唯一性冲突检查,尝试获取LOCK_S | LOCK_ORDINARY[line 15]。那为什么看到时LOCK S,是因为LOCK_ORDINARY对应的数字表示为0,任何与之进行“与”运算都等于本身,所以看不出来:

C. 然后T2所在的连接会将T1中的隐式锁转换为显示锁[line 17],此时T1将获取LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP,即看到的Lock X, Rec_not_gap。由于是T2所在的线程为T1创建的锁,因此该锁对应的thread_id为T2的线程ID,但trx_id为T1的事务ID

D. 但由于 T1的LOCK X|LOCK_REC_NOT_GAP与T2的LOCK S| LOCK_ORDINARY不兼容[line 23],因此T2被堵塞

E. 当T2被堵塞时,内部函数add_to_waitq 在处理时,同样会记录创建一个锁,并设置属性 LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC,以指示锁等待[line 24]。随后T2返回上层函数,以等待锁资源[line 38]

F. 随后,T1又执行了(40,8)的insert语句。由于其插入的唯一索引值是8(注意不是10),因此不存在主键冲突,直接执行乐观插入操作[line 43]。

G. 执行乐观插入时,需要检查其它事务是否堵塞insert操作。其核心是获取待插入记录的下一个值[line 46-47](这里刚好是10),并获取该记录上的所有锁,与需要添加的锁(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION)判断是否存在冲突[line 51]。

H. 第E步T2持有了记录10的LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOC

K_REC 锁与T1的LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION不兼容,因此T1被T2堵塞[line 51]

I. 死锁形成。

如需了解更多实现细节,大家可以结合源码进一步确认。

至此,回答了最开始提出的问题Q2,Q3,Q4:

Q2: T1既然持有了锁,为什么又会等待锁?

答:持有锁应该没有疑问,在分析类似问题注意隐式锁转换为显示锁的机制(lock_rec_convert_impl_to_expl)。等待锁主要由于T2被堵塞后,会创建锁(LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC)。然后T1在执行乐观插入时,需要遍历记录上存在的所有>锁进行锁冲突判断,由于锁模式不兼容,因此被堵塞

Q3: T2持有和等待相同的锁,到底是持有还是在等待?

答:虽然从innodb status看到T2持有和等待都是lock s, next key lock。但实际上等待lock s, next key lock;由于锁冲突,加入等待队列时会持有锁LOCK_S | LOCK_ORDINARY | LOCK_WAIT | LOCK_REC

Q4: 死锁到底是如何产生的?

答:见以上分析死锁过程分析

五、解决问题

综合以上死锁发生的发生过程和原因,总结原因如下:

原因1:主要研发过度依赖唯一索引,插入的数据不符合唯一索引要求,需要进行唯一性冲突检查。

原因2:批量插入的数据不是有序的。两种情况同时存在,导致死锁发生。

原因2在并发场景下,控制起来较为复杂。原因1该场景为并发批量插入逻辑,可以在执行插入时,避免插入重复的uname。随后,研发同学进行逻辑优化后,问题不再发生。

对于死锁问题,建议结合业务情况尽量选择Read Committed隔离级别,适当的减少Unique索引。如确实发生死锁,读者朋友可以参考本次故障案例,合理利用性能数据来跟踪死锁问题,结合源码或者已有的案例分析死锁根本原因和解决方案。

以上信息仅左右交流,作者水平有限,如有不足之处,欢迎在评论区交流。

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

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

相关文章

Python初学小知识(十四):数据分析处理库Pandas

Python初学小知识&#xff08;十四&#xff09;&#xff1a;数据分析处理库Pandas 十八 Pandas1 文件读取1.1 读取csv1.2 读取txt1.3 读取excel&#xff08;xlsx&#xff09; 2 内容读取2.1 读取行2.2 读取列 3 数据处理3.1 加减乘除3.1.1 列 与 元素3.1.2 列 与 列 3.2 最值、…

React-Native 热更新实践

以下是基于CodePush的热更新方案的实践,有需要的可以参考一下: 一、配置appcenter 1.1 安装appcenter 安装appcenter的命令如下: npm install -g appcenter-cli /** 安装完成后 */ appcenter help /** 如果出现帮助指令说明安装成功 */安装成功之后,登录appcenter,涉…

Python——线性回归、梯度下降、正则化(原理)

目录 1 线性回归-最小二乘法(LSM) 2 梯度下降 3 数据归一化/标准化 4 过拟合和欠拟合 4.1 过拟合的处理 4.2 欠拟合的处理 5 正则化 一种通过属性的线性组合来进行预测的 线性模型 &#xff0c;其目的是找到一条直线或者一个平面或者更高维的超平面&#xff0c; 使得预…

FPGA学习笔记(三):PLL 锁相环

在 FPGA 芯片内部集成了 PLL(phase-locked loop&#xff0c;锁相环)&#xff0c;可以倍频分频&#xff0c;产生其它时钟类型。PLL 是 FPGA 中的重要资源&#xff0c;因为一个复杂的 FPGA 系统需要不同频率、相位的时钟信号&#xff0c;一个 FPGA 芯片中 PLL 的数量是衡量 FPGA …

CAN总线网络中为什么需要安装终端电阻?

摘要&#xff1a; 为什么CAN总线网络中为什么需要安装终端电阻&#xff1f; ​在详解CAN总线&#xff1a;高速CAN总线和低速CAN总线的特性​文章中&#xff0c;高速CAN网络和低速CAN网络都需要安装终端电阻。 详解CAN总线&#xff1a;高速CAN总线和低速CAN总线的特性 高速CA…

HTB_Netmon CVE-2018-9276 RCE漏洞复现

文章目录 信息收集解题创建用户exppowershell脚本 信息收集 扫描结果还是很丰富的 nmap -sC -sV -p- -T4 ip ftp服务直接允许匿名登录了&#xff0c;直接连接&#xff0c;翻目录&#xff0c;在 Users/Public/里就看到了 user flag&#xff0c;get命令保存到本地查看&#xff0…

Vue3父组件向子组件传值之ArticleItem组件的封装与使用

组件概念 官方文档:组件基础 本节核心内容:组件的封装与传值 组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构: 这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们…

易点易动固定资产管理平台如何帮助高校精准管理海量固定资产

高校拥有大量的固定资产,如教室、实验室、办公设备、体育设备等,这些资产数量庞大,分布广泛,管理难度大。传统的管理方式效率低下,难以实现精准和动态管理。易点易动固定资产管理平台利用现代信息技术,为高校提供一套智能化的资产管理解决方案,实现固定资产全流程的精准管理。 …

马云上三路和下三路

马云的上三路、下三路&#xff0c;马云最牛搭档总结 马云刚最牛搭档&#xff1a;蔡崇信&#xff0c;关明生 《关乎天下》是关明生写的一本书 趣讲大白话&#xff1a;没有方法走不远 【趣讲信息科技143期】 **************************** 马云上三路&#xff1a;使命&#xff0c…

为什么要选择付费SSL证书?免费和付费SSL证书的区别是什么?

近几年&#xff0c;由于互联网的发展与新冠疫情的影响&#xff0c;线上教育、线上办公、线上学习等逐渐融入我们的生活。但与此同时&#xff0c;信息数据泄露等网络安全问题也日益突出&#xff0c;为了保护企业与用户的隐私信息&#xff0c;越来越多的公司选择安装SSL证书来保护…

渲染02-内置Uniform

参考: CesiumJS 源码杂谈 - 从光到 Uniform 渲染02-内置Uniform 1 Unifrom 的接口 路径: pacaages/engin/Source/Renderer/AutomaticUniforms.js AutomaticUniforms 实际上是 Cesium 暴露给着色使用的接口集合。 AutomaticUniforms┖ AutomaticUniform(czm_projection/czm…

【Maven】Maven的打包方式和执行jar

Maven的打包方式和执行jar 1. Jar1.1 Pom.xml1.2 Run1.3 自动执行方法 2. Assembl1y2.1 Pom.xml2.2 Run2.3 要执行的方法 3. Awakening 1. Jar 1.1 Pom.xml <plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-co…

2024年浙大MBA提面申请细则及条件详解

中文MBA项目申请 浙江大学MBA招生分提前批和常规批两类。其中&#xff0c;提前批是针对工作年限较长、管理经验较丰富的考生&#xff0c;需要在正式报名参加MBA全国联考前向浙江大学MBA教育中心提出申请并在通过审核后参加相应的面试&#xff08;具体申请和面试安排请登录浙江…

【英语】100个句子记完5500个考研单词

文章目录 Sentence 01Sentence 02Sentence 03Sentence 04Sentence 05Sentence 06Sentence 07Sentence 08Sentence 09Sentence 10Sentence 11Sentence 12Sentence 13Sentence 14Sentence 15Sentence 16Sentence 17Sentence 18Sentence 19Sentence 20Sentence 21Sentence 22Sente…

力扣sql中等篇练习(十一)

力扣sql中等篇练习(十一) 1 好友申请|| :谁有最多的好友 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 出现数字次数越多,就代表它的好友越多 # 对两列数据合并时 不取出合并数据,采用UNION ALL SELECT t1.id,count(*) num FROM (SELECT request…

低功耗设计方法学——篇Ⅰ

引言 低功耗设计关乎ASIC芯片的性能稳定。对ASIC 特别是一些Soc芯片的设计有着重要的影响&#xff0c;随着集成规模的大幅度增加&#xff0c;芯片自身的功耗问题暴露也越来越明显。低功耗设计的需求和必要性也越来越值得关注。本文就《Low Power Methodology Manual For Syste…

【开发日志】2023.04 ZENO----Composite----CompNormalMap

NormalMap-Online (cpetry.github.io)https://cpetry.github.io/NormalMap-Online/ CompNormalMap 将灰度图像转换为法线贴图 将灰度图像转换为法线贴图是一种常见的技术&#xff0c;用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图&…

文件 与 IO操作

前言 本篇介绍文件的基本操作&#xff0c;认识文本文件与二进制文件的区别&#xff0c;什么是绝对路径与相对路径&#xff0c;java标志库中又是如何进行文件操作的&#xff1b;认识流对象进行简单的文件读取操作&#xff1b;如有错误&#xff0c;请在评论区指正&#xff0c;让…

全网最详细,Pytest自动化测试框架关联/参数化实战,及拿即用...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Pytest自动化测试框…

从C出发 27 --- 深入理解指针与地址

指针是什么? 指针是变量&#xff0c;这种变量有一点特殊&#xff0c;它特殊在保存的值是内存地址&#xff0c;并且我们还可以通过指针所保存的内存地址来直接访问相应内存中的数据。 每一种指针类型的变量只保存对应类型变量的地址 比如这个类型的指针变量&#xff0c;只保…