一、什么是事务(transaction)
- 事务就是一堆的逻辑语句+逻辑判断
- 一个数据库事务由一条或者多条sql语句构成,它们形成一个逻辑的工作单元。这些sql语句要么全部执行成功,要么全部执行失败
- 事务是保证数据的完整性和一致性的重要手段
- 注意事项、@@autocommit作用
系统变量@@autocommit默认是打开的,这意味着任何1条DML语句是一个事务,会自动提交(Oracle数据库默认是关闭的,要自动提交)。
要将@@autocommit关闭,防止误操作造成数据的丢失损坏
实际使用中,应该使用SET语句来关闭自动提交,否则一个事务不可能由多条SQL语句构成
mysql> SHOW VARIABLES LIKE '%autocommit%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SET @@autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW VARIABLES LIKE '%autocommit%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
1 row in set (0.01 sec)
二、.为什么要使用事务
要将一部分的操作作为一个整体来执行,要么所有语句都成功,要么所有语句都失败
例如银行转账,应该是我主转账给你,然后你收到钱,这才是完整的步骤,如果我转账了,而你没有收到钱,那么所有的执行失败
三、.事务类型
- DML事务:由一条或者多条DML语句构成
如果关闭autocommit参数,需要手工commit或者rollback
如果没有关闭autocommit参数,Mysql会自动执行commit操作
如果显式开启一个事务,就需要手工执行commit和rollback
- DDL事务:总是由一条DDL语句构成(create、drop、alter语句)
执行完了DDL之后之后,Mysql自动执行一个commit操作
- DCL事务:总是由一条DCL语句构成(创建一个用户类似的操作)数据控制事务
执行完了DCL操作时,Mysql自动执行一个commit
保存点是事务的一个事件
四、.commit/rollback
commit:将所有操作永久写入数据库当中
rollback:回滚之前的操作,不写入数据库
- commit和rollback操作前的数据情况
1.所有数据都是在内存中操作,不写入磁盘(不持久化)
2.如果事务未提交或回滚,别的会话不可以看到事务修改的数据(不允许脏读)
3.本事务修改的数据行会被锁定,不允许其他事务进行操作
- commit和rollback操作后的数据情况
1.所有数据都持久化到磁盘
2.所有的会话都可以看到修改后的数据
3.提交之后,锁定的行会释放,其他事物可以对这些行进行操作
五、.其它事务控制语句
SAVEPOINT identifier :建立一个保存点,用来在事务中做一个标记,专门提供给rollback to语句使用
ROLLBACK TO [SAVEPOINT] identifier:回滚到保存点。专门用来撤销事务所做的部分工作:保存点之后所做的工作全部撤销。该语句并不结束事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | b |
| 3 | c |
+----+------+
3 rows in set (0.00 sec)
mysql> savepoint b;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into rep values(4,'d');
Query OK, 1 row affected (0.00 sec)
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | b |
| 3 | c |
| 4 | d |
+----+------+
4 rows in set (0.00 sec)
mysql> rollback to b;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | b |
| 3 | c |
+----+------+
3 rows in set (0.00 sec)
六、事务的四大特性(ACID)
1.原子性:整个事务作为一个整体,要么都成功,要么所有的都失败
解释:例如转账这件事,有我扣钱,你到账着两步骤,总不能我扣了钱,你没有到账这种情况,所以是里面所有环节哪怕一个出错,都需要回滚,就是一切回到之前那样
2.一致性:同一个事务、相同的条件、不同的时间,要得到相同的结果
解释:还是转账来说,假如用户A跟用户B两者之间的钱加起来是100元,那么不管A、B之间如何转账、转几次账,事务结束后两个用户的钱相加起来还是100元,这就是事务的一致性
3.隔离性:可以避免两个事务之间在未提交之前只能看到自己修改的数据,不能看到对方修改的数据
解释:隔离性是当多个用户冰法访问数据库时,比如操作同一张表,数据库为每个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离
例如:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行----------关于事务的隔离性数据库提供了4中隔离级别
4.持久性:持久化到磁盘
解释:持久性是指一个事务一旦被提交了,那么对于数据库中的数据的改变就是永久性的,即使在数据库遇到故障的情况下也不会丢失提交事务的操作
七、当事务执行时,不可避免的会出现以下四个问题:
1.脏读:事务T1可以读取到事务T2未提交的数据
解释:例如转账,我转账给你1000元,但是我还没有提交,但是你查询账号,发现自己多了1000元(在过程中,你查到了没有提交的事务的数据)
解决方法:如果一个事物在读的时候,禁止读取未提交的事务
2.不可重复读:同一个事务,同样的条件,前后可能得到不同的数据---------侧重的是修改(读取了提交的新事务,只更新操作)
解释:不可重复读是指在于数据库中的某个数据,一个事务范围内多次查询却返回不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了
解决方式:可以对特定的行加锁解决
不可重复读和脏读的区别:脏读是读取了另一个事务未提交的数据,而不可重复读是读取了另一个事务已提交的数据
3.幻读:同一个事务,同样的条件,前后可能得到不同的数据-----侧重的是新增和删除(读取了提交的新事务,只增、删操作)
解释:在事务A多次读取构成中,事务B对数据进行了新增操作,导致事务A多次读取的数据不一样
幻读和不可重复读的区别:不可重复读是针对记录的update操作,只要在记录上加锁,就可避免。幻读是对记录的insert、delete 操作
解决方式:对表加锁解决
4.丢失更新:一个事务的修改覆盖了另一个事务所做的操作
事务1:对id列执行了update操作,将ID列改为5
事务2:对ID列执行update 操作,将ID列修改为10,这时事务1提交。事务2再提交
以上四个问题都可以通过不允许两个用户同时运行一个事务来很容易的解决掉:用户1的事务没有结束,用户二的事务就不能开始,这样就不会出错了。但是这样的话,数据库的并发性能较差,不能接受
每个事务都有一个隔离级别,规定了并发运行的两个事务之间是否允许发生上面的问题
八、事务的隔离级别:每个事务都有一个隔离级别(参数tx_isolation=)
1.未提交读:会发生脏读、不可重复读、幻读、丢失更新
解释:以操作同一行数据为前提,读事务允许其他事务读事务和写事务,未提交的写事务禁止其他写事务,但是允许其他读事务
一、两边将隔离级别设定为未提交读,同时开启事务
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
mysql> start transaction;
二、在事务1中先查看一下要修改的表,然后更新表中的内容,事务1不提交
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | b |
| 3 | c |
+----+------+
mysql> update rep set name='q' where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
三、在事务1的修改未提交的情况下,去事务2查看表rep的内容
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | q |
| 3 | c |
+----+------+
2.以提交读:避免脏读,发生了不可重复读、幻读、丢失更新
一、两边将隔离级别设定为以提交读,同时开启事务
mysql> SET SESSION tx_isolation='READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
二、两边查看要修改的表
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | b |
| 3 | c |
+----+------+
三、事务1对表进行修改,不提交
mysql> update rep set name='q' where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
四、事务2对id=2的数据进行查看和修改
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | b |
| 3 | c |
+----+------+
mysql> update rep set name='m' where id=2;---------------这行表示没有出现结果,因为这一行被事务1锁住了
3.可重复读:避免了脏读、不可重复读、幻读,发生了丢失更新
解释:以操作同一行数据为前提,读事务禁止其他写事务,但允许其他读事务,未提交的写事务禁止其他读事务和写事务
一、两边将隔离级别设定为可重复读,同时开启事务
mysql> SET SESSION tx_isolation='REPEATABLE-READ';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
二、事务1对表rep进行读事务
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | b |
| 3 | c |
+----+------+
3 rows in set (0.00 sec)
mysql> update rep set name='z' where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | z |
| 3 | c |
+----+------+
3 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
三、事务2对表rep进行读事务
mysql> select * from rep;
+----+------+
| id | name |
+----+------+
| 1 | c |
| 2 | b |
| 3 | c |
+----+------+
3 rows in set (0.00 sec)
4.串行化:避免脏读、不可重复读、幻读、丢失更新
解释:提供严格的事务隔离,它要求事务序列化执行,事务只能一个接一个的执行,不能并发执行
一、两边将隔离级别设定为串行化,同时开启事务
mysql> SET SESSION tx_isolation='SERIALIZABLE';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------+
1 row in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
二、事务1对rep表进行修改,不提交
mysql> update rep set name='q' where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
三、事务2对rep表进行查看、修改
mysql> select * from rep;--------------------事务2对表不能查看
mysql> update rep set name='m' where id=2;----------事务2对表也不能修改
事务的隔离级别建议在创建数据库时在配置文件中指定,最好不要后期修改
隔离级别 | 脏读 | 不可重复的 | 幻读 | 丢失更新 |
READ-UNCOMMITTED | YES | YES | YES | YES |
READ-COMMITTED | NO | YES | YES | YES |
REPEATABLE-READ | NO | NO | NO | YES |
SERIALIZABLE | NO | NO | NO | NO |