GVKun编程网logo

你知道MySQL锁与事物隔离级别吗?(mysql事务隔离级别和锁)

20

这篇文章主要围绕你知道MySQL锁与事物隔离级别吗?和mysql事务隔离级别和锁展开,旨在为您提供一份详细的参考资料。我们将全面介绍你知道MySQL锁与事物隔离级别吗?的优缺点,解答mysql事务隔离

这篇文章主要围绕你知道MySQL锁与事物隔离级别吗?mysql事务隔离级别和锁展开,旨在为您提供一份详细的参考资料。我们将全面介绍你知道MySQL锁与事物隔离级别吗?的优缺点,解答mysql事务隔离级别和锁的相关问题,同时也会为您带来JDBC事物隔离级别、MySQL - InnoDB 锁与事务(三)隔离级别与表的关系、MySQL 之查看默认的事物隔离级别、MySql 事物及隔离级别的实用方法。

本文目录一览:

你知道MySQL锁与事物隔离级别吗?(mysql事务隔离级别和锁)

你知道MySQL锁与事物隔离级别吗?(mysql事务隔离级别和锁)

相关免费学习推荐:mysql数据库(视频)

前言

  • MysqL索引底层数据结构与算法
  • MysqL性能优化原理-前篇
  • MysqL性能优化-实践篇1
  • MysqL性能优化-实践篇2

前面我们讲了MysqL数据库底层的数据结构与算法、MysqL性能优化篇一些内容。我们再来聊聊MysqL的锁与事务隔离级别,分上下两篇,本篇重点讲MysqL的行锁与事务隔离级别。

锁定义

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

在数据库中,除了传统的计算资源(如cpu、RAM、I/O等)的争用以外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

锁分类

  • 从性能上分为乐观锁(用版本对比来实现)和 悲观锁
  • 从数据库操作类型分为:读锁写锁 (都属于悲观锁)
    • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响;
    • 写锁(排它锁):当前写操作没有完成之前,它会阻断其它写锁和读锁。
  • 从数据库操作的粒度分为:表锁行锁

对于锁深入的理解,可以查看《关于Java中锁的理解》。

MysqL的锁

  • 行锁(Record Locks)

  • 间隙锁(Gap Locks)

  • 临键锁(Next-key Locks)

  • 共享锁/排他锁(Shared and Exclusive Locks)

  • 意向共享锁/意向排他锁(Intention Shared and Exclusive Locks)

  • 插入意向锁(Insert Intention Locks)

  • 自增锁(Auto-inc Locks)

  • 预测锁,这种锁主要用于存储了空间数据的空间索引。

下篇来分别聊聊,本篇重点是行锁以及事务隔离级别。

表锁

每次操作锁住整张表。

  • 开销小,加锁快;
  • 不会出现死锁;
  • 锁粒度大,发生锁冲突的概率最高;
  • 并发度最低。

基本操作

示例表,如下:

# 建表sqlCREATE TABLE mylock (    id INT(11) NOT NULL AUTO_INCREMENT,    NAME VARCHAR(20) DEFAULT NULL,
    PRIMARY KEY(id)
) ENGINE = MyISAM DEFAULT CHARSET = utf8;

# 插入数据INSERT INTO`test`.`mylock`(`id`,`NAME`) VALUES ('1','a'); 
INSERT INTO`test`.`mylock`(`id`,`NAME`) VALUES ('2','b'); 
INSERT INTO`test`.`mylock`(`id`,`NAME`) VALUES ('3','c'); 
INSERT INTO`test`.`mylock`(`id`,`NAME`) VALUES ('4','d');复制代码
  • 手动增加表锁
lock table 表名称 read(write), 表名称2 read(write);复制代码
  • 查看表上加过的锁
show open tables;复制代码
  • 删除表锁
unlock tables;复制代码

案例分析 — 加读锁

LOCK TABLE mylock read;复制代码

image.png

当前 session 和其他 seesion 都可以读该表;

当前 session 中插入或者更新锁定表都会报错,其他 session 插入或者更新则会等待。

image.png

案例分析 — 加写锁

LOCK TABLE mylock WRITE;复制代码

image.png

当前 session 对该表的增删改查都没有问题,其他 session 对该表的所有操作都会被阻塞 。

案例结论

MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁;在执行增删改查操作前,会自动给涉及的表加写锁。

  • 对 MyISAM 表的读操作(加读锁),不会阻塞其他进程同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其他进程的写操作。
  • 对 MyISAM 表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其他进程的读写操作。

总结:读锁会阻塞写,但不会阻塞读;而写锁则会把读和写都阻塞

行锁

每次操作锁住一行数据。

  • 开销大,加锁慢;
  • 会出现死锁;
  • 锁定粒度最小,发生锁冲突的概率最低;
  • 并发度最高。

InnoDB 和 MyISAM 的最大不同点:

  • 支持事务(TRANSACTION)
  • 支持行级锁

行锁支持事务

事务(Transaction)及其 ACID 属性

事务是由一组 sql 语句组成的逻辑处理单元,事务具有以下四个属性,通常简称为事务的 ACID属性

  • 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全部执行,要么全部不执行。
  • 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B+树索引或双向链表)也都必须是正确的。
  • 隔离性(Lsolation):数据库系统提供一定的隔离机制,保障事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
  • 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能保持。

并发事务处理带来的问题

  • 更新丢失(Lost Update)

当两个或多个事务选择同一行,然后基于最初选定的值更新该行值,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题,最后的更新覆盖来其他事务所做的更新。

  • 脏读(Dirty Reads)

一个事务正在对一条记录做修改,在这个事务完成并提交前,这个条记录的数据就处于不一致的状态;这时另外一个事务也来读取同一条记录,如果不加控制,第二个事务读取来这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫做“脏读”。

总结:事务A读取到来事务B已经修改但尚未提交的数据,还在这个数据基础上做来操作。此时,如果事务B回滚,事务A读取的数据无效,不符合一致性要求。

  • 不可重复读(Non-Repeatable Reads)

一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生来改变、或某些记录已经被删除了,这种现象就叫做“不可重复读”。

总结:事务A读取到了事务B已经提交的修改数据,不符合隔离性。

  • 幻读(Phantom Reads)

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。

总结:事务A读取到了事务B提交的新增数据,不符合隔离性。

事务隔离级别

“脏读”、“不可重复读”、“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。

同时,不同应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读” 并不敏感,可能更关系数据并发访问的能力。

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

show variables like 'tx_isolation';复制代码

image.png

设置事务隔离级别

set tx_isolation='REPEATABLE-READ';复制代码

数据库版本是5.7,隔离级别是Repeatable-Read(可重复读),不同的数据库版本和隔离级别对语句的执行结果影响很大。所以需要说明版本和隔离级别

行锁与隔离级别案例分析

事务控制语句

  • BEGINSTART TRANSACTION;显式地开启一个事务;
  • COMMIT;也可以使用 COMMIT WORK,不过二者是等价的。COMMIT会提交事务,并使已对数据库进行的所有修改称为永久性的;
  • ROLLBACK;有可以使用 ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
  • SAVEPOINT identifier;SAVEPOINT允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT;
  • RELEASE SAVEPOINT identifier;删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
  • ROLLBACK TO identifier;把事务回滚到标记点;
  • SET TRANSACTION;用来设置事务的隔离级别。InnoDB存储引擎提供事务的隔离级别有READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLE

事务处理方法

MysqL 事务处理主要有两种方法:

  1. BEGIN, ROLLBACK, COMMIT来实现
    • BEGIN 开始一个事务
    • ROLLBACK 事务回滚
    • COMMIT 事务确认
  1. 直接用 SET 来改变 MysqL 的自动提交模式:
    • SET AUTOCOMMIT=0 禁止自动提交
    • SET AUTOCOMMIT=1`` 开启自动提交

示例表,如下:

