事务
一、事务是什么
mysql中,事务其实是一个最小的不可分割的工作单元,事务能保证一个业务的完整性。
比如银行转账:
a :减掉100
update user set money=money-100 where name='a';
b:加上100
update user set money=money+100 where name='b';
如果这两条语句只有一条执行成功了,另外一条没有成功,会出现数据前后不一致。
update user set money=money-100 where name='a';
update user set money=money+100 where name='b';
多条sql语句,可能会有同时成功的要求,要么就同时失效
这就是事务所要完成的
二、如何控制事务
mysql中如何控制事务?
(1)mysql默认是开启的(自动提交)1表示开启,0表示关闭
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
(2)默认事务的作用?
当我们去执行一个sql语句的时候,效果会立即体现出来,且不能回滚
回滚:
create database bank;
use bank;
create table user(
id int primary key,
name varchar(20),
money int
);
insert into user values(1,'a',1000);
---事务回滚:撤销sql语句执行效果(执行rollback不会生效)
rollback;
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
---设置mysql自动提交为false(关闭自动提交)此操作关闭了mysql的自动提交(commit)
set autocommit=0;
---插入2号
insert into user values(2,'b',1000);
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
---执行rollback
rollback;
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
---使用commit才是真正的提交,不会被回滚
mysql> insert into user values(2,'b',1000);
mysql> commit;
mysql> rollback;
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 800 |
| 2 | b | 1000 |
+----+------+-------+
使用commit再使用rollback不起作用(持久性)
自动提交 —@@autocommit=1
手动提交 —commit;
事务回滚 —rollback;
事务给我们提供了一个反悔的机会
三、手动开启事务
自动提交设置为1,会自动提交,立即生效,不能rollback
mysql> set autocommit=1;
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 800 |
| 2 | b | 1000 |
+----+------+-------+
mysql> update user set money=money-100 where name='a';
mysql> update user set money=money+100 where name='b';
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 700 |
| 2 | b | 1100 |
+----+------+-------+
mysql> rollback;
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 700 |
| 2 | b | 1100 |
+----+------+-------+
手动开启事务方法1
mysql> begin;
mysql> update user set money=money-100 where name='a';
mysql> update user set money=money+100 where name='b';
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 600 |
| 2 | b | 1200 |
+----+------+-------+
mysql> rollback;
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 700 |
| 2 | b | 1100 |
+----+------+-------+
手动开启事务方法2
mysql> start transaction;
mysql> update user set money=money-100 where name='a';
mysql> update user set money=money+100 where name='b';
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 600 |
| 2 | b | 1200 |
+----+------+-------+
mysql> rollback;
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 700 |
| 2 | b | 1100 |
+----+------+-------+
加commit不能回滚
mysql> start transaction;
mysql> update user set money=money-100 where name='a';
mysql> update user set money=money+100 where name='b';
mysql> commit;
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 600 |
| 2 | b | 1200 |
+----+------+-------+
mysql> rollback;
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 600 |
| 2 | b | 1200 |
+----+------+-------+
2 rows in set (0.00 sec)
四、ACID特征与使用
事务的四大特征:
A:原子性:事务是最小的单位,不可以再分割
C:一致性:事务要求,同一事务中的语句必须保证同时成功或者同时失败
I:隔离性:事务a和事务b之间是具有隔离性的
D:持久性:事务一旦结束(commit,rollback),就不可以返回。
事务开启:
1.修改默认提交 set autocommit=1;
2.begin
3.start transaction;
事务提交:
commit;
事务手动回滚:
rollback;
五、事务隔离性-脏读
- read uncommitted; 读未提交的
- read committed; 读已经提交的
- repeatable read;可以重复的
- serializable; 串行化
1—read uncommitted
如果有事务a和事务b,a事务对数据进行操作,在操作的过程中,事务没有被提交,但是b可以看见a操作的结果
mysql> insert into user values(3,'xiaoming',1000);
mysql> insert into user values(4,'taobaodian',1000);
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 600 |
| 2 | b | 1200 |
| 3 | xiaoming | 1000 |
| 4 | taobaodian | 1000 |
+----+------------+-------+
(1)如何查看数据库的隔离级别
mysql8.0版本8.0
系统级别
mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
会话级别
mysql> select @@transaction_isolation;
mysql5.x版本
系统级别
mysql> select @@global.tx_isolation;
会话级别
mysql> select @@tx_isolation;
(2)如何修改数据库的隔离级别
mysql> set global transaction isolation level read uncommitted;
mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| READ-UNCOMMITTED |
+--------------------------------+
转账:xiaoming在taobaodian买鞋子,800块
start transaction;
update user set money=money-800 where name='xiaoming';
update user set money=money+800 where name='taobaodian';
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 600 |
| 2 | b | 1200 |
| 3 | xiaoming | 200 |
| 4 | taobaodian | 1800 |
+----+------------+-------+
mysql> use bank;
Database changed
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 600 |
| 2 | b | 1200 |
| 3 | xiaoming | 200 |
| 4 | taobaodian | 1800 |
+----+------------+-------+
如果两个不同的地方,都在进行操作,如果事务a开启之后,他的数据可以被其他事务读取到,这样就会出现脏读
脏读:一个事务读到了另外一个事务没有提交的数据,就叫做脏读。
实际开发不允许出现脏读
六、事务隔离性-不可重复读
在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据并修改数据。那么,在第一个事务的两次读数据之间。由于另一个事务的修改,那么第一个事务两次读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
2— read committed
读到已提交的
mysql> set global transaction isolation level read committed;
mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| READ-COMMITTED |
+--------------------------------+
例子:
假设小张是银行的会计,在读取小明的账户余额。读取一次之后,小明在另一事务修改了自己的余额,则小张在同一事务中读到的同一数据就不一样了
--小张
start transaction;
select * from user where name='小明';
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 3 | 小明 | 1000 |
+----+--------+-------+
---小明在小张事务还没有提交时修改数据
start transaction;
update user set money=800 where name='小明';
---小张再次读取,发现数据不对了
select * from user where name='小明';
+----+--------+-------+
| id | name | money |
+----+--------+-------+
| 3 | 小明 | 800 |
+----+--------+-------+
七、事务隔离性-幻读
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样。
3—repeatable read
mysql> set global transaction isolation level repeatable read;
mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
例子:
假如小王想在系统中注册一个账户,然后他先查询是否有账户被注册:
start transaction;
select * from user;
+----+-----------+-------+
| id | name | money |
+----+-----------+-------+
| 1 | a | 800 |
| 2 | b | 1100 |
| 3 | 小明 | 1000 |
| 4 | 淘宝店 | 1000 |
+----+-----------+-------+
小王发现5号还没有被注册,于是准备插入进行注册。
此时,小李也发现5号没有被注册,所以将5号注册为了自己。
start transaction;
insert into user values(5,'小李',0);
commit;
这时候小王开始注册
insert into user values(5,'小王',0);
ERROR 1062 (23000): Duplicate entry '5' for key 'user.PRIMARY'
但是却会报错,提示5号已经存在了。这就是幻读。
八、隔离级别-串行化_SERIALIZABLE
4— serializable
mysql> set global transaction isolation level serializable;
mysql> select @@global.transaction_isolation;
Serializable是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。
虽然Serializable隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。
实现的原理是通过锁来实现。