Mysql中的Innodb事务和锁详解

 更新时间:2023年09月11日 11:01:07   作者:feiyingHiei  
这篇文章主要介绍了Mysql中的Innodb事务和锁详解,InnoDB是MySQL的一种存储引擎,它是一种可靠性和性能非常好的存储引擎,它具有ACID事务支持,可以提供高并发性和数据完整性,需要的朋友可以参考下

Innodb事务和锁

一. 事务隔离级别

  1. READ UNCOMMITED :读未提交,事务之间可以看到彼此之间正在修改的内容,会出现所谓的脏读现象
  2. READ COMMITED : 读已提交,只能读取已经提交的事务修改的数据,不会出现脏读的现象,但会出现不可重复读和幻读的情况
  3. REPEATABLE READ : 可重复度,在innodb中解决了不可重复读和幻读的问题
  4. SERIALIZED : 串行化, 这个使用的比较少,隔离性最强,性能也最差

二. 锁类型

  • S LOCK : 共享锁 : S锁与S锁相互兼容,与X锁不兼容
  • X LOCK :排他锁 :与其他所有锁类型不兼容,简单的可以理解为读写锁即可
  • 意向锁 (IS, IX) : 主要是用来表达加锁的一种层次概念,比如希望在行级别加X锁,那么就需要在表级和页级别加上IX锁,意向锁整体上兼容性会比较好,如果当前表中已经有了多个行级锁,如果希望尝试加表锁,如果没有意向锁的话,那么就需要取检测当前行锁的加锁情况,但是有了意向锁,就可以快速判断当前尝试加表锁可能是需要等待的。

监控方法

  • SHOW ENGINE INNODB STATUS 命令查看innodb引擎信息,其中包含当前执行事务的状态和锁的状态
  • 通过information_schema提供的表来排查可能存在的问题
    • a. information_schema.innodb_trx
    • b. information_schema.innodb_locks
    • c. innodb_lock_waits

三.锁释放的时机

lock通常在事务commit或是rollback之后才会释放

四. 一致性非锁定读

这是非常常见的问题,事务中会有大量的select操作,select操作通常情况下是不会对数据做加锁操作的, 这样保证了数据的并发性能,在不同的事务隔离级别下,非一致性锁定读的行为是不太一样的

  • READ COMMITED : 读取undo_log中该行最新的一条快照数据
  • READ REPETABLE : 读取undo_log中,最近的早于当前事务开启时间的快照数据

五. 加锁算法

上文描述了锁的类型,即排它锁,共享锁,意向锁之类,这里所说的是加锁的算法,即为innodb使用了什么样的加锁策略来处理实际场景中的并发问题。

  • record lock : 行锁,对单个行记录进行加锁,行是innodb的最小数据管理单位,innodb中有专门的数据结构来存储和管理行记录,我们所有的查询的数据最终都会落到存储引擎的行上
  • gap lock : 间隙锁,即对间隙加锁,间隙其实就这里只是针对间隙,不包括行(todo ,间隙的概念需要搞一下)
  • next-key lock : 对行和间隙和行都进行加锁

netx-key lock算法主要解决的问题就是幻读问题,因此这个算法只在 RR事务隔离级别中会使用

疑问? 不是MVCC可以在无锁的情况下解决幻读的问题吗,为什么还要上next-key lock这么重的算法呢?这里就要引出两个概念了,一致性非锁定读(快照读) 和一致性锁定读(当前读) 的概念了

一致性非锁定读 : 我们平时使用的select * from table 这种查询语句,都是快照读,在innodb RC和RR级别下都是通过MVCC机制来实现的,过程中是不加锁的,读取的数据未必是当前行中的真实的数据。

一致性锁定读 : select * from table for update 或者select * from table in shared mode 在这种情况下RC基本是会对对应的行加锁,RR级别下就会使用next-key算法加锁。

六、redo日志

redo日志是物理日志,记录的页的物理修改日志

七、undo日志

undo日志是逻辑日志,记录的是操作的sql逻辑,通常我们在修改数据的时候,先对行加锁,然后将该版本的数据拷贝到undo日志中,然后修改当前行的操作事务id,修改该行事务,然后把回滚指针指向und日志。

七、MVCC

多版本控制(MVCC)在许多关系型数据库中都有实现,innodb中主要是依赖undo日志来实现,其中涉及到的一个比较重要的概念就是read veiw

  • low_limit_id :最大活跃事务id
  • up_limit_id : 最小活跃事务id
  • trx_ids : 活跃事务集合
  • 如果当前trx_id > low_limit_id,那数据一定是不可见的,因为数据是在当前事务开启后才修改的
  • 如果当前trx_id < up_limit_id,那么数据一定是可见的,因为事务在当前事务开启前就已经提交了
  • 如果trx_id 在这之间,那么如果trx_id在trx_ids中,说明事务还没有提交,那么数据就不可见, 否则事务就是可见的。

无论是在什么事务隔离级别下,基于read view的快照读机制都相同的,只是创建read view的机制不太一样。