CREATE TABLE `user` (    `id` INT (11) NOT NULL AUTO_INCREMENT,    `name` VARCHAR (255) DEFAULT NULL,    `balance` INT (11) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;INSERT INTO `test`.`user` (`name`,`balance`) VALUES ('zhangsan','450');INSERT INTO `test`.`user` (`name`,`balance`) VALUES ('lisi', '16000');INSERT INTO `test`.`user` (`name`,`balance`) VALUES ('wangwu','2400');复制代码

行锁演示

一个 session 开启事务更新不提交,另一个 seesion 更新同一条记录会阻塞,更新不同记录u会阻塞。

image.png

image.png

读未提交

(1)打开一个客户端A,并设置当前事务模式为 read uncommitted (读未提交),查询表 user 的初始化值

set tx_isolation='read-uncommitted';复制代码

image.png

(2)在客户端A的事务提交之前,打开另一个客户端B,更新表 user

image.png

(3)这时,虽然客户端B的事务还没提交,但是在客户端A就可以查询到B已经更新的数据

image.png

(4)一旦客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那么客户端A查询到的数据其实就是脏数据

image.png

(5)在客户端A执行更新语句 update user set balance = balance - 50 where id = 1; zhangsan 的 balance没有变成350,居然是400,是不是很奇怪,数据不一致啊。如果你这么想就太天真了,在应用程序中,我们会用400-50=350,并不知道其他会话回滚了,要想解决这个问题可以采用读已提交的隔离级别。

image.png

读已提交

(1)打开一个客户端A,并设置当前事务模式为 read committed (读已提交),查询表 user 的所有记录

set tx_isolation='read-committed';复制代码

image.png

(2)在客户端A的事务提交之前,打开另一个客户端B,更新表 user

image.png

(3)这时,客户端B的事务还没提交,客户端A不能查询到B已经更新的数据,解决了脏读问题。

@H_301_521@

(4)客户端B的事务提交

image.png

(5)客户端A执行与上一步相同的查询,结果与上一步不一致,即产生了不可重复读的问题。

image.png

可重复读

(1)打开一个客户端A,并设置当前的事务模式为 repeatable read ,查询表 user 的所有记录。

set tx_isolation='repeatable-read';复制代码

image.png

(2)在客户端A的事务提交之前,打开另一个客户端B,更新表 user 并提交。

image.png

(3)在客户端A查询表 user 的所有记录,与步骤(1)查询结果一直,没有出现不可重复读的问题。

image.png

(4)在客户端A,接着执行 update user set balance = balance - 50 where id = 1 , balance 没有变成 400 - 50 = 350, zhangsan 的 balance 的值用的是步骤(2) 中的 350 来计算的,所以是300,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了 MVCC(multi-version concurrency control)机制,select 操作不会更新版本号,是快照读(历史版本);insert、update、delete 会更新版本号,是当前读(当前版本)。

我们下篇来讲 MVCC。

image.png

(5)重新打开客户端B,插入一条新数据后提交。

image.png

(6)在客户端A查询表user 的所有记录,没有查出新增数据,所以没有出现幻读。

image.png

(7)验证幻读 在客户端A执行 update user set balance = 8888 where id = 4; ,能更新成功,再次查询到客户端B新增的数据。

串行化

(1)打开一个客户端A,并设置当前事务模式为 serializable ,查询表 user 的初始值

set tx_isolation='serializable';复制代码

image.png

(2)打开一个客户端B,并设置当前事务模式为 serializable ,插入一条记录报错,表被锁了插入失败,MysqL 中事务隔离级别为 serializable 时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。

image.png

案例结论

InnoDB 存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会更高一下,但是在整体并发处理能力方面要远远优于 MyISAM 的表级锁定的。当系统并发量最高的时候,InnoDB 的整体性能和 MyISAM 相比就会有比较明显的优势。

但是,InnoDB 的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让 InnoDB 的整体性能表现不仅不能比 MyISAM 高,甚至可能会更差。

行锁分析

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

show status like 'innodb_row_lock%';复制代码

image.png

对各个状态量的说明如下:

  • Innodb_row_lock_current_waits :当前正在等待锁定的数量
  • Innodb_row_lock_time :从系统启动到现在锁定总时间长度
  • Innodb_row_lock_time_avg :每次等待所花平均时间
  • Innodb_row_lock_time_max :从系统启动到现在等待最长的一次所花时间
  • Innodb_row_lock_waits :系统启动后到现在总共等待的次数

对于这5个状态变量,比较重要的主要是:

  • Innodb_row_lock_time_avg (等待平均时长)
  • Innodb_row_lock_waits (等待总次数)
  • Innodb_row_lock_time(等待总时长)

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

死锁

set tx_isolation='repeatable-read';复制代码
Session_1执行:select * from user where id=1 for update;
Session_2执行:select * from user where id=2 for update;
Session_1执行:select * from user where id=2 for update;
Session_2执行:select * from user where id=1 for update;复制代码

查看近期死锁日志信息:

show engine innodb status\G;复制代码

大多数情况MysqL可以自动检测死锁并回滚产生死锁的那个事务,但是有些情况 MysqL没法自动检测死锁

优化建议

  1. 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁;
  2. 合理设计索引,尽量缩小锁的范围;
  3. 尽可能减少检索条件范围,避免间隙锁;
  4. 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行;
  5. 尽可能低级别事务隔离。

问答

  1. MysqL 默认级别是 repeatable-read,有什么办法可以解决幻读妈?

间隙锁(Gap Lock)在某些情况下可以解决幻读问题,它是 Innodb 在 可重复读 提交下为解决幻读问题时引入的锁机制。要避免幻读可以用间隙锁在Session_1 下面执行 update user set name = 'hjh' where id > 10 and id <= 20; ,则其他 Session 没法在这个范围锁包含的间隙里插入或修改任何数据。

如:user 表有3条数据, id > 2 and id <=3 会把第三条记录锁住,其他会话对则无法对第三条记录做操作。

image.png

image.png

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

客户端A执行: update user set balance = 800 where name = 'zhangsan';

image.png

客户端B对该表任一行执行修改、删除操作都会阻塞

image.png

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

  1. 锁定某一行还可以用 local in share mode(共享锁)for update(排它锁) ,例如: select * from test_innodb_lock where a = 2 for update; 这样其他 session 只能读这行数据,修改则会被阻塞,直到锁定行的 session 提交。

JDBC事物隔离级别

JDBC事物隔离级别

 

事物隔离级别用户指定事务中对数据的操作对其他事物的“可见性”。不同的事物隔离级别能够解决并发访问数据带来的不同的并发问题,而且会直接影响并发效率。数据并发可能会出现一下几种问题:

脏读:这种情况发生在事务中允许读取未提交的数据。例如,A事物修改了一条数据,但是未提交修改,此时A事物对数据的修改其他事物是可见的,B事物中能够读取A事物未提交的修改。一旦A事物回滚,B事物中读取就是不正确的数据。
不可重复读:这种情况发生在如下场景:
1)A事物中读取一行数据。
2)B事务中修改了改行数据。
3)A事物中再次读取该行数据将得到不同的结果。
幻读:种种情况发生在如下场景:
1)A事物通过WHERE条件读取若干行。
2)B事务中插入了符合条件的若干条数据。
3)A事务中通过相同的条件再次读取数据时将会读取到B事物中插入的数据。
JDBC遵顼SQL:2003规范,定义了4种事物隔离级别,另外增加了一种TRANSACTION_NONE,表示不支持事物,这几种事物隔离级别如下。
TRANSCATION_NONE:表示驱动不支持事物,这意味着他是不兼容JDBC规范的驱动程序。
TRANSCATION_READ_UNCOMMITTED:允许读取未提交更改的数据,这意味着可能出现脏读、不可重复读、幻读等现象。
TRANSCATION_READ_COMMITTED:表示在事务中进行的任何数据更改,在提交之前对其他认识是不可见的。这样可以防止脏读,但是不能解决不可重复读和幻读问题。
TRANSCATION_REPEATABLE_READ:该事物隔离级别能够解决脏读和不可重复读问题,但是不能解决幻读问题。
TRANSCATION_SERIALIZABLE:该事务隔离级别下,所有事物串行执行,能够有效解决脏读,不可重复读和幻读问题,但是并发效率低。
Connection对象的默认事物级别由JDBC驱动程序指定。通常它是底层数据源支持的默认事物隔离级别。Connection接口中提供了一个setTransactionIoslation()方法,允许JDBC客户端设置Connection对象的事物隔离级别,新设置的事物隔离级别会在之后的会话中生效。在一个事物中调用setTransactionIsolation()方法是否对当前事物有效取决于具体的驱动实现。JDBC规范建议在调用setTransactionIsolation()方法后,下一个新的事物开始生效。另外,JDBC驱动可能不完全支持除TRANSACTION_NONE之外的4个事物级别。
调用Connection对象的setTransactionIsolation()方法时,如果参数的驱动不支持是事物隔离级别,则驱动程序应该使用更高级别代替该参数级别,如果驱动不支持更高级别,就会抛出SQLExecption异常,可以调用DatabaseMetaData对象supportsTransactionIsolationLevel()方法判断是否支持某一事物隔离级别。
示例图:

 

 
事物隔离级别

