mysql事务
mysql中,事务其实是一个最小的不可分割的工作单元。事务能够保证一个业务的完整性。
----比如我们的银行转账:
a-> -100
update user set money=money-100 where name='a';
b-> +100
update user set money=money+100 where name='b';
----实际程序中,如果只有一条语句执行成功了,而另外一条没有执行成功?
----出现数据前后不一致。
----多条sql语句,可能会有同时成功的要求,要么就同时失败。
----mysql中如何控制事务?
1、mysql默认是开启事务的(自动提交)。
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
----默认事务开启的作用是什么?
当我们去执行一个sql语句的时候,效果会立即体现出来,且不能回滚。
----事务回滚:撤销sql语句执行效果
rollback;
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
1 row in set (0.00 sec)
//回滚无效
----设置mysql自动提交为false;
set autocommint=0;
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
1 row in set (0.00 sec)
mysql> insert into user values(2,'b',1000);
Query OK, 1 row affected (0.00 sec)
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
+----+------+-------+
1 row in set (0.00 sec)
//插入命令后,输入commit,再rollback不会回滚;
mysql> insert into user values(2,'b',1000);
Query OK, 1 row affected (0.00 sec)
//手动提交数据
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
//再撤销,是不可以撤销的(持久性)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 1000 |
| 2 | b | 1000 |
+----+------+-------+
2 rows in set (0.00 sec)
----自动提交?
@@autocommit=1;
----手动提交?
commit;
----事务回滚?
rollback;自动提交时,不能回滚,手动提交前,事务可回滚,手动提交后,事务不可回滚。
----事务给我们提供了一个返回的机会。
mysql> set autocommit=1;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@autocommit=1;
+----------------+
| @@autocommit=1 |
+----------------+
| 1 |
+----------------+
1 row in set (0.00 sec)
----手动开启事务;
----begin;
或者start transaction;
都可以帮我们手动开启一个事务。
但是事务开启之后,一旦commit提交,事务不可回滚(即当前事务结束)。
mysql> update user set money=money-100 where name='a';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update user set money=money+100 where name='b';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
+----+------+-------+
2 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
+----+------+-------+
2 rows in set (0.00 sec)
ysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set money=money-100 where name='a';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update user set money=money+100 where name='b';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | a | 900 |
| 2 | b | 1100 |
+----+------+-------+
----事务的四大特征:
A:原子性:事务是最小的单位,不可在分割。
C:一致性:事务要求,同一事务中的 sql 语句,必须保证同时成功或同时失败。
I:隔离性:事务1 和事务2 之间是具有隔离性的。
D:持久性:事务一旦结束(commit,rollback),就不可返回。
----事务开启:
1、修改默认提交 set autocommit = 0;
2、begin;
3、start transaction;
----事务手动提交:
commit;
----事务手动回滚:
rollback;
----事务的隔离性:
1、read uncommitted; (读未提交的)
2、read committed; (读已经提交的)
3、repeatable read; (可以重复读)
4、serializable; (串行化)
1、read uncommitted
如果有事务a 和事务b,a 事务对数据进行操作,在操作的过程中,事务没有被提交,但是 b 可以看见a 操作的结果。
//bank数据库 user表
insert into user values(3,'xiaoming',1000);
insert into user values(4,'taobaodian',1000);
----如何查看数据库的隔离级别?
查看系统级别:
select @@global.transaction isolation;
查看会话级别:
select @@transaction isolation;
----如何修改数据库的隔离级别?
set global transaction isolation level read uncommitted;
----转账:小明在淘宝店买鞋子:800块。
start transaction;//事务开始
update user set money=money-800 where name='xiaoming';
update user set money=money+800 where name='taobaodian';
ysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 800 |
| 2 | b | 1200 |
| 3 | xiaoming | 200 |
| 4 | taobaodian | 1800 |
+----+------------+-------+
//淘宝店查询到账,发货,吃饭
mysql> rollback;//小明操作回滚
Query OK, 0 rows affected (0.00 sec)
//淘宝店吃过饭,付账发现钱不对
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 800 |
| 2 | b | 1200 |
| 3 | xiaoming | 1000 |
| 4 | taobaodian | 1000 |
+----+------------+-------+
4 rows in set (0.00 sec)
----淘宝店看到了小明未提交的数据,即读未提交。
----read uncommitted;
如果两个不同的地方,都在进行操作,如果事务a 开启之后,他的数据可以被其他事务读取到;这样就会出现脏读。
脏读:一个事务读到了另外一个事务没有提交的数据。
实际开发是不允许脏读出现的。
----read commit
修改数据库隔离级别;
mysql> set global transaction isolation level read
committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| READ-COMMITTED |
+--------------------------------+
bank数据库 user表
小张:银行的会计
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 800 |
| 2 | b | 1200 |
| 3 | xiaoming | 1000 |
| 4 | taobaodian | 1000 |
+----+------------+-------+
4 rows in set (0.00 sec)
小张休息抽烟中…
小王:
start transaction;
insert into user values(5,'c',100);
commit;
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 800 |
| 2 | b | 1200 |
| 3 | xiaoming | 1000 |
| 4 | taobaodian | 1000 |
| 5 | c | 100 |
+----+------------+-------+
----小张上完厕所,抽烟回来
ysql> select avg(money) from user;
+------------+
| avg(money) |
+------------+
| 820.0000 |
+------------+
1 row in set (0.00 sec)
----money 的平均值不是1000,变少了?
----虽然我只能读到另外一个事务提交的数据,但还是会出现问题,就是读取同一个表的数据,发现前后不一致;
即不可重复读现象:read committed
;
3、repeatable read; 可以重复读
set global transaction isolation level repeatable read;
select @@global.transaction_isolation
-> ;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
在repeatable read 隔离级别下又会出现什么问题?
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 800 |
| 2 | b | 1200 |
| 3 | xiaoming | 1000 |
| 4 | taobaodian | 1000 |
| 5 | c | 100 |
+----+------------+-------+
5 rows in set (0.00 sec)
----张全蛋-程度
start transaction;
----王尼玛-北京
start transaction;
----张全蛋-成都
insert into user values(6,'d',1000);
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 800 |
| 2 | b | 1200 |
| 3 | xiaoming | 1000 |
| 4 | taobaodian | 1000 |
| 5 | c | 100 |
| 6 | d | 1000 |
commit;//提交
----王尼玛-北京
//再开一终端
mysql> select * from user;
+----+------------+-------+
| id | name | money |
+----+------------+-------+
| 1 | a | 800 |
| 2 | b | 1200 |
| 3 | xiaoming | 1000 |
| 4 | taobaodian | 1000 |
| 5 | c | 100 |
+----+------------+-------+
mysql> insert into user values(6,'d',1000);
ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY'
----这种现象就叫做幻读!!!
事务a 和事务b 同时操作同一张表,事务a提交的数据,也不能被事务b读到,就可以造成幻读。
4、serializable; 串行化
//修改隔离级别为串行化
mysql> set global transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| SERIALIZABLE |
+--------------------------------+
1 row in set (0.00 sec)
张全蛋-成都
start transaction;
insert into user values(8,'wangxiaohua',1000);
//sql语句被卡主了?
王尼玛-北京
start transaction;
select * from user;
----当user被另外一个事务操作的时候,其他事务里面的写操作,是不可以进行的。
----直到进入排队状态(串行化),直到另一个事务结束之后(commit),其他事务的写入操作才会执行。
----在没有等待超时的情况下,其他事务会立即执行。
----串行化问题时,性能特差。
READ-UNCOMMITTED > READ-COMMITTED > REPEATABLE-READ > SERIALIZABLE;
----隔离级别越高,性能越差。
mysql 默认隔离级别是 REPEATABLE-READ;