聊聊MySQL中的锁

从电商秒杀到文件上传:聊聊upload-labs第17关背后的‘条件竞争’与MySQL锁的妙用 本文深入探讨了条件竞争漏洞在文件上传和电商秒杀等场景中的危害,并以upload-labs第17关为例,详细解析了漏洞原理及防御策略。重点介绍了MySQL悲观锁与乐观锁的实战应用,以及分布式环境下的锁机制演进,为开发者提供了多层防御体系构建方案。 阅读详情

原文地址:http://www.linzichen.cn/article/1571531799593484288

在程序开发中,当多个线程并发操作共享数据时,我们需要保证在任何时刻最多只能有一个线程在操作,以保证数据的 完整性和一致性。比如在 JAVA 中,单体应用有 synchornized 和 ReentrantLock锁,分布式应用有 分布式锁。同样在数据库中,用户数据作为一种共享资源,同样也提供对应的锁机制。

一、多事务访问的几种情况

并发事务访问相同记录的情况大致可以分为三种:

读读、写写、读写或写读。

1.1 读 - 读

即并发事务相继读取相同的记录。读取操作不会对记录有任何影响,也不会引起什么问题,所以允许这种情况发生。

1.2 写 - 写

即并发事务相继对相同的记录做出改动。

在这种情况下会发生脏写的问题,所以在多个事务对一条记录做改动时,需要让它们 排队执行。排队过程就是通过锁来实现的。它们的流程为:

1、当事务 T1 想对记录改动时,会先判断内存中是否存在当前记录的锁。如果不存在,则 T1会在内存中生成一个锁结构,与当前记录关联。
锁结构.png

锁结构中有很多信息,其中有两个重要的属性:

  • trx信息:当前锁结构是哪个事务生成的。
  • is_waiting:当前事务是否是等待状态。

2、在 T1事务提交之前,另一个事务T2也要对当前记录做改动,但是此时内存中,还存在 T1事务的锁与当前记录关联,所以 T2也会生成一条与当前记录关联的锁结构,只不过 T2锁结构的 is_waiting属性值是 true,代表当前事务需要等待。

3、在事务 T1提交之后,就会把它生成的锁结构释放掉,然后看看内存中有没有别的事务锁在等待状态,发现 T2在等待之后,会把 T2对应的锁结构的 is_waiting属性改为 false,然后把该事务的线程唤醒,让它继续执行,此时事务 T2就算获取到锁了,可以继续执行。

脏写:一个事务修改了另一个事务未提交的数据。
比如事务T1进行了 insert但还未提交:
insert into t1 (id, name) values (1, '张三');
但事务 T2对 T1未提交的数据进行了修改:
update t1 set name = '李四' where id = 1;
如果T1进行了回滚,那此数据实际是不存在的。这种现象就是脏写,数据库中任何隔离级别都不允许此情况发生。

1.3 读 - 写 或 写 - 读

即一个事务进行读操作,另一个进行写操作。这种情况下可能发生 脏读、不可重复读、幻读的问题。

脏读:事务T1读了另一个事务T2还未提交的数据。比如 T2进行了insert但还未提交,此时 T1事务select到了此数据,这种现象就是脏读。
不可重复读:事务T1查询了id=1的记录的name字段是张三,此时事务T2对此记录的name改为了 李四,并且提交了。然后 T1再次查询时发现 name字段变成了 李四。前后读取的数据不一致,这种现象就是不可重复读。
幻读:事务 T1查询了表中id>1的记录有3条,此时事务 T2进行了 insert操作(id > 1)并且提交,然后T1再次查询时记录变成了 4条。前后读取的记录数不一致。这种现象就是幻读。

二、并发事务问题解决方案

为了解决 脏读、不可重复读和幻读 这些现象的发生,其实有两种可选的解决方法。

MVCC

读操作利用 MVCC,写操作加锁。

所谓MVCC,就是通过 undo日志的版本链和 ReadView快照构成的多版本并发控制,它实际读取的是版本链中最新提交的数据,而非其他活动事务正在操作的数据,这样就可以避免因为活动中事务的写,造成的一系列的问题。MVCC不是本文的重点内容,所以这里不做过多介绍了。

加锁

脏读是因为事务 T1读到了事务T2写入的未提交的数据。那我们只要在T2写的时候加锁,让T1读的时候等待,等T2提交后才允许读,就可以避免脏读现象。

不可重复读是因为事务 T1先读了一条记录,随后事务T2修改了当前记录并提交,等T1再读时发现跟第一次读的不一致了。那我们只要在T1读的时候加锁,让T2写的时候等待,等T1读完之后T2再去进行写操作,就可以避免不可重复读。

