最近系统上线了,这两天收到反馈,操作按钮动不了了、删除按钮动不了了等等类似的问题,仔细查看日志错误,提示:Lock wait timeout exceeded; try restarting transaction。于是乎,就想把数据库事务与锁的知识深入了解一下,也为自己解决问题提供思路。本系列文章主要源于Kevin老师的《一次性彻底搞定数据库事务》,感谢老师的讲解。在本篇文章中,我们先来说说事务。
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列读或写操作。其目的是提供三种方法:失败恢复到正常状态的方法、保持一致性的方法、并发操作之间保持隔离的方法,通俗的说是那一系列操作要么完全地执行,要么完全地不执行。
A:原子性(Actomicity),该系列操作作为一个整体,不能被分割,要么全执行,要么都不执行;
C:一致性(Consistency),确保数据从一种状态变为另一种状态;
I:隔离性(Isolation),多个事务并发执行时,彼此之间互不影响;
D:持久性(Durability),当事务提交成功后,对数据的修改要永久保存在数据库中。
回滚丢失:由于一个事务的失败回滚导致另一个事务的更新丢失了;
覆盖丢失:更新被其他事务给覆盖了;
脏读:一个事务读取了另一个事务修改未提交的数据;
不可重复读:一个事务对同一行数据执行了两次或更多次查询,但是却得到了不同的结果;
幻读:与不可重复读相似,不过针对的是数据的数量,当一个事务查询记录两次或多次时,得到的数量不一致。
读未提交:一个事务的更新语句没有提交,但是别的事务可以读到这个改变;
读已提交:一个事务只能看到其他事务已经提交的更新、看不到未提交的更新,消除了脏读和回滚丢失,Oracle/Sqlserver就是此类;
可重复读:一个事务中进行两次或多次同样的对于数据内容的查询,得到的结果是一样的,但不保证对于数据条数的查询是一样的,只要存在读改行数据就禁止写,消除了不可重复读和覆盖丢失,如Mysql,需要注意的是虽然Mysql的隔离级别为可重复读,理论上是不能解决幻读异常的,但由于Next-key,Gap-lock的存在,使得Mysql可以避免幻读问题;
串行化:事务执行不允许其他事务操作,只要存在读就禁止写,但可以同时读,消除了幻读,但是效率太低。
隔离级别\并发异常 | 回滚丢失 | 脏读 | 不可重复读 | 覆盖丢失 | 幻读 |
读未提交 | 否 | 否 | 否 | 否 | 否 |
读已提交 | 是 | 是 | 否 | 否 | 否 |
可重复读 | 是 | 是 | 是 | 是 | 否 |
串行化 | 是 | 是 | 是 | 是 | 是 |
这里以Mysql为例进行说明,工具使用的是Navicat。
操作之前,我们先来了解几个指令:
查看数据库版本:select version();
查看隔离级别:select @@session.tx_isolation;
修改隔离级别:set @@session.tx_isolation=参数;
可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE
开启事务:start transaction;
提交:commit;
回滚:rollback;
我们可以通过使用指令设置Mysql的隔离级别,来验证上述表格中内容是否正确,不过需要说明的是,由于Mysql的锁等内容,可能会导致有些情况无法验证或验证有差异。这里,我设置Mysql的隔离级别为读已提交,验证脏读异常是否存在。
操作步骤:
1)新建一个数据库,命名为代表db1,建一张表,命名为tbook;
CREATE TABLE `tbook` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
2)右键数据库db1——命令列界面,这里我打开两个,模拟两个事务;
3)因Mysql默认的隔离级别为可重复读,因此我们需要先设置隔离级别为读已提交,在两个命令列界面中分别设置,同时开启事务:
set @@session.tx_isolation='READ-COMMITTED';
start transaction;
4)模拟脏读异常场景,在tbook表中插入一些数据。看下图效果显示,表明当隔离级别为读已提交,脏读异常不存在:
事务1 | 事务2 |
查询数据得到初始值1 | 查询数据得到初始值1 |
更新数据为2 | 查询数据值仍为1 |
commit | commit |