事务02之锁机制

锁机制

文章目录

  • 锁机制
    • 一:MySQL锁的由来与分类
      • 1:锁机制的分类
    • 二:共享锁与排他锁
      • 1:共享锁(S锁)
      • 2:排他锁(X锁)
      • 3:锁的释放
    • 二:表级别锁
      • 1:元数据锁(了解)
      • 2:意向锁
      • 3:自增锁(了解)
      • 4:全局锁(库锁,了解)
    • 三:行锁
      • 1:行锁和表锁之间的关系
      • 2:行锁的实现
      • 3:行锁/记录锁(Record lock)
      • 4:间隙锁(Gap Lock)
      • 5:临键锁(Next-key Lock)
      • 6:插入意向锁
    • 四:乐观锁和悲观锁
      • 1:乐观锁
      • 2:悲观锁
    • 五:总结
      • 1:各种锁的兼容和互斥
        • 1.1:行级锁对比
        • 1.2:表级锁对比
      • 2:SX锁
        • 2.1:SMO问题
        • 2.2:SX锁
        • 2.3:并发事务冲突分析

一:MySQL锁的由来与分类

前面说并发问题:脏写、脏读、不可重复读以及幻读问题,而对于这些问题又可以通过调整事务的隔离级别来避免

那为什么调整事务的隔离级别后能避免这些问题产生呢?

因为不同的隔离级别中,工作线程执行SQL语句时,用的锁粒度、类型不同。

1:锁机制的分类

在这里插入图片描述
总归说来说去其实就共享锁、排他锁两种,只是加的方式不同,加的地方不同,因此就演化出了这么多锁的称呼

二:共享锁与排他锁

最好不要将共享锁和排他锁称作读锁和写锁

1:共享锁(S锁)

共享锁的意思很简单,也就是不同事务之间不会排斥,可以同时获取锁并执行

⚠️这里所谓的不会排斥,仅仅只是指不会排斥其他事务来读数据,但其他事务尝试写数据时,就会出现排斥性

在这里插入图片描述
在MySQL中,我们可以在SQL语句后加上相关的关键字来使用共享锁

# MySQL8.0
select ... for share

# 如果用的是MySQL5.7,使用的是:(8.0不能使)
select ... in share mode

在这里插入图片描述

🎉 这种通过在SQL后添加关键字的加锁形式,被称为显式锁

而实际上为数据库设置了不同的事务隔离级别后,MySQL也会对SQL自动加锁,这种形式则被称之为隐式锁。

2:排他锁(X锁)

排他锁也被称之为独占锁,当一个线程获取到独占锁后,会排斥其他线程

如若其他线程也想对共享资源/同一数据进行操作,必须等到当前线程释放锁并竞争到锁资源才行

⚠️ 排他锁并不是只能用于写操作

对于一个读操作,也可以手动的指定为获取排他锁

当一个事务在读数据时,获取了排他锁,那当其他事务来读、写同一数据时,都会被排斥

比如事务T1对ID=88的这条数据加了一个排他锁,此时T2来加排他锁读取这条数据,T3来修改这条数据,都会被T1排斥。

在这里插入图片描述
在MySQL中,可以通过如下方式显式获取独占锁:

select ... for update;

做个小实验

在这里插入图片描述
当两个事务同时获取排他锁,尝试读取一条相同的数据时,其中一个事务就会陷入阻塞,直至另一个事务结束才能继续往下执行

但是下述这种情况则不会被阻塞:

在这里插入图片描述
而至于为什么不会阻塞的原因,是因为MVCC机制【后面说】

3:锁的释放

MySQL中释放锁的动作都是隐式的,毕竟如果交给咱们来释放,很容易由于操作不当造成死锁问题发生。

因此对于锁的释放工作,MySQL自己来干,就类似于JVM中的GC机制一样,把内存释放的工作留给了自己完成。

但对于锁的释放时机,在不同的隔离级别中也并不相同

