前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >innodb锁机制探究(一)

innodb锁机制探究(一)

作者头像
AsiaYe
发布2019-11-06 17:15:05
3330
发布2019-11-06 17:15:05
举报
文章被收录于专栏:DBA随笔DBA随笔
//

innodb锁机制探究(一)

//

关于innodb里面的锁

在之前的文章中,我们说过三次关于锁的文章,分别是去年的12月25号、12月26号以及今年的1月14号的文章,这3篇文章里面简单介绍过一些锁的知识点,后续我们将详细介绍MySQL里面的锁,但是不能保证每天更新,所以把题目做成一个系列标签,如果有时间我做下归类整理。

innodb中的锁当中有两个概念需要搞清楚,一个是latch,一个是lock,我们一般说的锁就是lock。这两个概念的区别是什么呢?

latch一般是轻量级的锁,它的锁定时间特别短,否则应用的性能特别差,在innodb存储引擎中,latch可以分为mutex互斥量和rwlock读写锁,它的目的是保证并发线程操作临界资源的正确性。并且通常没有死锁检测的机制。关于它的状态,可以使用show engine innodb mutex来查看。

lock的对象是事务,它通常用来锁定页、行、表等等,并且lock住的对象一般在commit事务或者rollback事务之后提交。lock是有死锁机制的。

关于latch,我们这里不再多说,后面有时间单独拿出一篇文章来讲述。

锁问题实战

我们简单演示一下锁的生成吧,如下代码所示,我们首先创建一个表,然后插入数据,接着使用select * for update给这个表加上排他锁,也就是X锁,如下:

SESSION 1

mysql:yeyztest 12:16:19>>create table test (id int primary key auto_increment,age int);
Query OK, 0 rows affected (0.02 sec)
mysql:yeyztest 12:20:49>>select * from test;
+----+------+
| id | age  |
+----+------+
|  1 |    1 |
|  2 |    2 |
|  5 |    5 |
|  8 |    8 |
+----+------+
4 rows in set (0.00 sec)

mysql:yeyztest 13:19:55>>begin;
Query OK, 0 rows affected (0.00 sec)

mysql:yeyztest 13:21:37>>select * from test where id=1 for update;
+----+------+
| id | age  |
+----+------+
|  1 |    1 |
+----+------+
1 row in set (0.00 sec)

然后我们在另外一个回话中使用下面的命令再次请求这行记录的锁:

SESSION 2:

mysql:yeyztest 13:22:48>>select * from test where id =1 lock in share mode;

可以看到,返回的结果迟迟没有出现,没有得到对应的记录,此时我们查看数据字典information_schema中关于锁和事务的表,结果如下:

mysql:yeyztest 13:23:47>>select * from information_schema.innodb_locks\G
*************************** 1. row ***************************
    lock_id: 422062388676960:164:3:2
lock_trx_id: 422062388676960
  lock_mode: S
  lock_type: RECORD
 lock_table: `yeyztest`.`test`
 lock_index: PRIMARY
 lock_space: 164
  lock_page: 3
   lock_rec: 2
  lock_data: 1
*************************** 2. row ***************************
    lock_id: 1288486:164:3:2
lock_trx_id: 1288486
  lock_mode: X
  lock_type: RECORD
 lock_table: `yeyztest`.`test`
 lock_index: PRIMARY
 lock_space: 164
  lock_page: 3
   lock_rec: 2
  lock_data: 1
2 rows in set, 1 warning (0.00 sec)

可以看到,有两个锁的信息,我们解释下各个字段的意思:

lock_id:锁的id
lock_trx_id:事务id
lock_mode:锁的模式,S还是X
lock_type:锁的类型,表锁还是行锁
lock_table:要加锁的表
lock_index:锁住的索引
lock_space:锁对象的space id
lock_page:锁住的页数量,若是表锁,这个值是null
lock_rec:锁住的行的数量,若是表锁,这个值是null
lock_data:事务锁定记录的主键值,若是表锁,这个值是null

需要注意的是,这个lock_data的值不是可信的,如果我们锁定了一个范围内的记录,这个lock_data的值会变成这个范围内的第一条记录的id值。

再来看下lock_wait表里面的内容:

mysql:yeyztest 13:24:56>>select * from information_schema.innodb_lock_waits\G
*************************** 1. row ***************************
requesting_trx_id: 422062388676960
requested_lock_id: 422062388676960:164:3:2
  blocking_trx_id: 1288486
 blocking_lock_id: 1288486:164:3:2
1 row in set, 1 warning (0.00 sec)

可以看到,这里写的是当前请求锁的事务id和锁id,以及阻塞的事务id和锁id。

一段时间过后,session 2上面的锁没有拿到,发生了锁超时的错误,如下:

SESSION 2

mysql:yeyztest 13:24:39>>select * from test where id =1 lock in share mode;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

再回过头来看session 1中的所等待信息,可以看到,所等待的信息已经不存在了:

mysql--dba_admin@127.0.0.:yeyztest 13:24:56>>select * from information_schema.innodb_lock_waits\G
*************************** 1. row ***************************
requesting_trx_id: 422062388676960
requested_lock_id: 422062388676960:164:3:2
  blocking_trx_id: 1288486
 blocking_lock_id: 1288486:164:3:2
1 row in set, 1 warning (0.00 sec)

mysql:yeyztest 13:25:13>>select * from information_schema.innodb_lock_waits\G
Empty set, 1 warning (0.00 sec)

一致性非锁定读

一致性非锁定读是指innodb通过行记录的多版本控制来读取当前执行时间数据库中行的数据,如果读取的行正在执行delete或者update操作,则会读取一个该行记录的一个快照数据。

它的实现实际上是通过undo日志来实现的,undo日志使用在事务回滚时候的一些日志,关于啥是个undo日志的具体细节,如果大家不了解,后续会单独拿一篇文章去写,现在只需要知道有这么个东西即可。

通过上面的描述,我们可以发现,一致性非锁定读提高了数据库的并发性,因为读操作不用等待锁的释放。然而这并不能解决所有的问题,因为读取到的快照数据是当前行记录之前的历史版本,每行记录可能有多个历史版本,也就是说你现在看到这个id是4,可能之前是3,再往前是2,我们到底使用哪个版本的数据给客户看,这就牵扯到多版本控制方法,也就是常说的MVCC。

一致性锁定读

上面说到了 一致性非锁定,下面我们看看一致性锁定读,非锁定读我们知道读取的是数据的快照,可能不是数据当前的真是内容,而锁定读读取的就是当前数据的真是内容。它需要在读取的时候给记录进行上锁。看到这里,可能你已经熟悉了,这不就是我们使用的:

select * for update(X锁定)

select * lock in share mode(S锁定)

这两种方法终于有名字了,就叫做一致性锁定读。这里需要注意的是,这两种方法必须存在于一个事务当中,才能获得锁,否则是不行的,我专门做了测试:

SESSION 1
mysql:yeyztest 13:19:29>>select * from test where id =1 for update;
+----+------+
| id | age  |
+----+------+
|  1 |    1 |
+----+------+
1 row in set (20.11 sec)

SESSION 2
mysql:yeyztest 13:21:37>>select * from test where id=1 for update;
+----+------+
| id | age  |
+----+------+
|  1 |    1 |
+----+------+
1 row in set (0.00 sec)

可以看到,第一个会话不加begin等关键字使用上述语句的时候,第二个会话中还是可以读到这行记录的值,而不收到阻塞。

所以,一致性锁定读,必须写在事务里面才管用,否则这样的语句不会获得这一行记录的锁。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DBA随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档