MySQL - InnoDB 锁与事务(三)隔离级别与表的关系

MySQL - InnoDB 锁与事务(三)隔离级别与表的关系

    事务的隔离级别是用来调节数据库的并发性和数据的可靠性之间的平衡的工具。MySQL支持所有四种标准的隔离级别,Repeatable Read,Read Commited,Read Uncommited, Serializable。他默认的隔离级别是Reapeatable Read。下面来说一下不同的隔离级别运作的原理,与锁在其中产生的作用。

 

  • Repeatable Read 可重复读

意思是同一事务中,多次对同一记录读取时,所读取到的值是一样的。这里涉及到一个InnoDB的Multi-Version的概念,正是它,实现了在同一事务中的一致性读取。同一事务多次读取的时候,都是读取的同一版本的数据快照,所以有可能在事务进行中,其他事务已经对记录做出来了变更,但原事务还是读取到旧版本的记录。

 

  1. Multi-Version

InnoDB是一个使用了多版本技术的数据库,当一条记录被修改或删除时,在MySQL Tablespace 中一个叫回滚段(Rollback Segment)的数据结构中的重做日志(Redo log Segment)中,会存储该记录恢复到之前的样子的信息。存储这个信息的作用有两个, 一个是回滚的时候恢复数据,另外一个作用,就是为了能够在同一事务中实现一致读 (Consistent Read)。

 