比如在“读未提交RU”级别中,是SQL执行完成后就立马释放锁,而在“可重复读RR”级别中,是在事务结束后才会释放。

二:表级别锁

表锁应该是听的最多的一种锁,因为实现起来比较简单,同时应用范围也比较广泛,几乎所有的存储引擎都会支持这个粒度的锁

表锁顾名思义就是以表作为锁的基础,将锁加在表上,一张表只能存在一个同一类型的表锁

在Innodb中,它的锁机制是基于聚簇索引实现的,当SQL执行时,如果能在聚簇索引命中数据,则加的是行锁,如无法命中聚簇索引的数据则加的是表锁

# 假设在innodb下执行这条sql,无法命中聚簇索引,所以加的表级锁
# 这个表级锁,并不是真正意义上的表锁,是一个“伪表锁”,但作用是相同的,锁了整张表。
select * from person for update

在MyISAM中,因为MyISAM不支持聚簇索引,所以若要在MyISAM引擎中使用表锁,又需要使用额外的语法

-- MyISAM引擎中获取读锁(具备读-读可共享特性)
lock tables table_name read;

-- MyISAM引擎中获取写锁(具备写-读、写-写排他特性)
lock tables table_name write;

-- 查看目前库中创建过的表锁(in_use > 0 表示目前正在使用的表锁)
show open tables where in_use > 0;

-- 释放已获取到的锁
unlock tables;

下面是演示的一个小例子:
在这里插入图片描述

MyISAM中,当你加了read读锁后,再尝试加write写锁,就会发现无法获取锁,当前线程会陷入阻塞,反过来也是同理

1:元数据锁(了解)

Meta Data Lock -> 元数据锁,也被简称为MDL锁,这是基于表的元数据加锁

在MySQL5.5 - MySQL7.x之间所有存储引擎的表都会存在一个.frm文件,这个文件中主要存储表的结构的,而MDL锁就是基于.frm文件中的元数据加锁的。

这个锁主要是用于:更改表结构时使用,比如你要向一张表创建/删除一个索引、修改一个字段的名称/数据类型、增加/删除一个表字段等这类情况

因为毕竟当你的表结构正在发生更改,假设此时有其他事务来对表做CRUD操作,自然就会出现问题

比如我刚删了一个表字段,结果另一个事务中又按原本的表结构插入了一条数据,这显然会存在风险

因此MDL锁在加锁后,整张表不允许其他事务做任何操作

2:意向锁

InnoDB引擎是一种支持多粒度锁的引擎,而意向锁则是InnoDB中为了支持多粒度的锁,为了兼容行锁、表锁而设计的

表锁和行锁的兼容性问题

假设一张表中有一千万条数据,现在事务T1对ID=99999的这条数据加了一个行锁,此时来了一个事务T2,想要获取这张表的表级别写锁

写锁必须为排他锁,也就是在同一时刻内,只允许当前事务操作,如果表中存在其他事务已经获取了锁,目前事务就无法满足“独占性”,因此不能获取锁。

由于T1是对ID=99999的数据加了行锁,那T2获取表锁时,是不是得先判断一下表中是否存在其他事务在操作?

因为InnoDB中有行锁的概念,所以表中任何一行数据上都有可能存在事务加锁操作

为了能精准的知道答案,MySQL就得将整张表的1000W条数据全部遍历一次,然后逐条查看是否有锁存在,那这个效率自然会非常的低。

在这里插入图片描述
就算能接受一行行的慢点遍历!但实际上不仅仅存在这个问题

还有另外一个致命问题,比如现在MySQL已经判断到了第700行数据,发现前面的数据上都没有锁存在,正在继续往下遍历。

而MySQL是支持并发事务的,也就是MySQL正在扫描后面的每行数据是否存在锁时,万一又来了一个事务在扫描过的数据行上加了个锁怎么办?

比如在第123条数据上加了一个行锁。那难道又重新扫描一遍嘛?这就陷入了死循环,行锁和表锁之间出现了兼容问题。

意向锁,挂牌告知