幻读是因为事务T1查询了一个范围内的记录,随后事务T2对当前范围内插入了新的记录并提交,导致T1再查询范围时发现记录数变了。那我们可以在T1查询的范围上给加锁,此时T2在此范围内插入数据时让其等待,就可以避免幻读。

三、锁从不同角度的分类

mysql中的锁,不但保证了数据的一致性,而且还为实现各个隔离级别提供了保证。尤其对于一些敏感数据上我们必须采用加锁的方式,所以锁对数据库而言显得尤其重要,也更加复杂。本文从mysq锁的不同角度,详细聊一聊mysql中的锁。

锁家族.jpg

四、操作角度:读写锁

对于数据库中并发事务的读-读不会引起什么问题。对于 写-写、读-写或写-读这些情况可能会引起一些问题,需要通过 MVCC或加锁方式解决。在使用加锁方式时,由于既要允许读-读情况不受影响,又要使 写-写、读-写或写-读情况中的操作互相阻塞,所以mysql实现一个由两种类型的锁组成的锁系统来解决。这两种类型的锁通常被称为 共享锁(Shared Lock) 和 排他锁(Excluslve Lock),也叫 读锁(read lock)和 写锁(write lock) 。

4.1 共享锁

也称为读锁,英文用 S表示。针对同一份数据,多个事务的共享锁可以同时进行而不互相影响,互相不阻塞。

4.2 排他锁

也成为写锁,英文用 X表示。此事务没有完成前,它会阻断其他的排他锁和共享锁。这样能确保在给定的时间里,有且只有一个事务能访问同一资源。

4.3 读锁案例

  • 对读的记录加S锁:

一般情况下,我们需要对读操作加S锁,以避免不可重复读、幻读这些问题。

select …… lock in share moe;
# 或
select …… for share; #(8.0语法)

在普通的select语句后面加 lock in share mode,如果当前事务执行了该语句,那么它会为读取到的记录加S锁,此时允许其他事务继续获取这些记录的S锁,但是不能获取这些记录的X锁。想获取X锁的事务将会阻塞,知道当前事务的S锁释放掉后才可以。

读-读:
读读.png

读-写:
读写.png

4.4 写锁案例

  • 对读取记录加X锁:

在有些场景下,比如涉及到金额存取款问题等,我们需要对读操作加X锁。即当前事务在读取的时候,不需要其他的事务进行读写操作。

select …… for update;

在普通的select语句后加for update,如果当前事务执行了该语句,那么事务会为读取的记录加X锁。此时其他事物,无论是对这些记录加S锁 还是 X锁,都会进入阻塞状态。

写-读:
写读.png

写-写:
写写.png

4.5 小结:

当一个事务加了S锁,其他事务S锁不影响,X锁阻塞。

当一个事务加了X锁,其他事务S锁阻塞,X锁阻塞。

S锁X锁
S锁不阻塞阻塞
X锁阻塞阻塞

在5.7及之前版本,select …… for update,如果获取不到锁,会一直等待,直到innodb_lock_wait_timeout超时。
在8.0版本中,select …… for update,select …… for share 添加 NOWAIT、SKIP LOCKED语法,可以跳过锁等待,或者跳过锁定。

如果查询的行已经加锁:

  • NOWAIT:会立即返回报错。
  • SKIP LOCKED:会立即返回结果,结果中不包含被锁定的行。

五、 数据操作粒度角度:表锁、页锁、行锁

为了尽可能提高数据库的并发度,每次锁定的数据范围越小越好,理论上每次只锁定当前操作的数据会得到最大的并发度,但是管理锁是很耗资源的事情(涉及获取、检查、释放锁等动作)。因此数据库系统需要在高并发响应 和 系统性能两方面进行平衡,就产生了 锁粒度的概念。

对一条记录加锁影响的就是该记录而已,我们就说这个锁的粒度比较细;一个事务也可以在表级别进行加锁,我们称之为 表锁,对一个表加锁会影响这个表的记录,我们就说这个锁的粒度比较粗。锁的粒度主要分为表锁、页锁和行锁。

5.1 表锁

5.1.1 表级别的读写锁

在对表执行select、insert、delete、update 这些DML语句时,InnoDB不会为该表添加 S锁或X锁的,但是其他事务对该表进行 alter table、drop table 等这类 DDL语句时会发生阻塞。同理,当事务对表进行 DDL时,其他事务的 DML也会进行堵塞。这个过程是通过在server层使用一种称之为元数据锁(Metadata Locks,简称 MDL)结构来实现的。

表锁.png

手动获取表 的 S锁或者X锁:

  • lock tables t read:对表 t加 S锁。
  • lock tables t write:对表 t加 X锁。
  • lock tables t1 read, t2 write;:对表t1加S锁,t2加写锁。

表S锁.png

查看正在加锁的表:

  • show open tables where In_use > 0;
    in_use.png