MySQL的Repeatable Read隔离级别,同时也部分解决了幻读的问题。为什么说是部分呢?因为对于普通的Select ,同一事务中查询两次,并不保证记录数一致,因为不会加间隙锁(Gap Lock)或临键锁(Next-key Lock)来防止别的事务插入记录。

 

然而对于加锁的读( select ... lock in share mode 或者 select ... for update) 或者update和delete,MySQL会根据查询条件是否为范围查询,来加间隙锁(Gap Lock)或临键锁(Next-key Lock),以防止幻读的发生。也就是说,在事务结束前,其他事务锁住的区间都不能进行插入操作。

 

  • Read Committed 提交读

意思是在同一事务中,两次读取到的都是已提交的最新版本的数据,所以两次读取的值可能会有不同。它只解决了脏读的问题,不会读取到未提交的数据。

 

对于加锁的读( select ... lock in share mode 或者 select ... for update) 或者update和delete,MySQL只会对满足查询条件的记录加锁,不会在索引之前加临键锁或者间隙锁。所以不会防止其他事务插入数据,会导致幻读的发生。

 

  • Read Uncommitted 未提交读

会读取到未提交的记录,其他的和Read Committed一致。

 

  • SERIALIZABLE 序列化

这个级别和Repeatable Read差不多,但是在Select查询的时候,auto commit关闭的状况下,默认会在select 语句之后加上lock in share mode。也就是说,Select查询之间不会阻塞,但是碰到update,delete等加排他锁的语句时,会阻塞,等待获取锁。

MySQL 之查看默认的事物隔离级别

MySQL 之查看默认的事物隔离级别

OSC 请你来轰趴啦!1028 苏州源创会,一起寻宝 AI 时代

1. 切换到 performance_schema database 下

    List-1 查看当前用的是哪个 database

mysql> select database();
+--------------------+
| database()         |
+--------------------+
| performance_schema |
+--------------------+
1 row in set (0.01 sec)

2. 用如下命令

    List-2 

mysql> select * from global_variables where variable_name like "%tx%";
+---------------+-----------------+
| VARIABLE_NAME | VARIABLE_VALUE  |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
| tx_read_only  | OFF             |
+---------------+-----------------+
2 rows in set (0.01 sec)

    List-2 中可以看出事物隔离级别是 repeatable-read;事物的 read-only 是被关闭的,这个 tx_read_only 在 Spring 的注解上可以通过 @Transactional 来修改。

MySql 事物及隔离级别

MySql 事物及隔离级别

一、事务的基本要素(ACID)

  1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

   2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。

   3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

   4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

二、事务的四种隔离级别

