美文网首页数据库mysql代码改变世界mysql
mysql原理(九) 锁,你更新一条记录真的不会造成死锁吗?

mysql原理(九) 锁,你更新一条记录真的不会造成死锁吗?

作者: 我犟不过你 | 来源:发表于2021-08-19 14:02 被阅读0次

    锁是数据库区别于文件系统的一个关键特性。锁机制用于管理共享资源的并发访问。InnoDB除了在表上面进行上锁之外,在其他层面也会进行上锁,如操作缓冲池当中的LRU列表,删除、添加和移动都需要有锁的介入。本文只讨论在InnoDB中的锁。

    一、InnoDB存储引擎中的锁

    1.1 锁的类型

    1)共享锁(s lock):允许多个事务读一行数据。
    2)排它锁(x lock):允许一个事务修改或删除数据。

    锁的兼容锁的不兼容

    X S
    X 不兼容 不兼容
    S 不兼容 兼容

    例:
    T1获取了行r的共享锁,T2仍然可以获取r的共享锁,这称为锁的兼容
    如果此时T3想要获取r的排他锁,则需要等到T1和T2释放r的共享锁之后,这称为锁的不兼容

    InnoBD允许行级锁和表级锁同时存在,为了支持在不同粒度上执行加锁操作,支持了额外的锁方式,称之为意向锁(Intention Lock)。InnoDB设计的意向锁比较简单,因为其支持行级锁,所以意向锁仅设计在表锁上。其主要目的是为了揭示下一行将要被请求的锁类型。
    1)意向共享锁(IS Lock)想要获得一张表中某几行的共享锁。
    2)意向排它锁(IX Lock)想要获得一张表中某几行的排它锁。

    意向锁不会阻塞除全表扫描以外的请求。

    在information_schema当中有以下三张表可以让我们分析锁的情况:INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS

    INNODB_TRX
    首先查看INNODB_TRX中的主要段含义:

    字段名 说明
    trx_id 唯一事务id
    trx_state 事务状态
    trx_started 事务开始时间
    trx_requested_lock_id 等待事务的锁id,如trx_wait为lock_wait的时候;如果不是trx_wait,则是null
    trx_wait_started 事务等待开始时间
    trx_weight 权重,该值反映修改和锁住的行数。当发生死锁需要回滚时,会回滚该值最小的记录
    trx_mysql_thread_id mysql中的线程id
    trx_query 事务运行的sql语句
    例子

    INNODB_LOCKS
    首先查看INNODB_LOCKS中的主要段含义:
    |字段名|说明|
    |lock_id|锁的id|
    |lock_trx_id|事务的id|
    |lock_mode|锁模式|
    |lock_type|锁的类型,表锁和行锁|
    |lock_table|要加锁的表|
    |lock_index|锁住的索引|
    |lock_space|锁对象的space id|
    |lock_page|事务锁定页的数量。表锁该值为null|
    |lock_rec|事务锁定记录的数量,表锁该值为null|
    |lock_data|事务锁定记录的主键值,表锁该值为null|

    例子

    INNODB_LOCK_WAITS
    首先查看INNODB_LOCK_WAITS中的主要段含义:

    字段名 说明
    requesting_trx_id 申请锁资源的事务id
    requested_lock_id 申请锁的id
    blocking_trx_id 阻塞的事务id
    blocking_lock_id 阻塞锁的id
    例子

    1.2 一致性非锁定读

    一致性非锁定读是指InnoDB通过多版本并发控制MVCC(Multi-Version Concurrency Control)来读取当前执行时间数据库中行的数据。

    如果一行或多行数据正在进行update或者delete操作,这时候另外的事务去读取这些数据并不会等待其释放锁,可以直接进行读取。读取的是其快照数据。

    之所以称之为一致性非锁定读,因为不需要等到行上面的X锁进行释放即可进行读取。如下图所示:

    一致性非锁定读 (1).png

    快照数据是指行之前版本的数据,通过undo log实现。

    如上图所示,一个行的版本可能有多个,一般称之为多版本技术,由此带来的并发控制称之为多版本并发控制(MVCC)

    在事务隔离级别read commited(oracle默认)和repeatable read(innoDB的默认隔离级别)中,使用非锁定一致性读,然而其对于快照读的定义却不相同。

    隔离级别 读取版本
    rc 读取最新版本数据
    rr 读取最开始版本数据

    下面看个例子:

    时间 会话1 会话2
    1 begin
    2 select * from student where id = 1
    3 begin
    4 update student set id =3 where id = 1
    5 select * from student where id = 1
    6 commit
    7 select * from student where id = 1
    8 commit

    如上表格所示:
    在rr级别下,会话1读取到的结果分别是:1,1,1
    在rc级别下,会话1读取到的结果分别是:1,1,empty

    1.3 一致性锁定读

    在某些特定情况下,用户需要显示的对数据库读取进行加锁操作以保证数据的一致性。InnoDB对select支持两种一致性锁定读:

    1. select * from table where id = X for update 对数据加X锁,其他事物不能对该行数据加任何锁。
    2. select * from table where id = X lock in share model 对数据加S锁,其他事务可以对其加S锁,但是加X锁会被阻塞。

    以上加锁操作必须保证在一个事务当中,一旦事务提交后,锁即被释放掉。所以使用时不需使用begin、start transaction、set autocommit = 0。

    1.4 自增长和锁

    自增长是一种常见的使用方式,也是数据主键的首选。InnoDB对于每一个含有自增长属性的表都维护一个自增长计数器。会依据该计数器的值自动加1赋予自增长列。这种方式称作Auto Inc Locking,这是一种特殊的表锁机制,而这种方式存在一定的性能问题。

    在mysql 5.1.22开始,提供了一种轻量级互斥量的自增长方式,这种机制大大提高了自增长值插入的性能。并且提供一个innodb_autoinc_lock_mode来控制自增长,默认为1。另外还有0和2两种方式。

    0:并发性能不好,在5.1.22版本前使用。
    1:默认值。
    2:性能最高,并发插入会带来一定的问题,导致不连续。

    二、锁的算法

    2.1 三种行锁的算法:

    1)Record Lock:单条记录锁。
    2)Gap Lock:区间锁或间隙锁,锁定一个范围但不包含本身。
    3)Next-key Lock:临键,Record Lock + Gap Lock,锁定范围区间的同时并且锁定记录本身。

    如下索引值有1,5,9,11,name在三种锁的不同区间如下所示:

    锁的区间

    Next-key Lock算法是结合了record lock 和gap lock,其出现的原因是为了解决幻读(snapshot read),利用这种技术,锁定的不再是单个值,而是一个范围。

    如果查询的索引含有唯一索引时,Next-key Lock将会降级为Record lock,仅锁住索引本身。

    如果查询的索引是辅助索引,使用Next-key Lock进行加锁。按照上图给出下面一个sql,key是辅助索引,本例子不写唯一索引了:

    select * from table where key = 5 for update;添加了X锁
    

    那么会对5这个值增加(1,5]的前区间,还会使用gap lock对其增加一个后区间(5,9),因此,运行一下sql都会被阻塞:

    select * from table where key = 6 lock in share mode;无法对X锁添加S锁
    insert into table select 2;2在锁定区间,被阻塞
    insert into table select 7;7在锁定区间,被阻塞
    

    Gap Lock的作用是为了解决幻读问题,只在rr级别存在,如果想要关闭可以修改默认隔离界别为rc,但是这违反了隔离性。

    在默认的隔离级别repeatable read下,Innodb使用next-key lock来解决幻读问题。幻读问题是指在同一事务的两次查询中,可能得到不同的结果,即第二次可能查询到之前不存在的行。

    相关文章

      网友评论

        本文标题:mysql原理(九) 锁,你更新一条记录真的不会造成死锁吗?

        本文链接:https://www.haomeiwen.com/subject/rhygbltx.html