数据库的锁

是用于管理对共享资源的并发访问,是数据库系统区别于文件系统的一个关键特性

MySQL中的锁

在这里插入图片描述
本文主要来谈InnoDB引擎,InnoDB引擎支持行锁、表锁粒度的锁

意向锁

为了支持多粒度锁定,InnoDB 存储引擎引入了意向锁(Intention Lock
意向锁是表级锁

什么是意向锁呢?如果没有意向锁,当已经有人使用行锁对表中的某一行进行修改时,如果另外一个请求要对全表进行修改,那么就需要对所有的行是否被锁定进行扫描,在这种情况下,效率是非常低的;不过,在引入意向锁之后,当有人使用行锁对表中的某一行进行修改之前,会先为表添加意向排他锁(IX),再为行记录添加排他锁(X),在这时如果有人尝试对全表进行修改就不需要判断表中的每一行数据是否被加锁了

申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突

锁的兼容性

共享锁(读锁、S锁),允许事务读一行数据。

排他锁(写锁、X锁),允许事务删除或更新一行数据。

读锁会阻塞其他事务写,写锁会阻塞其他事务的读和写

行锁的算法

  • Record Lock:行锁,单个行记录上的锁
  • Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止幻读、防止间隙内有新数据插入、防止已存在的数据更新为间隙内的数据
  • Next-Key Lock:1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。InnoDB行锁默认加锁方式是next-key 锁

如何加锁

首先需要了解MVCC + 2PL + 事务隔离级别

MVCC

ref: https://mp.weixin.qq.com/s/Jeg8656gGtkPteYWrG5_Nw

首先我们需要了解MVCC(Multi-Version Concurrency Control),与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)

特点:读不加锁,读写不冲突

InnoDB之所以并发高,因为快照读不加锁,所有普通select都是快照读
MVCC只在 READ COMMITTED 和 REPEATABLE READ 2个隔离级别下工作

快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外)

select * from table where ?;

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;

所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

在RR级别下,快照读是通过MVVC(多版本控制)和undo log来实现的,当前读是通过加record lock(记录锁)和gap lock(间隙锁)来实现的。

MVCC原理分析

两段锁协议

数据库遵循的是两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁)

  • 加锁阶段:在该阶段可以进行加锁操作。在对任何数据进行读操作之前要申请并获得S锁,在进行写操作之前要申请并获得X锁。加锁不成功,则事务进入等待状态,直到加锁成功才继续执行。
  • 解锁阶段:当事务释放了一个封锁以后,事务进入解锁阶段,在该阶段只能进行解锁操作不能再进行加锁操作。
  • 这种方式虽然无法避免死锁,但是两段锁协议可以保证事务的并发调度等同于串行的执行

3事务隔离

隔离性:并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。不同事务的隔离级别,实际上是一致性与并发性的一个权衡与折衷。

锁可以实现事务并发,但也导致了一些问题

  • 更新丢失:A事务正常运行,B事务回滚
  • 脏读 :A事务读取到了B事务没有提交的数据,B事务有回滚,或者继续更新,反正数据变了,A读到的无效
  • 不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
  • 幻读

Read Uncommited
可以读取未提交记录。此隔离级别,不会使用,忽略。

Read Committed (RC)
针对当前读,RC隔离级别保证对读取到的记录加锁 (记录锁),存在不可重复读

Repeatable Read (RR)
针对当前读,RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),存在幻读
这也是InnoDB的默认隔离级别

Serializable
从MVCC并发控制退化为基于锁的并发控制。不区别快照读与当前读,所有的读操作均为当前读,读加读锁 (S锁),写加写锁 (X锁),不存在幻读。

1.读未提交RU

2.读已提交RC

InnobDB加锁是对索引加行锁。如果没有索引的话,会给整张表的所有数据行的加行锁,再进行过滤,发现不满足后,会调用unlock_row方法,把不满足条件的记录释放锁 (违背了二段锁协议的约束)

实现方式:

  • 普通读(快照读):基于MVCC并发控制,在每次读之前生成一个ReadView
  • 当前读:基于锁的并发控制,行锁或表锁
    • RC下:加S, X锁

3.可重复读RR

  • InnoDB默认隔离级别为RR
  • InnoDB RR解决了幻读

实现方式:

  • 普通读(快照读):基于MVCC并发控制,在该事务第一次读之前生成一个ReadView

  • 当前读:基于锁的并发控制,行锁或表锁

    • select for update;锁住了,另一个事务阻塞,解决幻读

    • RR下:加S,X + Gap锁 = next-key锁,此时避免了幻读

    •   begin;
        select *
        from student
        where s_name = 'john' for update;  行锁
        /*where id>=1 for update;*/ next-key锁
        
        /*上面事务不提交,下面的会一直阻塞*/
        begin ;
        insert into student values(2,'john','c2');
        commit;
        
        /* 如果是RC级别下,就会造成幻读 */
      

避免幻读 Next-key = 行锁+Gap锁

  • 如果where条件部分命中或者全部命中,则会加Gap锁
  • Gap锁会用在非唯一索引或者不走索引的当前读中
  • 受限于这种实现方式,Innodb很多时候会锁住不需要锁的区间。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于Spring事务和数据库的相关问题,我可以为你提供一些基本的信息。请注意,以下内容仅供参考。 Spring事务是一种用于管理数据库操作的机制,它确保了数据的一致性和完整性。在Spring中,你可以使用声明式事务或编程式事务来管理数据库操作。 声明式事务是通过在方法或类级别上添加事务注解来实现的。常用的事务注解包括`@Transactional`,它可以应用在方法或类上,并提供了一系列属性来配置事务的行为。 编程式事务是通过编写代码来手动管理事务的开始、提交和回滚过程。Spring提供了`TransactionTemplate`类来支持编程式事务管理。 关于数据库,它是一种用于控制并发访问数据库的机制。数据库可以保证数据的一致性和完整性,防止多个事务同时对同一数据进行修改而导致冲突。 常见的数据库包括行级、表级和页级。行级用于数据库中的单个数据行,表级用于定整个表,而页级则用于数据库中的页。 在Spring事务中,默认情况下,使用的是数据库的行级机制。如果需要使用其他级别的或更复杂的模式,你可以在SQL语句中明确指定。 需要注意的是,使用机制可能会导致性能下降和死等问题。因此,在设计和实现数据库操作时,需要合理地选择和使用机制。 希望以上信息对你有所帮助。如果你还有其他问题,请继续提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值