由于行锁和表锁之间存在兼容性问题,所以意向锁诞生!

意向锁实际上也是一种特殊的表锁,意向锁其实是一种挂牌告知的思想

好比日常生活中的出租车,一般都会有一个牌子,表示它目前是“空车”还是“载客”状态,而意向锁也是这个思想。

在这里插入图片描述
比如当事务T1打算对ID=99999这条数据加一个行锁之前,就会先加一个表级别的意向锁

比如目前T1要加一个行级别的读锁,就会先添加一个表级别的意向共享锁(IS),如果T1要加行级别的写锁,亦是同理(IX)。

此时当事务T2尝试获取一个表级锁时,就会先看一下表上是否有意向锁

  • 如果有的话再判断一下与自身是否冲突:
    • 比如表上存在一个意向共享锁,目前T2要获取的是表级别的读锁,那自然不冲突可以获取。
    • 如果T2要获取一个表记的写锁时,就会出现冲突,T2事务则会陷入阻塞,直至T1释放了锁(事务结束)为止。

在这里插入图片描述
互斥兼容关系 (排它锁和谁都互斥,IX和S互斥)

在这里插入图片描述

3:自增锁(了解)

自增锁,这个是专门为了提升自增ID的并发插入性能而设计的,通常情况下咱们在建表时,都会对一张表的主键设置自增特性

我们在写SQL的时候,通常会对主键设置自增,例如:

create table table_name (
	xx_id not null auto_increment,
    ...
) engine = innodb

当对一个字段设置AUTO_INCREMENT自增后,意味着后续插入数据时无需为其赋值,系统会自动赋上顺序自增的值

现在假设这样的一种场景:

在这里插入图片描述

目前表中最大的ID=88,如果两个并发事务一起对表执行插入语句

由于是并发执行的原因,所以有可能会导致插入两条ID=89的数据。

因此这里必须要加上一个排他锁,确保并发插入时的安全性

但也由于锁的原因,插入的效率也就因此降低了,毕竟将所有写操作串行化了。

为了改善插入数据时的性能,自增锁诞生了

自增锁也是一种特殊的表锁,但它仅为具备AUTO_INCREMENT自增字段的表服务,同时自增锁也分成了不同的级别

可以通过innodb_autoinc_lock_mode参数控制:

  • innodb_autoinc_lock_mode = 0:传统模式。
  • innodb_autoinc_lock_mode = 1:连续模式(MySQL8.0以前的默认模式)。
  • innodb_autoinc_lock_mode = 2:交错模式(MySQL8.0之后的默认模式)。

在这里插入图片描述

三种插入类型

想要了解自增锁的三种模式,就先要了解MySQL的三种插入类型:

  • 普通插入:指通过INSERT INTO table_name(...) VALUES(...)这种方式插入。
  • 批量插入:指通过INSERT ... SELECT ...这种方式批量插入查询出的数据。
  • 混合插入:指通过INSERT INTO table_name(id,...) VALUES(1,...),(NULL,...)这种方式插入,其中一部分指定ID,一部分不指定。

用一句话来概述自增锁的作用:自增锁主要负责维护并发事务下自增列的顺序

也就是说,每当一个事务想向表中插入数据时,都要先获取自增锁先分配一个自增的顺序值,但不同模式下的自增锁也会有些许不同。

4:全局锁(库锁,了解)

全局锁其实是一种尤为特殊的表锁,其实将它称之为库锁也许更合适,因为全局锁是基于整个数据库来加锁的

加上全局锁之后,整个数据库只能允许读,不允许做任何写操作,一般全局锁是在对整库做数据备份时使用。

flush tables with read lock; # 获取全局锁

unlock tables; # 释放全局锁的命令

将其归纳到表锁范围,因为获取锁以及释放锁的命令都是表锁的命令

三:行锁

1:行锁和表锁之间的关系

在这里插入图片描述
一张表就类似于一个生活中的酒店,每个事务/请求就类似于一个个旅客,旅客住宿为了确保夜晚安全,通常都会锁门保护自己。

