深入理解Mysql事务隔离级别与锁机制问题

 更新时间:2021年09月28日 10:47:15   作者:daidavid_csdn  
MySQL默认的事务隔离级别是可重复读,用Spring开发程序时,如果不设置隔离级别默认用MySQL设置的隔离级别,如果Spring设置了就用已设置的隔离级别,本文重点给大家介绍Mysql事务隔离级别与锁机制的相关知识,一起看看吧

概述

数据库一般都会并发执行多个事务,多个事务可能会并发的对相同的一批数据进行增删改查操作,可能导致脏读、脏写、不可重复度和幻读。这些问题的本质都是数据库的多事务并发问题,为了解决事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制,用一整套机制来解决多事务并发问题

事务及其ACID属性

原子性:操作的不可分割;

一致性:数据的一致性;

隔离性:事务之间互不干扰;

持久性:数据的修改时永久的;

并发事务处理带来的问题

脏写:丢失更新,最后的更新覆盖了由其他事务所做的更新;

脏读:事务A读取到了事务B已经修改但未提交的数据;

不可重复读:事务内部相同的查询在不同时刻结果不一样,针对的是数据的更新、删除操作;

幻读:事务A读取到了其后开始的事务B提交的新增数据;针对的是数据的插入;

事务隔离级别

隔离级别 脏读 不可重复读 幻读
读未提交
读已提交 ×
可重复读 × ×
可串行化 × × ×

 READ-UNCONMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE

查看当前数据库的事务隔离级别:

show variables like 'tx_isolation'

设置事务隔离级别:

set tx_isolation='REPEATABLE-READ';

MySQL默认的事务隔离级别是可重复读,用Spring开发程序时,如果不设置隔离级别默认用MySQL设置的隔离级别,如果Spring设置了就用已设置的隔离级别;

锁详解

锁是计算机协调多个进程或线程并发访问某一资源的机制。

锁分类

从性能上分为:乐观锁(用版本对比来实现)和悲观锁;

从对数据库操作类型分:读写和写锁(悲观锁);

        读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响;

        写锁(排它锁,X锁(exclusive)):当前写操作没有完成前,阻断其他写锁和读锁;

从对数据库操作的粒度分:表锁和行锁

        表锁:每次操作锁住整张表,开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。

# 手动增加表锁
lock table 表名称 read(write),表名称2 read(write);
# 查看表上加过的锁
show open tables;
# 删除表锁
unlock tables;

        行锁:每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。

InnoDB与MYISAM的最大不同点:1、InnoDB支持事务;2、InnoDB支持行级锁。

总结:

MyISAM在执行查询语句前,会自动给涉及的表加读锁;执行update、insert、delete操作加写锁;

InnoDB在执行查询语句前(非串行隔离级别),不会加锁;执行update、insert、delete操作会加行锁。

读锁会阻塞写,但不会阻塞读。而写锁会把读写都阻塞。

行锁与事务隔离级别案例分析

 mysql准备一张表

1、脏读,事务A读取到另外一个事务已修改但未提交的数据,此种情形简单,不具体阐述。对应的事务隔离级别:read uncommitted(读未提交)。

2、不可重复读,对应的事务隔离级别:read committed(读已提交)

事务A:

set session transaction isolation level read committed;
 
start transaction;
 
select * from t_user;

事务B:

set session transaction isolation level read committed;
 
start transaction;
 
-- insert into t_user values (1,'张',8);
update t_user set age = 9 where id = 1;
 
commit;

事务A第一次执行到查询语句,结果如下:

此时,事务B执行完毕,事务A还未结束,继续执行一次查询,结果如下:

 

 产生了不可重复读的问题,一个事务内前后两次查询的数据结果不一致,读取到了其他事务已经提交的数据。

3、可重复读,设置事务隔离级别为repeatable read(可重复读);

事务A第一次执行结果如下:

 事务B执行,修改操作,update  age=8并提交,结果对比如下

 左边为事务A,查询结果与开始时一样,解决了不可重复读的问题;直接查询,此时age=8;

可重复读的隔离级别下使用了MVCC(multi-version concurrency control)机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。

4、幻读,在3中,新增一条数据,如下

 此时事务A再次查询,结果如下:

 结果依然和开始的一样,此种场景,可重复读隔离级别有效的防止了不可重复读和幻读的问题;

如果,事务A,在第一次查询后,执行不加条件的update,这个update会作用在所有的行上面,包括事务B新增加的数据。此时,再执行查询,结果如下:

 出现了幻读,Mysql官方给出的幻读解释是:只要在一个事务中,第二次select多出了row计算幻读。

5、串行化,serializable,InnoDB的查询也会被加上行锁。如果查询的是一个范围,那么该范围内的所有记录行包括每行记录所在的间隙区间范围都会被加锁,即使该行数据还没有被插入。