释放当前会话的读写锁:

  • unlock tables:只能释放当前会话的锁,其他会话的锁释放不了。

unlock.png

小结

关于表锁的读写类型,可以用下面表格来概述:

锁类型自己可读自己可写自己可操作其他表他人可读他人可写
读锁是否否是否,等
写锁是是否否,等否,等

一般情况下不会对InnoDB存储引擎使用表级别的 S锁和X锁,它们并不会提供什么额外的保护,只会降低并发能力而已。InnoDB的厉害之处是实现了更细粒度的 行锁。

5.1.2 表级别的意向锁

概念

意向锁是由存储引擎自己维护的一种表级锁,我们自己无法手动操作意向锁。当我们给表中的某些记录添加了 共享锁时,存储引擎会自动给所在的表添加 意向共享锁;当我们给表中的某些记录添加了排他锁时,存储引擎会自动给所在的表添加意向排他锁。

  • 意向共享锁(intention shared lock,IS):由事务添加行的共享锁时,存储引擎自动给表添加的。
  • 意向排他锁(intention exclusive lock,IX):由事务添加行的排他锁时,存储引擎自动给表添加的。

为什么需要意向锁

此时可能会有疑问,我们既然给表中的记录填加行级的共享/排他锁,为什么还需要给表添加意向锁?其实意向锁的存在,是为了协调行锁和表锁的关系。举个例子:

假如事务T1想给a表添加表的排他锁(X锁),因为排他锁互斥的关系,T1需要在加锁前,判断该表中的所有记录行,是否存在 S锁 或 X锁,如果数据量很大,每一行都去做判断则非常的耗时。所以我们在给表中记录添加S/X锁时,会对应的给表也添加上IS/IX锁。此时事务T1再想给表a添加X锁时,只需要先判断该表是否存在 IS/IX锁即可,就不需要再去遍历每一行判断了,可以提升系统效率。

意向锁之间的关系

行级锁的某一条记录,同一时间内可以存在多个S锁,但只能存在一个X锁,且S锁与X锁互相排斥。而在意向锁中,由于多个事务可以给记录同时添加S锁,所以一个表也会存在多个IS锁;由于多个事务可以给表中不同的记录分别添加X锁,所以一个表也会存在多个IX锁。

所以我们得出结论,意向锁之间互不排斥,可以共存。意向锁与行锁之间不排斥,可以共存。

表锁与意向锁之间:表的S锁与 IS锁可以共存,其余的表锁与意向锁互斥。

小结

  1. InnoDB支持多粒度锁,行级锁可以与表级锁共存。
  2. 意向锁之间互相兼容,意向锁与行级锁之间互相兼容。
  3. 意向共享锁与表共享锁兼容,其余的意向锁与表锁互不兼容。
  4. 意向锁在保证并发的前提下,实现了行锁和表锁共存且满足事务隔离性的要求。

5.1.3 表级别的元数据锁

mysql5.5 引入了 meta data lock,简称MDL锁,属于表锁范畴。MDL的作用是,保证读写的正确性。比如如果一个查询正在遍历表中的数据,而执行期间另一个线程对这个表结构做变更,增加了一个字段,那么查询线程拿到的结果跟表结构对不上,肯定不允许的。

因此,当对一个表做增删改查操作的时候,加MDL读锁;当对表做结构变更操作的时候,加MDL写锁。

读锁之间不互斥,因此可以有多个线程同时对一张表增删改查。读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性,解决了DML和DDL操作之间的一致性问题。不需要显示调用,在访问一个表的时候会自动加上。

元数据锁.png

此时返现DDL操作的事务处于等待状态,看一下当前正在运行的线程,发现该事务正在进行元数据锁等待。

元数据锁等待.png

5.2 InnoDB行锁

行锁(Row Lock)也称为记录锁,顾名思义,就是锁住某一行(某条记录)。需要注意的是,mysql 服务层并没有实现行锁机制,行级锁只在存储引擎层实现。

优点:锁定粒度小,发生 锁冲突概率低,可以实现的 并发度搞。

缺点:对于锁的开销比较大,加锁会比较慢,容易出现死锁情况。

InnoDB与MyISAM最大不同有两点:一是支持事务;二是采用了行级锁。

5.2.1 记录锁

记录锁是有S锁和X锁之分的,称之为S型记录锁和X型记录锁。

  • 当一个事务获取了一条记录的S型记录锁后,其他事务也可以继续获取该记录的S型记录锁,但不可以继续获取X型记录锁。
  • 当一个事务获取了一条记录的X型记录锁后,其他事务既不可以获取改获取的S型记录锁,也不可以继续获取X型记录锁。

5.2.2 间隙锁

间隙锁可以解决mysql中幻读问题。由于间隙锁细节比较多,比如聚簇索引与二级索引的间隙锁表现不同,二级索引中覆盖索引与回表的表现又不同。所以单独拿出一盘文章来详细聊聊,此处不再介绍。

5.2.3 临键锁

临键锁可以看做是间隙锁和记录锁的结合,也跟间隙锁放在同一篇文章里聊。

5.2.4 插入意向锁

5.3 页锁

页锁就是在页的粒度上进行锁定,锁定的数据资源比行锁要多,因为一个页中可以有多个行记录。当我们使用页锁的时候,会出现数据浪费的现象,但这样的浪费最多也就是一个页上的数据行。页锁的开销介于表锁和行锁之间,会出现死锁。锁定的粒度介于表锁和行锁之间,并发度一般。

每个层级的锁数量是有限制的,因为锁会占用内存空间,锁空间的大小是有限的。当某个层级的锁数量超过了这个层级的阈值时,就会进行锁升级。锁升级就是用更大粒度的锁替代多个更小粒度额锁,比如InnoDB中行锁升级为表锁,这样做的好处是占用的锁空间降低了,但同时数据的并发度也下降了。

六、 悲观锁和乐观锁

从对待锁的态度来看锁的话,可以将锁分成乐观锁和悲观锁,从名字中也可以看出这两种锁是两种看待数据并发的思维方式。需要注意的是,乐观锁和悲观锁并不是锁,而是锁的设计思想。

6.1 悲观锁

顾名思义,就是以悲观的态度去看待事务访问。所以为了自己的事务不被其他事务干扰,每次访问数据时都会加锁,这样其他事务想访问数据就会阻塞。悲观锁是一种思想,会通过数据库自身的锁机制来实现,从而保证数据操作的排他性。

在一些商场业务系统中,购买商品的业务可以简化为 查询商品库存-下订单-扣减库存。我们应该保证这三步是原子性操作,如果不加锁,可能最后会出现商品库存是负数的情况。而我们需要将执行的sql语句都放在一个事务中,否则达不到原子性的目的。

# 1. 查询商品库存
select quantity from items where id = 1001 for update ;
# 2. 如果库存大于0,则需要生成订单
insert into orders (……) values (……) :
# 3. 修改商品的库存,num表示购买的数量
update items set quantity = quantity - num where id = 1001;

select …… for update 是mysql中的悲观锁。此时在items表中,if为1001的商品已经被锁定了,如果其他事务要对这个商品进行购买时,必须等待本次事务提交之后才能执行。这样就可以保证当前的数据不会被其他事务修改。

注意:select …… for update 语句执行过程中所有扫描的行都会被锁上,因此在mysql中用悲观锁,必须确定使用了索引而不是全表扫描,否则将会被这个表锁上。

悲观锁.png

小结:悲观锁不使用的场景较多,它存在一些不足,因为悲观锁大多数情况下依靠数据库的锁机制来实现,以保证程序的并发访问行,这样对数据库性能开销影响很大,特别是长事务而言,这样的开销往往无法承受,这时候就需要乐观锁。

6.2 乐观锁

乐观锁对并发事务持乐观的态度,不在乎其他事务是否修改了数据,所以乐观锁不采用数据库自身的锁机制,而是通过程序来实现的。乐观锁可以用 版本号和时间戳机制来实现。

1. 乐观锁的版本号机制

在数据表中设计一个版本字段 version,在读数据时也要把 version 字段的值查出来。然后对数据进行修改的时候,执行 update …… set version = version + 1 where version = version。此时如果有其他事务对这条数据进行了更改,则 where 条件不成立,所以本次修改就不会成功。

这种方式类似于 SVN、GIT版本管理系统,当我们修改了代码进行提交时,首先会检查当前版本号与服务器上的版本号是否一致,如果一直就可以直接提交,如果不一致就需要更新服务器上的最新代码,然后再进行提交。

2. 乐观锁的时间戳机制

时间戳和版本号机制一样,也是在更新提交的时候,将当前库里数据的时间戳和之前查出来的时间戳做比较,如果两者一致,则没有被其他事务更改过,可以更新成功,否则就更新失败。

用乐观锁实现商城业务购买:

# 1. 查询商品库存
select quantity, version from items where id = 1001 ;
# 2. 如果库存大于0,则需要生成订单
insert into orders (……) values (……) :
# 3. 修改商品的库存,num表示购买的数量
update items 
set quantity = quantity - num, version = version + 1 
where id = 1001 and version = #{version} ;

这里我们也可以根据具体业务,因为 库存不能有小于0的场景,所以还可以加一个 where quantity - num >= 0 的条件。

注意:如果数据库采用了读写分离,当 master 表中写入的数据没有及时同步到 slave 表中的时候,会造成更新一直无效的问题,此时需要强制读取master表的数据(即把select语句放到事务中即可,这时候查询的就是master表)。

6.3 两种锁的使用场景

1、乐观锁适合 读操作多的场景,相对来说写的操作比较少。优点在于 基于程序实现,不存在死锁问题,适用场景也会相对乐观,因为它阻止不了除了程序以外的数据库操作。

2、悲观锁适合写操作多的场景,因为写的操作具有排他性。采用悲观锁的方式,可以在数据层面阻止其他事务对该数据的操作权限,防止 读写和写写的冲突。

七、 显式锁和隐式锁

7.1 显示锁

通过特点的语句进行加锁,我们一般称之为显示加锁,例如:

显示加共享锁:

select …… lock in share mode

显示加排他锁:

select …… for update

7.2 隐式锁

在聊隐式锁之前,我们先回顾一下记录的行格式。在 InnoDB索引数据结构 一文中,提到过行格式的示意图。
Compact 行格式

在 其他信息中存在着该记录的一些隐藏信息,我们称之为 隐藏列。隐藏列包含以下三部分:

  • row_id: 行ID,记录的唯一标识;当用户在表中定义了主键字段就优先选择用户定义的主键,如果没有,就查找是否有定义不为null的唯一索引,如果有就把该列作为主键,如果没有MySQL就会生成一列row_id隐藏列作为主键。
  • trx_id: 事务的ID;记录最后一次操作该数据的事务ID。
  • roll_pointer: 回滚指针,指向的是该记录的上一个版本号,MySQL的MVCC主要就是通过这个字段来实现的。

一个事务在执行insert操作时,如果即将插入的间隙已经被其他事务加了 gap锁,那么本次 insert操作会阻塞,并且当前事务会在该间隙上加一个 插入意向锁,否则一般情况下 insert操作是不加锁的。那如果一个事务T1首先插入了一条记录,在未提交事务之前,其他的事务·T2·:

  • 立即使用 S锁读取这条记录,如果允许,就会产生脏读问题。
  • 立即修改这条记录,也就是给这条记录添加X锁,如果允许,就会产生脏写问题。

这时候上面提到过的 事务id就起作用了。事务T1 在进行 insert操作,这条记录的 trx_id就是 该T1事务的id,此时T2想对该记录添加S/X锁时,首先会看一下该记录的trx_id代表的事务T1 是否处于活跃状态,如果T1处于活跃状态,那么T2就会帮T1创建一个X锁,且锁的 is_waiting属性是false,然后T2进入等待状态(也就是给自己再添加一个锁机构,is_waiting属性是 true)。

即:一个事务新插入记录时,不会产生锁结构,但是由于事务id的存在,相当于加了一个 隐式锁。其他事务在对这条记录加锁的时候,由于隐式锁的存在,会先帮助当前事务生成一个锁结构,然后自己再生成一个锁结构并进入等待状态。 所以 隐式锁是一种延迟加载的机制,这样的目的是为了减少加锁的数量。

隐式锁在实际内存对象中并不含有这个锁信息,只有当产生锁等待是,隐式锁才会转换为显式锁。

隐式锁的逻辑过程

A. InnoDB的每条记录中都有一个隐含的trx_id字段,这个字段存在于聚簇索引的B+Tree中。

B. 在操作一条记录钱,首先根据记录中的 trx_id 检查该事务是否处于活动中事务(未提交)。如果是活动事务,首先将隐式锁转换为显式锁(就是为该事务添加一个锁)。

C. 检查是否有锁冲突,如果有冲突,创建锁,并设置waiting状态为等待。如果没有冲突则不加锁,跳到步骤 E。

D.等待加锁成功,被唤醒,或者超市。

E. 写数据,并将自己的事务的id 写入行记录的 trx_id 字段

八、 全局锁

全局锁就是对整个数据库实例加锁。当需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(增删改查)、数据定义语句(建表、修改表结构等) 和 更新类事务的提交语句。全局锁的典型使用场景是做全库逻辑备份。

flush table with read lock

九、 死锁

两个事务都持有对方需要的锁,并且都在等待对方先释放,并且双方都不会释放自己的锁。

9.1 举例

事务1事务2
start transaction;
update account set money = 100 where id = 1
start transaction;
update account set money = 100 where id = 2
update account set money = 200 where id = 2
update account set money = 100 where id = 1

9.2 产生条件

  1. 两个或以上事务。
  2. 每个事务都已经持有锁并且申请新的锁。
  3. 锁资源同事只能被同一个事务持有或者不兼容。
  4. 事务之间因为持有锁和申请锁导致彼此循环等待。

死锁的关键在于:两个或以上的事务加锁的顺序不一致。

9.3 如何处理

方式一:等待,知道超市(innodb_lock_wait_timeout = 50s)

