mysql中,事务其实是一个最小的不可分割的工作单元,事务能够保证一个业务的完整性
比如:我们的银行转账:
a -> -100
update user set money=money-100 where name='a';
b -> +100
update user set money=money+100 where name='b';
---实际的程序中,如果只有一条语句成功了,而另外一条没有执行成功?
---出现数据前后不一致
---多条sql语句,可能会同时成功的要求,要么就同时失败。
1、mysql默认是开启事务的(自动提交)。
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.01 sec)
默认事务开启的作用是什么?
当我们去执行一个sql语句的时候,效果会立即体现出来,且不能回滚。
2、事务回滚:撤销sql语句执行效果------rollback;
例子:
插入数据
insert into user values(1,'a',1000);
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
查询是否有插入成功,发现,插入成功,撤销没有效果。
解决方法:设置mysql自动提交为false
set autocommit=0;
mysql> set autocommit=0;set
Query OK, 0 rows affected (0.04 sec)
mysql> select @@autocommit; 上面的操作,设置了mysql的自动提交为关闭
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
mysql> select * from user;
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
+----+----------+
1 row in set (0.04 sec)
mysql> insert into user values(2,'lisi');
Query OK, 1 row affected (0.11 sec)
mysql> rollback; 撤销操作
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user; 当autocommit=0时,撤销成功。
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
+----+----------+
1 row in set (0.00 sec)
再一次插入数据:
mysql> insert into user values(2,'lisi');
Query OK, 1 row affected (0.11 sec)
手动提交:
mysql> commit;
Query OK, 0 rows affected (0.04 sec)
撤销操作:
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
查询数据,发现无法撤销,因为commint手动提交了
mysql> select * from user;
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
| 2 | lisi |
+----+----------+
2 rows in set (0.00 sec)
自动提交? @@autocommit;
手动提交? commit;
事务回滚? rollback;
如果说这个时候转账:
金额没有改变,可以使用rollback来返回
当@@autocommit为1时,可以使用begin;或者start transaction;都可以手动开启一个事务,在修改语句之前使用
事务的四大特征:
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,'taobao',1000)
1、如何查看数据库的隔离级别:
mysql 5.6:
SELECT @@tx_isolation
系统级别:----mysql默认级别
mysql> SELECT @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set (0.00 sec)
会话级别:
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
2、如何修改隔离级别:
将隔离级别修改为:uncommitted
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED |
+-----------------------+
1 row in set (0.00 sec)
例子:小明买双鞋子付出800,淘宝店得到800
start transaction;
update user set money=money-800 where name='xiaoming';
update user set money=money+800 where name='taobao';
然后淘宝去进货付出1800,(两个初始为1000),发现钱不够,原来小明使用了rollback撤销了,所有数据变成了初始的每人1000。
问题来了,开启 read uncommitted 后:
如果两个不同的地方,都在进行操作,如果事务a read 开启之后,他的数据可以被其他事务读取到,这样就会出现(脏读),脏读:一个事务读到了另外一个事务没有提交的数据
2、read committed
将隔离级别修改为:committed
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-COMMITTED |
+-----------------------+
1 row in set (0.00 sec)
员工A在使用查询命令:
mysql> select * from bank;
+------+------+-------+
| id | name | money |
+------+------+-------+
| 1 | mo | 1000 |
| 2 | li | 1000 |
+------+------+-------+
2 rows in set (0.00 sec)
员工B在提交数据:
start transaction;
insert into bank values(3,'hu',8000);
commit;
mysql> select * from bank;
+------+------+-------+
| id | name | money |
+------+------+-------+
| 1 | mo | 1000 |
| 2 | li | 1000 |
| 3 | hu | 8000 |
+------+------+-------+
3 rows in set (0.00 sec)
员工A 进行求平均数操作:
mysql> select avg(money) from bank;
+--------------------+
| avg(money) |
+--------------------+
| 3333.3333333333335 |
+--------------------+
1 row in set (0.07 sec)
问题来了,当时A员工查询出来的时候没有3号的信息,毕竟实际数据太多,不可能一一对比,虽然我只能读到另一个事务提交的数据,但还是会出现问题,就是:读取同一个表的数据,发现前后不一致。
此现象叫:不可重复现象:read committed
3、repeatable read
设置为 可以重复读的 :
mysql> set global transaction isolation level repeatable read;
Query OK, 0 rows affected (0.02 sec)
mysql> SELECT @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set (0.00 sec)
例子:
现有两人:
员工A 手动开启一个事务:
start transaction;
员工B 在另外一个窗口进入数据库,也手动开启一个事务:、
start transaction;
员工A 插入一条数据:
insert into bank values(6,'b','100');
commit
员工B 也插入相同的数据:
insert into bank values(6,'b','100');
出错!!
这种现象,我们叫他“幻读”
事务a 和 事务b 同时操作一张表,事务a 提交的数据,也不能被事务b 读到,这就造成幻读。
4、serializable; 串行化
mysql> set global transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| SERIALIZABLE |
+-----------------------+
1 row in set (0.00 sec)
例子:
现有两人:
员工A 手动开启一个事务:
start transaction;
员工B 在另外一个窗口进入数据库,也手动开启一个事务:、
start transaction;
员工A 插入一条数据:
insert into bank values(6,'b','100');
commit
员工B 也插入相同的数据:
select * from bank; ----------发现,已经被同步过来了,不需要再次插入
员工A 再次插入一条数据:
start transaction;
insert into bank values(7,'d','100'); ---------发现卡住了,原因是:员工B 开启了start transaction,需要其commit后才能继续操作
结论:当bank 表被另一个事务操作的时候,其他事务里面的写操作,是不可以进行的。进入排队状态(串行化),直到员工B commit后,员工B 才能进行操作。前提,在没有等待超时的情况下。
所以串行化的问题是:性能差!!
性能级别:read uncommitted;读未提交 > read committed;读已经提交的 > repeatable read;可以重复读的 > serializable;串行化
mysql 默认级别为:repeatable read;