在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。我们的数据库锁,也是为了构建这些隔离级别存在的。
隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能
  • 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
  • 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
  • 可重复读(RepeatedRead):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读。mysql默认级别:可重复读
  • 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

三、事务的并发问题

  1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

  2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

  3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

  小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

四、MVCC在MySQL的InnoDB中的实现

在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。 在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。 在可重读Repeatable reads事务隔离级别下:

  • SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。
  • INSERT时,保存当前事务版本号为行的创建版本号。
  • DELETE时,保存当前事务版本号为行的删除版本号。
  • UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行。

通过MVCC,虽然每行记录都需要额外的存储空间,更多的行检查工作以及一些额外的维护工作,但可以减少锁的使用,大多数读操作都不用加锁,读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,也只锁住必要行。

我们不管从数据库方面的教课书中学到,还是从网络上看到,大都是上文中事务的四种隔离级别这一模块列出的意思。

  • MVCC 总结:为了实现快照读(读写不冲突)

五 MySQL死锁

死锁是只两个或者多个事务在同一个资源上相互占用,并请求锁定对方占用的资源。从而导致循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能产生死锁。多个事务同事锁定同一个资源时,也会产生死锁。例如,两个事务同事处理stockprice表。

事务1:
    start transaction;
    update stockprice set close = 45.50 where stock_id = 4 and date = ''2019-05-05'';
    update stockprice set close = 36.66 where stock_id = 3 and date = ''2019-05-06'';
    commit
事务2:
    start transaction;
    update stockprice set high = 33.33 where stock_id = 3 and date = ''2019-05-06'';
    update stockprice set high = 35.66 where stock_id = 4 and date = ''2019-05-05'';
    commit
    

如果连个都执行了第一条update 语句,跟新了一行数据,同事也锁定了该行数据,接着每个事务都尝试去执行第二条update语句,发现该行已经被对方锁定,然后两个事务都等待对方释放锁,同事又持有对方需要的锁,则陷入死循环。除非有外部因素介入才能解除死锁。

六 EXPLAIN 分析语句

1. select_type:查询类型,
    1. simple:一般是简单查询,不使用union或子查询
    2. subquery:子查询中的第一个select
    3. primary:最外层的select
2. type:mysql找到所需行的方式,又叫“访问类型”,由差到好的顺序:
    1. type=ALL:全表扫描(一般需要进行优化)
    2. type=index:整个索引扫描(不满足最左匹配可能会引起)
    3. type=range:使用一个索引来检索给定范围的行
    4. type=ref:mysql会根据特定的算法快速查找到某个符合条件的索引,而不是会对索引中每一个数据都进行一一的扫描判断
    5. type=eq_ref:类似ref,区别是使用的索引是唯一索引
    6. const、system:常量匹配,主键匹配
3. rows:找到所需记录需要读取的行数(估计值)
4. extra:详细信息,常见:
    1. Using index:只查询索引就能得到结果(覆盖索引)
    2. Using where:需要通过索引再检索实际数据进行过滤
    3. Using temporary:使用临时表
    4. Using filesort:使用临时文件排序
    

七 锁的定义

1.每次总是被什么共享锁、排他锁给绕晕了。简单实际一点。就只有 读锁 写锁 两种锁。对应关系如下:

锁类型 描述
共享锁(读锁 其他事务可以读,但不能写。
排他锁(写锁) 其他事务不能读取,也不能写。

2.乐视锁和悲观锁

  • 乐观锁:是一种解决问题的逻辑。采用版本号的处理方式实现
  • 悲观锁:是借用mysql InnoDB 行锁来实现的(where 条件必须要加索引)。

    select * from user whereid = 10 for update
    

3.行锁和表锁

  • 行锁:顾名思义就是锁定每一行
  • 表锁:顾名思义就是锁定整张表

4.其他mysql 内部实现的锁机制

隐式锁、间隙锁等。

今天关于你知道MySQL锁与事物隔离级别吗?mysql事务隔离级别和锁的分享就到这里,希望大家有所收获,若想了解更多关于JDBC事物隔离级别、MySQL - InnoDB 锁与事务(三)隔离级别与表的关系、MySQL 之查看默认的事物隔离级别、MySql 事物及隔离级别等相关知识,可以在本站进行查询。

本文标签: