一、InnoDB锁
对于MyISAM引擎来说,其锁是表锁。InnoDB引擎提供行锁。
1、InnoDB行锁
1)、共享锁(S Lock),允许事务读一行数据。
2)、排他锁(X Lock),允许事务删除或者更新一行数据。
当一个事务已经获得了行R的共享锁,其他事务可以立即获得该行R的共享锁,我们称之为锁兼容。但如果有事务像获得行R的排他锁,则它必须等待事务释放行上的共享锁,我们称之为锁不兼容。
下面我们来看一个例子:
可以看到我们在一个会话A使用了S锁,所以另一个B会话就不会获取到X锁了。不过我们来看下查询另一个Id:
可以看到我们并没有提交前面A会话的事务,B会话能给另一个Id加X锁。通过这个验证即表明了是使用的行锁,同时也表明了X锁与S锁是不兼容的。
3)、意向锁
InnoDB存储引擎支持多你读的锁定,即允许行级锁与表级锁同时存在。为了支持这种不同粒度的加锁操作,InnoDB存储引擎支持一种额外的锁方式,即意向锁。意向书是表级锁,其的目的主要是为了在一个事务中揭示下一行将被请求的锁类型,其不需要你主动操作。可以简单地将其理解,其表明这张表目前的加X或者S锁的状态。
意向共享锁(IS Lock):事务想要获得一个表中某几行的共享锁
意向排他锁(IX Lock):事务想要获得一个表中某几行的排他锁
2、锁&事务状态查看
1)、查看事务的锁状态
这里我们另起一个C会话,使用show engine innodb status\G查看,可以看到这里锁的行数是1。所以也就是说,S锁是不在这个行级锁统计的。
2)、schema.INNODB_TRX表查看
参数意义:
trx_id:事务id,可以看到其与上面事务那个ID是对应的
trx_state:当前事务的状态
trx_started:事务开始的时间
trx_request_lock_id:等待事务的锁ID。如果trx_state的状态是LOCK_WART,则该值代表当前事务等待之前事务占用锁资源的ID。我们以最开始B会话超时那条语句来看下
trx_wait_started:事务等待开始的时间
trx_weigth:事务的权重,反映一个事务修改和锁住的行数。在InnoDB存储引擎中,当发生死锁需要回滚时,InnoDB存储引擎会选择该值最小的进行回滚。
trx_mysql_thread_id:Mysql中的线程ID,SHOW PROCESSLIST显示结果
trx_query:事务有限的sql
3)、information_schema.INNODB_LOCKS查看锁状态
参数说明:
lock_id:锁的ID
lock_trx_id:事务ID
lock_mode:锁的模式
lock_type:锁的类型,锁的类型,表锁还是行锁
lock_table:要加锁的表
lock_index:锁的索引
lock_space:InnoDB存储引擎表空间的ID号
lock_page:被锁住的页的数量。表锁的话,该值为NULL
lock_rec:被锁住行的数量,表锁为NULL
lock_data:被锁住行的主键值,表锁为NULL
4)information_schema.INNODB_LOCK_WAITS表查看锁冲突信息
参数说明:
requesting_trx_id:申请获取锁资源的事务ID
requesting_lock_id:申请获取锁资源的ID
blocking_trx_id:阻塞的事务ID(现在资源的占有者)
blocking_trx_id:阻塞的锁ID
二、一致性的非锁定读操作
1、介绍基本内容
一致性的非锁定行读(consistent nonlocking read)是指InnoDB存储引擎通过行多版本控制(multi versioning)的方式来读取当前执行时间数据库行的数据。例如会话A在读取的时候,这个时候有其它的会话B等在进行delete、update操作,A并不会等B这些释放锁,而是直接去读取快照数据。
2、多版本并发控制
上面就是非锁定读,其会大大提高数据读取的并发性。在InnoDB的默认设置情况下,这是默认的读取方式,即读取并不会占用和等待表上的锁。但在不同的事务隔离级别下,读取的方式并不相同,并不是每个事务隔离级别下读取的都是一致性读。同样,即使都是一致性读,但第一快照数据的定义也不相同。
快照数据就是当前行数据之前的历史版本,可能有多个版本,我们称这种技术为行多版本技术。这种并发控制,就是多版本并发控制(Multi Version Concurrency Controller,MVCC)。
在Read Committed与Repeatable Read(InnoDB默认隔离级别)下,InnoDB使用非锁定的一致性读,但这两种对于快照数据的定义不同。
Read Committed:对于快照数据,非一致性读总是读取被锁定行的最新一份的快照数据。由于是最新的一份,所以也就可能出现不可重复读。
Repeatable Read:其对于快照数据的读取,总是读取事务开始时的行数据版本。这样就解决了不可重复读的问题。不过又会出现幻读,因为上面是行级别的控制,幻读是表级别的。所以要解决这个问题就需要串行处理。
在这里上面有举一个SQL案例来说明读已提交、可重复读,这个网上现在已经有很多例子了,这里就不再写了。上面梳理的行的多版本并发控制就解释了这两种级别的具体操作。
三、锁的算法
1、基本内容
InnoDB存储引擎有3种行锁的算法设计:
Record Lock:单个行记录上的锁
Gap Lock:间隙锁,锁定一个范围,当不包含记录本身
Next-key Lock:Gap lock+Record Lock,即行锁+间隙锁。其锁定一个范围,并且锁定记录本身。
1)、Record Lock总是去锁住索引记录。如果InnoDB表建立的时候并没有设置任何索引,其就会使用InnoDB默认的隐式主键来锁定。
2)、Next-key Lock是结合Gap Lock和Record Lock的一种锁定算法,在Next-key Lock算法下,InnoDB对行的查询都是使用这种锁定算法。对应不同的SQL查询语句,可能设置共享的Next-key Lock或排他的Next-key Lock。
2、案例说明
通过上面这两个会话A、B,我们可以看到已经验证了Next-key Lock的锁住范围(id = 5)、锁住当前行(id = 6)。这里在id = 5 | 6的时候语句是阻塞的,我直接切断了。
同时还需要注意的是,上面是范围id < 6,如果是直接id = 6,则其就会使用间隙锁而是直接行锁Record Lock。
四、一些名词介绍(锁问题)
1、脏数据
脏数据是指当前事务读到了其他事务还没有提交的数据,由于数据A还没有提交,其实这个数据A并不是确认的,可能发生回滚,也就并没有生效,这个会发生在READ UNCOMMITTED都未提交事务隔离级别。
2、脏页
脏页是指缓冲池中已经被修改的页,但整个时候还并有刷新写到磁盘(写磁盘是异步的,也就是说目前这个数据只是存在在内存中),所以这个时候磁盘中的页数据与缓存中的页数据并不一致。脏页并没有问题。
3、不可重复读
就是前面介绍到在多版本并发控制其会读取最新版本的行数据。例如事务A先开启一个事务查询到一行数据,然后处理其他的内容(还并没有提交事务),这个时候事务B只有一条修改一样行的语句就直接提交了。这个时候由于读取最新版本的原因,当事务A再次读取的时候就与第一次不同了。这里书上也有一个例子说明,也可以去搜索其他博文具体了解。
4、死锁
1)、基本介绍
如果程序是串行的,则并不会发生死锁。死锁只发送于并发的情况。
2)、demo
这里的两个会话,顺序是交叉的,先是A会话id = 5 for update ,再B会话id = 6 for update。然后在A会话id = 6 for update的时候会阻塞(图片这里并没有体现出来),然后在B会话id = 5 for update 的时候就会给出Deadlock,然后A会话就查询出来了。
上面就是演示两个会话分别给对方需要的资源上锁了,然后两个都不能获取到想要的资源就发生了死锁了。同时通过上面的可以判断,发生死锁,Mysql默认会释放掉锁。
作者:Feverasa
链接:https://juejin.cn/post/6922812298298032141
来源:掘金
网友评论