在RR基本下,read view在事务开启的时候就创建完成了,二在RC级别下,每次查询都会重新创建read view。

如果一个当前事务为事务A, 另外一个事务是事务B, 如果事务A开启的时候,事务B已经提交了,那么无论在RR级别或是RC级别下,B提交的数据对于事务A都是可见的; 如果B事务在A事务开启之前就已经开启了,但是A启动的时候B事务还没有提交,那么先让无论RR级别还是RC级别,数据在快照读模式下也都是不可见的;如果B事务在A开启之前就开启,但是在A事务提交前B事务就提交了,此时对于RR级别而言,B的trx_id在trx_ids之中(trx_ids是在开启事务的时候就已经初始化了),那么RR级别下数据是可见的,但对于RC而言,重新生成的read view了,显然数据就是可见的。

通过上述的分析,通过mvcc可以在RR级别下快照读是完全解决了重复读和幻读的问题,RC级别还是会有不可重复读和幻读的问题,但是需要注意的是,MVCC只工作在快照读(一致性非锁定读)条件下,对于当前读的问题是无法走到MVCC的,所以一定要注意,MVCC只解决了快照读的幻读和不可重复读问题。

这里需要补充一个case来理解一下快照读与当前读的巨大区别

CREATE TABLE `user_info` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL DEFAULT '',
  `age` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
)
事务隔离级别为RR

初始化数据

insert into user_info ('name', 'age') values ('jack', 1);

T1时刻A开启事务,然后启用快照读

select * from user_info where id = 1;

查询到的结果是

+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | tom  |   0 |
+----+------+-----+

T2时刻我们开启事务B, 并且执行

update user_info set age =1 where id = 1;

此时事务A中执行当前读

select * from user_info where id = 1 for update;

事务A将会被阻塞 此时提交事务B,事务A中显示的查询结果为

+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | tom  |   1 |
+----+------+-----+

然后我们再次执行一次快照读

select * from user_info where id = 1;

获得的结果为

+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | tom  |   0 |
+----+------+-----+

通过上述的例子,可以看到,快照读和当前读有着非常大的区别,在同一个事务当中,快照读和当前读返回的结果有可能是不一样的。

我们看到,mvcc保证了快照读每次读取的数据是一致,next-key算法保证了当前读是一致。

到此这篇关于Mysql中的Innodb事务和锁详解的文章就介绍到这了,更多相关Innodb事务和锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL长连接短连接面试精讲

    MySQL长连接短连接面试精讲

    这篇文章主要为大家介绍了MySQL长连接短连接面试精讲,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • MySQL中CONCAT()函数拼接出现NULL的问题解决

    MySQL中CONCAT()函数拼接出现NULL的问题解决

    本文主要介绍了MySQL中CONCAT()函数拼接出现NULL的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • MySQL慢查询优化解决问题

    MySQL慢查询优化解决问题

    这篇文章主要介绍了MySQL慢查询优化解决问题,MySQL的慢查询,全名是慢查询日志,是MySQL提供的一种日志记录,用来记录在MySQL中响应时间超过阀值的语句,下文详细介绍慢查询的调优情况,需要的小伙伴可以参考一下
    2022-03-03
  • MySQL 可以用localhost 连接,但不能用IP连接的问题解决方法

    MySQL 可以用localhost 连接,但不能用IP连接的问题解决方法

    这篇文章主要介绍了MySQL 可以用localhost 连接,但不能用IP连接的问题解决方法的相关资料,这里提供了解决方案,需要的朋友可以参考下
    2016-12-12
  • mysql批量插入insert语句的两种方法

    mysql批量插入insert语句的两种方法

    在MySQL中批量插入数据有几种方法,本文主要介绍了mysql批量插入insert语句的两种方法,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11
  • 详解远程连接Mysql数据库的问题(ERROR 2003 (HY000))

    详解远程连接Mysql数据库的问题(ERROR 2003 (HY000))

    本篇文章是对远程连接Mysql数据库的问题进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • MySQL插入中文不乱码的5种方法

    MySQL插入中文不乱码的5种方法

    这篇文章主要介绍了MySQL 插入中文不乱码的5种方法,需要的朋友可以参考下
    2016-06-06
  • MySql中流程控制函数/统计函数/分组查询用法解析

    MySql中流程控制函数/统计函数/分组查询用法解析

    这篇文章主要介绍了MySql中流程控制函数/统计函数/分组查询用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Can't connect to MySQL server on localhost (10061)解决方法

    Can't connect to MySQL server 

    今天在安装mysql后使用mysql命令连接mysql时报错 Can't connect to MySQL server on localhost (10061),一般是因为mysql服务没有启动或者用户名密码不正确
    2011-03-03
  • MySQL出现莫名其妙的断开连接以及解决方案

    MySQL出现莫名其妙的断开连接以及解决方案

    这篇文章主要介绍了MySQL出现莫名其妙的断开连接以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11

最新评论