即两个事务互相等待时,当一个事务等待时间超过设置的阈值时,就将其回滚,另外事务继续进行。这种方法简单有效,在innodb中,参数innodb_lock_wait_timeout用来设置超时时间。

缺点:对于在线服务来说,这个等待时间往往是无法接受的。假如把这个配置值改短,比如0.1s,也不合适,容易影响到普通的锁等待。

方式二: 使用死锁检测进行死锁处理

innodb提供了 wait-for graph算法来主动进行死锁检测,每当加锁请求无法立即满足需要并进入等待时,该算法都会被触发。

这是一种较为主动的死锁检测机制,要求数据库保存锁的信息链表和事务等待链表两部分信息。

9.4 如何避免

  • 合理实际索引,使业务sql尽可能通过索引定位更少的行,减少锁竞争。
  • 调整业务逻辑sql执行顺序,避免 update/delete 长时间持有锁的sql 在事务里。
  • 避免大事务,尽量将大事务拆成多个小事务来处理,小事务缩短锁定资源的时间,发生锁冲突的几率也更小。
  • 在并发比较高的系统中,不要显示加锁,特别是在事务里显示加锁。如 select …… for update 语句,如果实在事务里运行了 start transation 或者 设置了 autocommit 等于0,那么就会锁定所有查找到记录。
  • 降低隔离级别。如果业务允许,将隔离级别调低,比如从 RR调整为 RC,可以避免很多因为gap锁造成的死锁。
结合java中的锁聊聊锁的本质 在操作系统里面,也会遇到什么信号量、互斥量,然后说利用互斥量、信号量可以实现锁的功能,而操作系统提供的原语有又mutex锁在学习数据库的时候,什么表锁、行锁、读锁、写锁、排它锁、意向锁、meta锁等等,各种各样的锁的概念蜂拥而至在学习java的时候,我们会学习到synchronize关键字、jdk里的Lock接口,然后有各种各样的Lock的实现;然后在聊synchronize的时候又会又各种锁的概念出来:自旋锁、偏向锁、轻量级锁、重量级锁。 阅读详情

相关推荐

VS2019中C++动态链接库(DLL)的创建与调用

VS2019中C++动态链接库(DLL)的创建与调用,及一些进阶技巧。

qq_42232522的博客 7562

锁系列四之行锁

行锁,也称为记录锁,mysql服务层没有实现行锁机制,行锁只存在存储引擎层实现。 优点:锁粒度小,发生锁冲突的概率低,可以实现的并发度高。 缺点:对于锁的开销比较大,加锁会比较慢,容易出现死锁的情况。 案例 一、记录锁(Record Locks) 记录锁也就是仅仅把一条记录锁上,Lock_REC_NOT_GAP,比如把id=8的记录加上一个记录锁的示意图如下。 记录锁是有S锁和X锁之分的,称之为 S型记录锁 和 X型记录锁 (1)当一个事务获取了一条记录的S型记录锁后,其他事务也可..

勇心在馨 5235

终端运行脚本报: 因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fw link/?LinkID=135170 中的 about_Execution_

PowerShell 执行策略是一种安全机制,用于控制哪些脚本可以在系统上运行。默认情况下,Windows 系统会设置较为严格的执行策略,以防止恶意脚本的执行。执行策略有以下几种常见级别:Restricted(限制):不允许运行任何脚本。AllSigned(全部签名):仅允许运行经过数字签名的脚本。RemoteSigned(远程签名):允许运行本地脚本,但远程下载的脚本需要数字签名。Unrestricted(无限制):允许运行所有脚本,但会警告未签名的脚本。

欢迎来到我的技术博客!这里是一个专注于软件开发的地方,我将分享关于 Java、Python、C++ 和易语言等多种编程语言的教程、技巧、最佳实践以及开发经验。 5077

事务ACID特性;脏读、不可重复读、幻读;读未提交、读已提交、可重复读、串行化

事务具有4个特征,分别是原子性、一致性、隔离性和持久性,简称事务的ACID特性; 一、原子性(atomicity) 一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作,这就是事务的原子性 二、一致性(consistency) 事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。 如果数据库系统在运行过程中发生故障,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所作的修改有一部分已写入物理数据库,这是数据库就处于一种不正确

haoranhaoshi的博客 1208

英飞凌IGBT驱动芯片2ED020I12F2去饱和电路设计总结整理收集

记录下英飞凌IGBT驱动芯片限流电阻阻值设计

nizhenyuchina的博客 5994

BIDS数据存储格式介绍与dcm2bids安装与使用

BIDS格式&dcm2bids安装与使用

machinelearning_er的博客 7312

Mysql高级部分系列(六)

1、MySQL锁 锁是计算机协调多个进程或线程并发访问某一资源的机制。在程序开发中会存在多线程同步的问题,当多个线程并发访问某个数据的时候,尤其是针对一些敏感的数据(比如订单、金额等),我们就需要保证这个数据在任何时刻最多只有一个线程在访问,保证数据的完整性和一致性。在开发过程中加锁是为了保证数据的一致性,这个思想在数据库领域中同样很重要。 在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。为保证数据的一致性,需要对 并发操作进行控制 ,因此产生了锁 。

prefect_start的博客 1942

【从零开始玩量化1】AKShare: Python开源财经数据接口库

文章目录量化的第一步:数据AKShare介绍AKShare安装AKShare使用AKTools安装/使用结语 量化的第一步:数据 想要玩量化,第一步要有数据,这个数据可能是历史交易数据、财务数据、宏观数据等等。我们最终要做的,无非就是从数据中分析出有价值的信息,以便用来指导投资策略。 在获取数据的这条道路上,我走了很多弯路,尝试了各种方法:使用Tushare到放弃、自己写网易股票页面的爬虫,自己写申万行业数据的爬虫、自己写同花顺问财的爬虫、使用聚宽的免费数据API等等,直到最终,我发现了AKShare。 A

量化投资助手的博客 5万+

MySQL中的锁

IS、IX锁是表级锁,它们的提出仅仅为了在之后加表级别的S锁和X锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录。就是说其实IS锁和IX锁是兼容的,IX锁和IX锁是兼容的。我们画个表来看一下。

weixin_55229531的博客 5万+

文献计量VOSviewer分析知网文献基本操作及实用技巧

本教程旨在为读者提供一个关于VOS viewer的初级使用指南。VOS viewer是一个强大的科学研究可视化工具,主要用于构建和可视化科学地图。它能够处理大规模的科研数据,包括文献引用、共引、共词、共作者等信息。无论你是科研人员,还是数据分析师,甚至是研究生,都能在这个教程中找到使用VOS viewer的有效方法。我们将一步步教你如何使用这个软件,以及如何利用它进行科研数据的可视化。

Jia_n的博客 8766

告别Qt Creator?手把手教你用VSCode+MinGW调试QT项目(附完整launch.json配置)

本文详细介绍了如何在VSCode中配置完整的QT开发环境,特别是针对使用MinGW编译器的用户。从环境搭建到调试配置,包括生成.pro文件、Makefile,以及配置task.json和launch.json文件,帮助开发者高效调试QT项目。

weixin_27180827的博客 186

谈一下线程之间为什么是不可见的

前言: 因为最近刚学习线程相关的知识,所以想在此做一下笔记,顺带借此机会说一下自己对于相关知识的 理解以供参考;如在本文中发现有任何不妥的地方,请在评论区评论或与作者联系,以供修正;最后希望能在csdn这个平台与大家一起学习,共同进步。 正文: ...

林中有雨的博客 4998

【系列06】端侧AI:构建与部署高效的本地化AI模型 第5章:模型剪枝(Pruning)

结构化剪枝移除的是模型中的整个结构,比如一个完整的神经元、一个卷积核或一个通道。修剪后的模型权重矩阵仍然是稠密的,因此能够充分利用现代硬件和软件的并行计算优势,从而显著提升推理速度。非结构化剪枝是最细粒度的剪枝方法。对于需要极致压缩但对推理速度要求不高的场景,非结构化剪枝是好的选择。它就像修剪一棵树,通过移除模型中不必要的“枝叶”,让模型变得更精简、更高效,从而适应资源受限的设备。,开发者可以方便地对模型的不同层进行剪枝,并根据需求进行微调,从而在不影响太多性能的前提下,获得一个更轻量、更高效的模型。

学习,输出==》再学习再输出 649

聊聊mysql中的锁

今天我们聊聊mysql中innodb存储引擎中支持的锁以及锁算法 先看下innodb对事物的隔离级别支持的程度: 应对这些隔离级别innodb又是怎么实现的呢?我们一起来看下,innodb支持的锁类型有 共享锁(行锁):Sh...

lvjingang的博客 220

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

mysql 聊聊InnoDB七种锁

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。 为什么需要加锁呢? InnoDB的七种锁介绍 一条SQL是如何加锁的 RR隔离级别下的加锁规则 如何查看事务加锁情况 死锁案例分析 1. 为什么需要加锁? 数据库为什么需要加锁呢? 在日常生活中,如果你心情不好。想要一个人静静,不想被比别人打扰,你就可以把自己关进房间里,并且反锁。 同理,对于MySQL数据库来说的话,一般的

LoveSummer 4876

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

聊聊Mysql锁问题

今天一起来聊聊Mysql锁问题

陈泽钦的博客 1167

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

聊聊Mysql事务中,如何释放锁

在 MySQL 中,事务的锁是在事务结束时释放的。锁用于确保数据的一致性和完整性,防止多个事务同时修改同一数据导致冲突。COMMITCOMMITROLLBACKROLLBACK在关闭自动提交模式后,你需要手动使用COMMIT或ROLLBACK来结束事务并释放锁。

vangoghlaam的博客 938

一文带你聊聊MYSQL的锁和MVCC

本文通过LBCC(单版本控制-锁)的概念切入,分析了全局锁、表锁、行锁、页锁、死锁、两阶段锁等概念,还分析了MVCC(多版本并发控制),比较了两者的区别以及适用场景。

Linn的博客 1483

面试必问系列之聊聊Mysql -- InnoDB 锁

这次我们来聊聊MySQL数据库中InnoDB引擎中的锁。 InnoDB引擎是支持事务和行锁的。那么什么是行锁呢。 行锁 什么是行锁 行锁就是每次锁定的是一行数据的锁机制,也就是行级别锁定(row-level)。 行锁的实现方式 InnoDB中的行锁是通过给索引上的索引项加锁来实现的。 重点:所以只有通过索引条件检索数据,InnoDB才能使用行级锁,否则将使用表锁。 注意事项 1. 因为行锁的实现方式是对索引上的索引项加锁,而不是针对记录,所以使用相同的索引键是会出现锁冲突的。比如索引的类型

pontuss的博客 182

mysql记录锁与互斥锁区别_和你聊聊MySQL锁

1.前言在我们的日常生活中,随处可见锁的存在,比如自行车锁、电动车锁、汽车锁等。再比如商场的存货柜,持有锁的用户可以在指定的柜门中存放需要暂存的物品,其它没有锁的用户只能存放在其它柜门中或者等待你把物品取出并归还锁后才可以进行物品存放。从上面的描述中,你很容易得出锁存在的意义就是对资源的保护,资源包括独立资源、共享资源。在程序中我们讨论最多的就是共享资源,由于其共享的特性,会带来并发问题,从而导致...

weixin_30299099的博客 252

mysql 锁_大白话聊聊mysql的悲观锁

1、什么是悲观锁 2、mysql的悲观锁实现 3. 聊聊数据库悲观锁的用途 4. 聊聊数据库悲观锁的缺点大家好,我是四九城最豪横的小耳朵。 今天咱们来用大白话聊聊mysql的悲观锁。1、什么是悲观锁比如线程A对某个变量进行修改,在这个修改期间,它持悲观心理,认为其他线程在这个期间,也有可能去修改这个变量,所以它就给变量加个锁,保证在它修改期间,别的线程没法去访问这个变量。这个锁就是悲观锁。2、m...

weixin_39562234的博客 156

mysql:聊聊mysql中的锁八股文,锁分类,表锁(s锁,x锁,意向锁,自增锁,元数据锁)行锁(记录锁,间隙锁,临键锁,插入意向锁)乐观锁,悲观锁,显示锁,隐式锁

mysql:聊聊mysql中的锁八股文,锁分类,表锁(s锁,x锁,意向锁,自增锁,元数据锁)行锁(记录锁,间隙锁,临键锁,插入意向锁)乐观锁,悲观锁,显示锁,隐式锁

谷咕咕 391

从阻塞来聊聊mysql的 排他锁 共享锁 mvcc乐观锁;间隙锁;

读取一致性:当一个事务执行读取操作时,它会根据自己的事务 ID 和数据行的版本信息来决定哪些版本的数据是可以看到的。写入操作:当一个事务执行写入操作时,MVCC 会在数据行旁边创建一个新的版本,而不是直接修改现有的行数据。这也意味着写入操作通常也不需要加锁。共享锁(Shared Lock, S-Lock):多个事务可以同时获得共享锁,但如果有事务想要获取排他锁,则所有持有共享锁的事务都将被阻塞。阻塞:当一个事务使用 FOR UPDATE 获取锁时,其他想要修改相同数据的事务会被阻塞,直到第一个事务释放锁。

qq_21561833的博客 825

MySql系列:我们简单聊聊事务和锁的概念~~

聊一聊锁和事务,RR下如何解决幻读,死锁的检测~~~

程序员ZZ的源码 410

从电商秒杀到文件上传:聊聊条件竞争漏洞的“时间差”攻击与MySQL锁防范

本文深入探讨了条件竞争漏洞在电商秒杀和文件上传场景中的危害与防御策略。通过分析时间差攻击原理,对比不同锁机制(数据库行锁、文件锁、分布式锁)的适用场景,并提供了Upload-labs实战中的三种防御方案:原子操作、临时随机目录和内容检测+重命名,帮助开发者有效防范条件竞争漏洞。

weixin_33701251的博客 197
上一篇: CSS var() 函数
下一篇: CompletableFuture 引发的线上问题
Zichen_Lin
博客等级 码龄9年 9粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值