【读书笔记】MySQL存储引擎_第五天

一、什么是锁

  • 用于管理对共享资源的并发访问
  • 操作缓冲池中的LRU列表,删除、添加、移动LRU列表中的元素,为保证一致性,必须有锁的介入
  • InnoDB存储引擎锁的实现和Oracle数据库非常类似,提供一致性的非锁定读、行级锁支持

二、lock和latch

latch要求锁定时间必须非常短,如果持续时间过长,则应用的性能非常差。latch又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测机制

lock对象是事务,该锁一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放时间可能不同)

lock和latch的比较

-locklatch
对象事务线程
保护数据库内容内存数据内容
持续时间整个事务过程临界资源
模式行锁、表锁、意向锁读写锁 、互斥量
死锁通过waits-for graphtime out等机制进行死锁监测与处理无死锁监测与处理机制。仅通过应用程序加锁的顺序保证无死锁的情况发生
存在于Lock Manager的哈希表中每个数据结构的对象中

三、InnoDB存储引擎中的锁

3.1 锁的类型

  • 共享锁:(S Lock),允许事务读一行数据
  • 排他锁:(X Lock),允许事务删除或更新一行数据
  • 意向共享锁:(IS Lock),事务想要获得一张表中某几行的共享锁
  • 意向排他锁:(IX Lock),事务想要获得一张表中某几行的排他锁

意向锁的目的是为了在一个事务中揭示下一行将被请求的锁的类型。

表:InnoDB存储引擎中锁的兼容性

-ISIXSX
IS兼容兼容兼容不兼容
IX兼容兼容不兼容不兼容
S兼容不兼容兼容不兼容
X不兼容不兼容不兼容不兼容

可以通过指令SELECT * FROM information_schema.INNODB_TRX\G;进行查看

INNODB_TRX的结构说明

字段名说明
trx_idInnoDB存储引擎内部唯一的事务ID
trx_state当前事务的状态
trx_strted事务的开启时间
trx_requested_lock_id等待事务的锁ID。如trx_state的状态为LOCK WAIT,那么表示当前的事务等待之前事务占用锁资源的ID。若trx_state不是LOCK WAIT,则该值为NULL
trx_wait_started事务等待开始的时间
trx_weight事务的权重,反映了一个事物修改和锁住的行数,在InnoDB存储引擎中,当发生死锁需要回滚时,InnoDB存储引擎会选择该值最小的进行回滚
trx_mysql_thread_idMySQL中的线程ID,SHOW PROCESSLIST显示的结果
trx_query事务运行的SQL语句

但是该表只能显示当前运行的InnoDB事务,并不能直接判断锁的一些情况。如果要查看锁的一些情况需要指令SELECT * FROM information_schema.INNODB_LOCKS\G;

表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_LOCKS_WAITS来判断

通过指令SELECT * FROM information_schema.INNODB_LOCK_WAITS\G;进行查看

表INNODB_LOCK_WAITS的结构

字段说明
requesting_trx_id申请锁资源的事务ID
requesting_lock_id申请的锁的ID
blocking_trx_id阻塞的事务ID
blocking_lock_id阻塞的锁的ID

3.2 一致性非锁定读

InnoDB存储引擎通过多版本控制(mutil versioning)的方式来读取当前执行时间数据库中的行的数据。
简单概括来说就是:事务A修改id= 1的这条数据(delete或者update),此时事务B去读取这条id=1的记录,此时事务B读取的是事务A修改之前的记录。

实现方式:通过undo段中记录的快照(snapshot)来实现。事务开启之前,将需要操作的行的记录通过快照的形式保存在undo log中,然后再对当前行记录进行修改操作,此时如果另一个事务进来进行读取的操作时,只会去读取undo log中保存的快照,也就是之前的记录。因此不会存在并发安全问题。且因为没有加锁,所以并发操作效率更高。

  • 再事务隔离级别READ COMMITTED和REPEATABLE READ(InnoDB存储引擎的默认事务隔离级别)下,InnoDB使用非锁定的一致性读
  • 具体实现方式可以看这篇文章

实例执行的过程:

时间会话A会话 B
1BEGIN
2SELECT * FROM parent WHERE id = 1;结果为id=1
3BEGIN
4UPDATE parent SET id = 3 WHERE id = 1;
5SELECT * FROM parent WHERE id = 1;结果为id=1
6COMMIT
7SELECT * FROM parent WHERE id = 1;,结果为Empty set(0.00 sec)
8COMMIT

3.3 一致性锁定读

虽然InnoDB存储引擎的SELECT操作使用一致性非锁定读,但是某些情况下,用户需要显式地对数据库读取操作进行加锁保证数据逻辑的一致性。而这个要求数据库支持加锁语句,即使是对于SELECT的只读操作。InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读操作:

  • SELECT ... FOR UPDATE
  • SELECT ... LOCK IN SHARE MODE

3.4 自增长与锁

在Innodb存储引擎的内存结构中,每个含有自增长值的表都有一个自增长计数器。插入操作会依据这个自增长的计数器值加1赋予自增长列。这个实现方式称作AUTO-INC Locking。这种锁采用一种特殊的表锁机制,为了提高性能,锁不是在一个事务完成后才释放,而是在完成对自增值插入SQL语句之后立即释放

对于有自增长值的列的并发插入性能较差,事务必须等待前一个插入完成(虽然不用等待事务的完成),其次对于INSERT --SELECT 的大数据的插入会影响插入性能,因为另一个事务中的插入会阻塞

四、锁

4.1 行锁的3种算法

InnoDB存储引擎有3种行锁的算法,其分别是:

  • Record Lock:单个记录上的锁
  • Gap Lock:间隙锁,锁定一个范围,但不包括记录本身
  • Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身

4.2 锁的问题

  • 幻读:同一个事务下,连续两次相同的查询语句得到的结果不同
  • 脏读:读取到未提交的数据
  • 不可重复读:跟幻读很像,结果都是两次查询到的结果不同。区别在于不可重复读的操作是数据修改,幻读的操作是由于新增和删除。

脏读是读取到了未提交的数据,不可重复读读取的是已提交的数据,但是他还是违反了数据库事务一致性的要求。

4.3 死锁

  • 概念:死锁是两个或两个以上的事务在执行过程中,因抢夺锁资源而造成的一种相互等待的现象。

  • 解决办法就是:

    1. 只要有等待出现就回滚
    2. 但方法一容易造成并发性能的下降,因此在这里面加入了“超时”,等待超过某个时间阈值才回滚。
    3. 简单超时造成根据FIFO的机制来选择回滚对象,如果某些对象的事务占比很高,占用了很多的undo log,此时让他回滚着实牺牲有点大。因此,当前数据库普遍采用了“等待图
  • 等待图:
    图中有两种信息:锁的信息链表、事务等待链表。如果这两个信息构成的图出现了回路,那就存在死锁。

4.4 锁升级

锁升级指的是将当前锁的粒度降低:行锁变页锁,页锁变表锁
锁升级是为了保护系统资源,防止系统用过多的内存来维护锁。

参考

《MySQL技术内幕——InnoDB存储引擎》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

走出半生仍是少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值