MySQL性能优化学习——(三) 数据库事务与锁

一、什么是数据库的事务?

1.1 事务的定义

维基百科的定义:

事务是数据库管理系统(DBMS)执行过程中的一个逻辑单位,
由一个有限的数据库操作序列构成。

这里面有两个关键点,第一个,它是数据库最小的工作单元,是不可以再分的。
第二个,它可能包含了一个或者一系列的 DML 语句,包括 insert delete update。
(单条 DDL(create drop)和 DCL(grant revoke)也会有事务)
 

1.2 事务的典型场景

在项目里面,什么地方会开启事务,或者配置了事务?

无论是在方法上加注解,还是配置切面。

<tx:advice id="txAdvice" transaction-manager="transactionManager">

<tx:attributes>
<tx:method name="save*" rollback-for="Throwable" />
<tx:method name="add*" rollback-for="Throwable" />
<tx:method name="send*" rollback-for="Throwable" />
<tx:method name="insert*" rollback-for="Throwable" />
</tx:attributes>
</tx:advice>
 
比如下单,会操作订单表,资金表,物流表等等,这个时候我们需要让这些操作都在一个事务里面完成。
当一个业务流程涉及多个表的操作的时候,我们希望它们要么是全部成功的,要么都不成功,这个时候我们会启用事务。

1.3 哪些存储引擎支持事务?

InnoDB 支持事务,这个也是它成为默认的存储引擎的一个重要原因:
https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html
另一个是 NDB。

1.4 事务的四大特性

事务的四大特性:ACID

1)原子性(Atomicity)

指我们对数据库一系列的操作,要么都是成功,要么都是失败,不可能有部分成功或部分失败的情况。
例如转账,一个账户余额减少,对应一个账户增加,这两个账户一定是同时成功或者同时失败的。
如果前面一个操作已经成功了,后面的操作失败了,怎么让它全部失败呢?这个时候我们必须要回滚。
原子性,在InnoDB中是通过undo log来实现的,它记录数据修改前的值,一旦异常,就通过undo log来实现回滚操作。

2)一致性(consistent)

一致性指的是数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。
比如主键必须是唯一的,字段长度符合要求。
 
除了数据库自身的完整性约束,还有一个是用户自定义的完整性:
例如转账,A 账户余额减少 1000,B 账户余额只增加了 500,
这个时候因为两个操作都成功了,按照我们对原子性的定义,它是满足原子性的,
但是它没有满足一致性,因为它导致了会计科目的不平衡。

用户自定义的完整性通常要在代码中控制。

3)隔离性(Isolation

多个并发事物同时操作同一张表或同一行数据,必然会引发一些并发和干扰的问题。
隔离性就是指多个事物对表或行的并发操作,应该是透明的互不干扰的。
通过这种规则,也是为了保证数据的一致性。

4)持久性(Durable

我们对数据库的任意的操作,增删改,只要事务提交成功,那么结果就是永久性的。
不可能因为我们系统宕机或者重启了数据库的服务器,它又恢复到原来的状态了。
 
持久性以及数据库崩溃恢复(crash-safe)是通过什么实现的?
持久性是通过redo log 和 double write 来实现的,操作数据时,
会先写到内存的buffer pool中,同时记录redo log,如果在刷入磁盘前出现异常,
在重启后就可以读取redolog中的内容,写入磁盘,保证数据的持久性。
但是恢复成功的前提是数据页本身没有被破坏,是完整的,这个通过双写缓冲(double write)保证。
 
原子性、隔离性、持久性最终目的都是为了实现一致性。
 

1.5 数据库事物的开启

无论是在 Navicat 的这种工具里面去操作,还是在我们的 Java 代码里面通过API 去操作,
还是加上@Transactional 的注解或者 AOP 配置,其实最终都是发送一个指令到数据库去执行,
Java 的 JDBC 只不过是把这些命令封装起来了。
 
操作环境为版本(5.7),存储引擎(InnnoDB),事务隔离级别(RR)。
select version();
show variables like '%engine%';
show global variables like "tx_isolation";
 
执行这样一条更新语句的时候,它有事务吗?
update user set name = 'admin2' where id=2;
 
实际上,它自动开启了一个事物,并且提交了,所以最终写入了磁盘。
 
这个是开启事务的第一种方式,自动开启和自动提交。
InnoDB 里有一个 autocommit 的参数(分成两个级别, session 级别和 global级别)。
show variables like 'autocommit';
 
它的默认值是 ON。autocommit 代表是否自动提交。如果它的值是 true/on 的话,
在操作数据的时候,会自动开启一个事务,和自动提交事务。
否则,如果把 autocommit 设置成 false/off,那么数据库的事务就需要手动地开启和手动地结束。
手动开启事务也有几种方式,一种是用 begin;一种是用 start transaction。
 
怎么结束一个事务呢?我们结束也有两种方式,第一种就是提交一个事务, commit;
还有一种就是 rollback,回滚的时候,事务也会结束。
还有一种情况,客户端的连接断开的时候,事务也会结束。

 

1.6 事物并发会带来什么问题?

假如没有隔离性,并发事物会产生什么问题呢?

1)脏读


如图,有A,B两个事物,A事物读取一条记录name值是Jack,B事物修改这条数据,把name修改成Rose,没有提交

此时A事物又进行了一次查询,查到这条记录中name变成了Rose。

这种在一个事物中,前后两次读到的数据不一致,读到了其他事物没有提交的数据的情况,就被称为脏读。

 

2)不可重复读

A,B两个事务,A事务读取到的name是Jack,B事务修改后进行了提交,A事务再次读取到的name是Rose。

由于A事务再次读取时,读到了其他事务提交后的数据,读取到了两个不一致的数据。name到底时Jack还是Rose呢?

这种一个事务读取到了其他事务已提交的数据导致前后两次读取数据不一致的情况,被称为不可重复读。
 

3)幻读

在A事物中进行条件查询,满足条件的数据只有1条,在B事物中插入一条数据,并且提交

在A事物再次查询时发现多了一条数据。

一个事务前后两次读取数据数据不一致,是由于其他事务插入数据造成的,这种情况我们把它叫做幻读。
 
不可重复读和幻读的区别是什么?
不可重复读是修改或者删除,幻读是插入。
注意:只有insert操作导致的数据不一致才是幻读,修改或删除则是不可重复读。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章