表锁就类似于一个旅客住进酒店之后,直接把酒店大门给锁了,其他旅客就只能等第一位旅客住完出来之后才能一个个进去,每个旅客进酒店之后的第一件事情就是锁大门,防止其他旅客威胁自己的安全问题。

现在假设酒店门口来了一百位旅客,其中大部分旅客都是不同的房间,因此直接锁酒店大门显然并不合理。

行锁就类似于房间的锁,门口的100位旅客可以一起进酒店,每位旅客住进自己的房间之后,将房门反锁

这显然也能保障各自的人身安全问题,同时也能让一个酒店在同一时间内接纳更多的旅客,“性能”更高。

2:行锁的实现

在MySQL诸多的存储引擎中,仅有InnoDB引擎支持行锁

这是因为InnoDB支持聚簇索引,之前说过,InnoDB中如果能够命中索引数据,就会加行锁,无法命中则会加表锁。

InnoDB会将表数据存储在聚簇索引中,每条行数据都会存储在树中的叶子节点上,因此行数据是“分开的”,所以可以对每一条数据上锁

其他引擎大部分都不支持聚簇索引,表数据都是一起存储在一块的,所以只能基于整个表数据上锁,这也是为什么其他引擎不支持行锁的原因。

3:行锁/记录锁(Record lock)

# 获取行级别临键锁
select * from person where id = 1 lock in share mode;

# 获取行级别排它锁
select * from person where id = 1 for update;

想要使用InnoDB的行锁就是这样写的,如果你的SQL能命中索引数据,那也就自然加的就是行锁,反之则是表锁

4:间隙锁(Gap Lock)

间隙锁是对行锁的一种补充,主要是用来解决幻读问题的。想要理解间隙锁,需要理解啥叫间隙:

在这里插入图片描述

间隙锁是如何解决幻读的?

重温下幻读的概念:另外一个事务在第一个事务要处理的目标数据范围之内新增了数据,然后先于第一个事务提交造成的问题

举个例子:

现在要将ID>3的用户密码重置为1234,因此事务T1先查到了ID>3的4、9两条数据并上锁了,然后开始更改用户密码

但此时事务T2过来又插入了一条ID=6、password=7777的数据并提交

等T1修改完了4、9两条数据后,此时再次查询ID>3的数据时,结果发现了ID=6的这条数据并未被重置密码。

回顾下幻读的操作:

在这里插入图片描述
为了防止出现安全问题,所以T1在操作之前会对目标数据加锁

在T1事务执行时,这条幻影数据还不存在,因此就会出现一个新的问题:不知道把锁加在哪儿,毕竟想要对ID=6的数据加锁,就是加了个寂寞。

那难道不加锁了吗?肯定得加锁,但怎么加呢?间隙锁应运而生!间隙锁的功能与它的名字一样,主要是对间隙区域加锁,举个例子:

select * from zz_users where user_id = 6 lock in share mode;

ID=6的数据在表中还没有呀,咋加锁呢?

其实这个就是间隙锁,此时会锁定{4~9}之间、但不包含4、9的区域,因为间隙锁是遵循左右开区间的原则

在这里插入图片描述
🖊 当对一个不存在的数据加锁后,默认就是锁定前后两条数据之间的区间,当其他事务再尝试向该区间插入数据时,就会陷入阻塞,只有当持有间隙锁的事务结束后,才能继续执行插入操作。

5:临键锁(Next-key Lock)

临键锁是间隙锁的加强版本,或者可以说成是一种由记录锁+间隙锁组成的锁:

RR级别的幻读就是通过临键锁解决的

  • 记录锁:锁定的范围是表中具体的一条行数据。
  • 间隙锁:锁定的范围是左右开区间,但不包含当前这一条真实数据,只锁间隙区域。

临键锁则是两者的结合体,加锁后,即锁定左开右闭的区间,也会锁定当前行数据

select * from zz_user where user_id = 9 lock in share mode

在这里插入图片描述

所以临键锁就是:记录锁【当前这行】 + 间隙锁【没有数据的行】

⚠️ 下面两点需要注意:

  • 当原本持有锁的T1事务结束后,T2会执行插入操作,这时锁会被T2获取,当你再尝试开启一个新的事务T3,再次获取相同的临键锁时,是无法获取的,只能等T2结束后才能获取(因为临建锁包含了记录锁)。
  • 实际上在InnoDB中,除开一些特殊情况外,当尝试对一条数据加锁时,默认加的是临键锁,而并非记录锁、间隙锁。

6:插入意向锁

实际上插入意向锁是一种间隙锁,这种锁是一种隐式锁,也就是无法手动的获取这种锁

在MySQL中插入数据时,是并不会产生锁的,但在插入前会先简单的判断一下,当前事务要插入的位置有没有存在间隙锁或临键锁

如果存在的话,当前插入数据的事务则需阻塞等待,直到拥有临键锁的事务提交。

当事务执行插入语句阻塞时,就会生成一个插入意向锁,表示当前事务想对一个区间插入数据

在这里插入图片描述
在这里插入图片描述
当持有原本持有临建锁的事务提交后,当前事务即可以获取插入意向锁,然后执行插入操作

当此时如若又来一个新的事务,也要在该区间中插入数据,那新的事务会阻塞吗?

不会阻塞,可以直接执行插入操作

对于写入SQL都会做一次唯一性检测,如果要插入的数据,与表中已有的数据,存在唯一性冲突时会直接抛出异常并返回。

这也就意味着:如果没抛出异常,就代表着当前要插入的数据与表中数据不存在唯一性冲突,或表中压根不存在唯一性字段,可以允许插入重复的数据。

在这里插入图片描述
🖊 能够真正执行的插入语句,绝对是通过了唯一检测的,因此插入时可以让多事务并发执行,同时如果设置了自增ID,也会获取自增锁确保安全性,所以当多个事务要向一个区间插入数据时,插入意向锁是不会排斥其他事务的,从这种角度而言,插入意向锁也是一种共享锁。

四:乐观锁和悲观锁

乐观锁和悲观锁只是思想,不是指具体的锁

  • 乐观锁:每次执行都认为只会有自身一条线程操作,因此无需拿锁直接执行。
  • 悲观锁:每次执行都认为会有其他线程一起来操作,因此每次都需要先拿锁再执行。

1:乐观锁

编程中的无锁技术,或者说乐观锁机制,一般都是基于CAS思想实现的

在MySQL中则可以通过version版本号+CAS的形式实现乐观锁,也就是在表中多设计一个version字段,然后在SQL修改时以如下形式操作:

update ... set version = version + 1, ... where ... and version = version

也就是每条修改的SQL都在修改后,对version + 1

比如T1、T2两个事务一起并发执行时,当T2事务执行成功提交后,就会对version+1

因此事务T1的version=version这个条件就无法成立,最终会放弃执行,因为已经被其他事务修改过了。

当然,一般的乐观锁都会配合轮询重试机制,比如上述T1执行失败后,再次执行相同语句,直到成功为止

乐观锁更加适用于读大于写的业务场景,频繁写库的业务则并不适合加乐观锁。

因为写操作的并发较高时,就容易导致一个事务长时间一直在重试执行,从而导致客户端的响应尤为缓慢。

2:悲观锁

在每次执行前必须获取到锁,然后才能继续往下执行,而数据库中的排他锁,就是一种典型的悲观锁类型。

... for update;

五:总结

1:各种锁的兼容和互斥

1.1:行级锁对比
行级锁对比共享临键锁排他临键锁间隙锁插入意向锁
共享临键锁兼容冲突兼容冲突
排他临键锁冲突冲突兼容冲突
间隙锁兼容兼容兼容冲突
插入意向锁冲突冲突冲突兼容
1.2:表级锁对比
表级锁对比共享意向锁排他意向锁元数据锁自增锁全局锁
共享意向锁兼容冲突冲突冲突冲突
排他意向锁冲突冲突冲突冲突冲突
元数据锁冲突冲突冲突冲突冲突
自增锁冲突冲突冲突冲突冲突
全局锁兼容冲突冲突冲突冲突

对于表级锁,咱们只需要关注共享意向锁和共享排他锁即可,其他的大多数为MySQL的隐式锁

2:SX锁

2.1:SMO问题

在SQL执行期间一旦更新操作触发B+Tree叶子节点分裂,那么就会对整棵B+Tree加排它锁

这不但阻塞了后续这张表上的所有的更新操作,同时也阻止了所有试图在B+Tree上的读操作,也就是会导致所有的读写操作都被阻塞,其影响巨大。

在这里插入图片描述
MySQL 5.7版本中引入SX锁要解决的问题。

2.2:SX锁

如何解决上面的这个问题呢?

最简单的方式就是减小SMO问题发生时影响的B+树范围,锁定的B+Tree粒度

当发生SMO问题时,就只锁定B+Tree的某个分支,而并不是锁定整颗B+树,从而做到不影响其他分支上的读写操作。

在这里插入图片描述

引入共享排他锁后,究竟是如何实现的这点呢?

首先要弄清楚SX锁的特性,它不会阻塞S锁,但是会阻塞X、SX锁

在聊之前首先得搞清楚SQL执行时的几个概念:

  • 读取操作:基于B+Tree去读取某条或多条行记录。
  • 乐观写入:不会改变B+Tree的索引键,仅会更改索引值,比如主键索引树中不修改主键字段,只修改其他字段的数据,不会引起节点分裂。
  • 悲观写入:会改变B+Tree的结构,也就是会造成节点分裂,比如无序插入、修改索引键的字段值。

读操作的执行流程

  • ①读取数据之前首先会对B+Tree加一个共享锁。
  • ②在基于树检索数据的过程中,对于所有走过的叶节点会加一个共享锁。
  • ③找到需要读取的目标叶子节点后,先加一个共享锁,释放步骤②上加的所有共享锁。
  • ④读取最终的目标叶子节点中的数据,读取完成后释放对应叶子节点上的共享锁。

乐观写入的执行流程

  • ①乐观写入之前首先会对B+Tree加一个共享锁。
  • ②在基于树检索修改位置的过程中,对于所有走过的叶节点会加一个共享锁。
  • ③找到需要写入数据的目标叶子节点后,先加一个排他锁,释放步骤②上加的所有共享锁。
  • ④修改目标叶子节点中的数据后,释放对应叶子节点上的排他锁。

悲观写入的执行流程

  • ①悲观更新之前首先会对B+Tree加一个共享排他锁。
  • ②由于①上已经加了SX锁,因此当前事务执行过程中会阻塞其他尝试更改树结构的事务。
  • ③遍历查找需要写入数据的目标叶子节点,找到后对其分支加上排他锁,释放①中加的SX锁。
  • ④执行SMO操作,也就是执行悲观写入操作,完成后释放步骤③中在分支上加的排他锁。

如果需要修改多个数据时,会在遍历查找的过程中,记录下所有要修改的目标节点。

2.3:并发事务冲突分析

对于读操作、乐观写入操作而言,并不会加SX锁,共享排他锁仅针对于悲观写入操作会加

由于读操作、乐观写入执行前对整颗树加的是S锁,因此悲观写入时加的SX锁并不会阻塞乐观写入和读操作

但当另一个事务尝试执行SMO操作变更树结构时,也需要先对树加上一个SX锁,这时两个悲观写入的并发事务就会出现冲突,新来的事务会被阻塞。

在这里插入图片描述
当第一个事务寻找到要修改的节点后,会对其分支加上X锁,紧接着会释放B+Tree上的SX锁。这时另外一个执行SMO操作的事务就能获取SX锁啦

虽然一个执行悲观写入的事务,找到了要更新/插入数据的节点后会释放SX锁,但是会对其上级的叶节点(叶分支)加上排他锁,因此正在发生SMO操作的叶分支,依旧是会阻塞所有的读写行为!也就是当一个要读取的数据,位于正在执行SMO操作的叶分支中时,依旧会被阻塞。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络工程师 (8)存储管理

一、页式存储基本原理 (一)内存划分 页式存储首先将内存物理空间划分成大小相等的存储块,这些块通常被称为“页帧”或“物理页”。每个页帧的大小是固定的,例如常见的页帧大小有4KB、8KB等,这个大小由操作系统决定。同…

[EAI-026] DeepSeek-VL2 技术报告解读

Paper Card 论文标题:DeepSeek-VL2: Mixture-of-Experts Vision-Language Models for Advanced Multimodal Understanding 论文作者:Zhiyu Wu, Xiaokang Chen, Zizheng Pan, Xingchao Liu, Wen Liu, Damai Dai, Huazuo Gao, Yiyang Ma, Chengyue Wu, Bin…

(动态规划路径基础 最小路径和)leetcode 64

视频教程 1.初始化dp数组&#xff0c;初始化边界 2、从[1行到n-1行][1列到m-1列]依次赋值 #include<vector> #include<algorithm> #include <iostream>using namespace std; int main() {vector<vector<int>> grid { {1,3,1},{1,5,1},{4,2,1}…

cf1000(div.2)

Minimal Coprime最小公倍数 输入&#xff1a; 6 1 2 1 10 49 49 69 420 1 1 9982 44353 输出&#xff1a; 1 9 0 351 1 34371 代码

【单细胞第二节:单细胞示例数据分析-GSE218208】

GSE218208 1.创建Seurat对象 #untar(“GSE218208_RAW.tar”) rm(list ls()) a data.table::fread("GSM6736629_10x-PBMC-1_ds0.1974_CountMatrix.tsv.gz",data.table F) a[1:4,1:4] library(tidyverse) a$alias:gene str_split(a$alias:gene,":",si…

事务04之死锁,锁底层和隔离机制原理

死锁和事务底层原理 文章目录 死锁和事务底层原理一&#xff1a;MySQL中的死锁现象1&#xff1a;何为死锁1.1&#xff1a;死锁的概念1.2&#xff1a;死锁产生的四个必要条件&#xff1a; 2&#xff1a;MySQL的死锁2.1&#xff1a;死锁的触发2.2&#xff1a;MySQL的死锁如何解决…

Maven的单元测试

1. 单元测试的基本概念 单元测试&#xff08;Unit Testing&#xff09; 是一种软件测试方法&#xff0c;专注于测试程序中的最小可测试单元——通常是单个类或方法。通过单元测试&#xff0c;可以确保每个模块按预期工作&#xff0c;从而提高代码的质量和可靠性。 2.安装和配…

VirtualBox:跨磁盘导入已存的vdi磁盘文件顺便测试冷迁移

目录 1.背景 2.目的 3.步骤 3.1 安装在移动硬盘上 3.2.接管现有主机磁盘上的虚拟机 3.3接管迁移到移动硬盘的虚拟机 4. 结论 1.背景 电脑重新做了系统&#xff0c;然后找不到virtualbox的启动程序了&#xff0c;另外电脑磁盘由于存储了其他文件已经爆红&#xff0c;无法…

二级C语言:二维数组每行最大值与首元素交换、删除结构体的重复项、取出单词首字母

目录 一、程序填空 --- 二维数组每行最大值与首元素交换 题目 分析 知识点 --- 交换语句 二、程序修改 --- 删除结构体的重复项 题目 分析 三、程序设计 --- 取出单词首字母 题目 分析 前言 本章讲解&#xff1a;二维数组每行最大值与首元素交换、删除结构体的重复项…

优盘恢复原始容量工具

买到一个优盘&#xff0c;显示32mb&#xff0c;我见过扩容盘&#xff0c;但是这次见到的是缩容盘&#xff0c;把2g的容量缩成32MB了&#xff0c;首次见到。。用芯片查询工具显示如下 ChipsBank(芯邦) CBM2199E 使用以下工具&#xff0c;恢复原始容量。。 其他CMB工具可能不行…

面对企业文件交换难题,镭速跨网文件交换系统是如何解决的?

在当今这个数字化快速发展的时代&#xff0c;企业越来越依赖于数据交换来维持其业务运作。无论是内部网络之间的沟通还是与外部合作伙伴的数据共享&#xff0c;高效且安全的跨网文件交换都显得尤为重要。然而&#xff0c;在实际操作中&#xff0c;许多企业面临着各种各样的挑战…

黑马点评 - 商铺类型缓存练习题(Redis List实现)

首先明确返回值是一个 List<ShopType> 类型那么我们修改此函数并在 TypeService 中声明 queryTypeList 方法&#xff0c;并在其实现类中实现此方法 GetMapping("list")public Result queryTypeList() {return typeService.queryTypeList();}实现此方法首先需要…

计算机毕业设计Python+CNN卷积神经网络高考推荐系统 高考分数线预测 高考爬虫 协同过滤推荐算法 Vue.js Django Hadoop 大数据毕设

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

知识库管理如何推动企业数字化转型与创新发展的深层次探索

内容概要 在当今数字化转型的大背景下&#xff0c;知识库管理日益显现出其作为企业创新发展的核心驱动力的潜力。这种管理方式不仅仅是对信息的存储与检索&#xff0c;更是一种赋能&#xff0c;以提升决策效率和员工创造力。企业能够通过系统地整合和管理各类知识资源&#xf…

【Leetcode】463. 岛屿的周长

一、题目描述 给定一个KaTeX parse error: Undefined control sequence: \x at position 4: row\̲x̲\col的二维网格地图 g r i d grid grid&#xff0c;其中&#xff1a; g r i d [ i ] [ j ] 1 grid[i][j]1 grid[i][j]1表示陆地&#xff0c; g r i d [ i ] [ j ] 0 grid[…

企业微信远程一直显示正在加载

企业微信远程一直显示正在加载 1.问题描述2.问题解决 系统&#xff1a;Win10 1.问题描述 某天使用企业微信给同事进行远程协助的时候&#xff0c;发现一直卡在正在加载的页面&#xff0c;如下图所示 2.问题解决 经过一番查找资料后&#xff0c;我发现可能是2个地方出了问题…

【Block总结】ODConv动态卷积,适用于CV任务|即插即用

一、论文信息 论文标题&#xff1a;Omni-Dimensional Dynamic Convolution作者&#xff1a;Chao Li, Aojun Zhou, Anbang Yao发表会议&#xff1a;ICLR 2022论文链接&#xff1a;https://arxiv.org/pdf/2209.07947GitHub链接&#xff1a;https://github.com/OSVAI/ODConv 二…

英语语法 第一天

I’m a student. 我是个学生 我是个新东方的学生 I’m a student of New Oriental School 我爱你 I love you 我在心中爱你 I love you in my heart. 这是一朵花 This is a flower 这是一朵在公园里的花 This is a flower in the park.(修饰部分在修饰词后面) 主干…

doris:高并发导入优化(Group Commit)

在高频小批量写入场景下&#xff0c;传统的导入方式存在以下问题&#xff1a; 每个导入都会创建一个独立的事务&#xff0c;都需要经过 FE 解析 SQL 和生成执行计划&#xff0c;影响整体性能每个导入都会生成一个新的版本&#xff0c;导致版本数快速增长&#xff0c;增加了后台…

智联出行公司布局中国市场,引领绿色出行新潮流

近日&#xff0c;智联共享科技有限公司&#xff08;智联出行ZSTL&#xff09;正式入驻中国香港市场&#xff0c;开启中国地区“合伙人”战略部署&#xff0c;其线上服务平台也于同日上线。 作为共享单车领域的先行者&#xff0c;智联出行公司此举标志着其全球化布局的重要进展&…