间隙锁(Gap Lock)

session_1执行update t_user set name ='哈哈' where id>8 and id<18;则其他session无法在这个范围包含的所有行记录以及行记录所在的间隙里插入或修改任何数据

间隙锁在可重复读隔离级别下才会生效

临键锁(Next-key Locks)

Next-key Locks是行锁与间隙锁的组合。在间隙锁(8,18)这个范围,实际会找到存在的值,比如id距离这个区间最近的是,3,20;则实际在(3,20]这个范围都处在行锁范围内。

无索引行锁会升级为表锁

锁主要是加在索引上,如果对非索引字段更新,行锁可能会变表锁。

InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁

锁定某一行还可以用lock in share mode(共享锁)和for update(排它锁)

结论:

Innodb存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会更高,但是在整体并发处理能力方面要远远优于MYISAM的表级锁定。

但是,Innodb的行级锁定同样有其脆弱的一面,如使用不当,可能会让整体的性能更差。

行锁分析

通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况

show status like 'innodb_row_lock%';

比较重要的主要有:

Innodb_row_lock_time_avg(等待平均时长)

Innodb_row_lock_waits(等待总次数)

Innodb_row_lock_time(等待总时长)

当等待次数很高,且每次等待时长也不小的时候,就需要分析系统中为什么会有如此多的等待,根据分析结果制定优化计划。

死锁

set session transaction isolation level repeatable read;
 
start transaction;
 
select * from t_user where id = 2 for update;
select * from t_user where id = 1 for update;

事务A先锁定id=1,再锁定id=2;事务B顺序相反,出现死锁,结果如下:

 大多数情况Mysql可以自动检测死锁并回滚产生死锁的那个事务,但有些情况无招。

查看近期死锁日志信息:

show engine innodb status\G;

锁优化建议:

1、尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁;

2、合理设计索引,尽量缩小锁的范围;

3、尽可能减少索引条件范围,避免间隙锁;

4、尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行;

5、尽可能低级别事务隔离

到此这篇关于深入理解Mysql事务隔离级别与锁机制的文章就介绍到这了,更多相关Mysql事务隔离级别与锁机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mysql中自定义函数的创建和执行方式

    Mysql中自定义函数的创建和执行方式

    这篇文章主要介绍了Mysql中自定义函数的创建和执行方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Mysql索引选择以及优化详解

    Mysql索引选择以及优化详解

    这篇文章主要给大家介绍了关于Mysql索引选择以及优化的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 浅谈MySQL使用笛卡尔积原理进行多表查询

    浅谈MySQL使用笛卡尔积原理进行多表查询

    这篇文章主要介绍了浅谈MySQL使用笛卡尔积原理进行多表查询, 文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • MySQL重复数据提取最新一条技术方法详解

    MySQL重复数据提取最新一条技术方法详解

    在MySQL数据库中清除重复数据是一项常见的任务,下面这篇文章主要给大家介绍了关于MySQL重复数据提取最新一条的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • 利用explain排查分析慢sql的实战案例

    利用explain排查分析慢sql的实战案例

    在日常工作中,我们会有时会开慢查询去记录一些执行时间比较久的SQL语句,下面这篇文章主要给大家介绍了关于利用explan排查分析慢sql的相关资料,需要的朋友可以参考下
    2022-04-04
  • mysql修改表结构方法实例详解

    mysql修改表结构方法实例详解

    这篇文章主要介绍了mysql修改表结构方法,以实例形式较为详细的分析了mysql修改表结构的具体方法与相关注意事项,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-10-10
  • MySQL系列之八 MySQL服务器变量

    MySQL系列之八 MySQL服务器变量

    其中有些参数支持运行时修改,会立即生效;有些参数不支持,且只能通过修改配置文件,并重启服务器程序生效;有些参数作用域是全局的,且不可改变;有些可以为每个用户提供单独(会话)的设置
    2021-07-07
  • mysql中的临时表如何使用

    mysql中的临时表如何使用

    这篇文章主要介绍了mysql中的临时表如何使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • MySQL实现批量插入测试数据的方式总结

    MySQL实现批量插入测试数据的方式总结

    在开发过程中经常需要一些测试数据, 这个时候如果手敲的话, 十行二十行还好, 多了就很死亡了, 接下来介绍两种常用的MySQL测试数据批量生成方式,希望对大家有所帮助
    2023-05-05
  • MySQL数据库存储引擎介绍及数据库的操作详解

    MySQL数据库存储引擎介绍及数据库的操作详解

    mysql面试中最常问的问题之一:小伙子,你说一下你们公司用的存储引擎,以及你知道有哪些存储引擎和他们之间的区别? 所以下面这篇文章主要给大家介绍了关于Mysql存储引擎的相关资料,需要的朋友可以参考下
    2022-08-08

最新评论