1、事务的四大特征
A:原子性:事务是最小的单位,不可以在分割;
C:一致性:事务要求,同一事务中的sql语句,必须保证同时成功或同时失败;
I:隔离性:事务1和事务2之间是具有隔离性的;
D:持久性:事务一旦结束,就不可以返回。
事务开启方式:
1.修改默认提交;
2.begin;
3.start transaction;
事务提交:
commit;
事务手动回滚:
rollback;
2、事务的隔离性:
1.read uncommitted; 读未提交的
如果有事务a和事务b,事务a对数据进行操作,在操作的过程中,事务没有被提交,但是事务b可以看见事务a操作的结果。
mysql> INSERT INTO user VALUES(3,'小明',1000); mysql> INSERT INTO user VALUES(4,'淘宝',1000); mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | +----+------+-------+
如何查看数据库的隔离级别?
系统级别:
mysql> select @@global.transaction_isolation;
会话级别:
mysql> select @@transaction_isolation;
默认的隔离级别:
mysql> select @@global.transaction_isolation; +--------------------------------+ | @@global.transaction_isolation | +--------------------------------+ | REPEATABLE-READ | +--------------------------------+
如何修改隔离级别?
mysql> set global transaction isolation level read uncommitted; mysql> select @@global.transaction_isolation; +--------------------------------+ | @@global.transaction_isolation | +--------------------------------+ | READ-UNCOMMITTED | +--------------------------------+
eg.小明买鞋
成都的小明转账800:
mysql> start transaction; mysql> update user set money=money-800 where name='小明'; mysql> update user set money=money+800 where name='淘宝'; mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 200 | | 4 | 淘宝 | 1800 | +----+------+-------+
广州的淘宝查收:
mysql> use bank; Database changed mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 200 | | 4 | 淘宝 | 1800 | +----+------+-------+
之后成都的小明:
mysql> rollback; mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | +----+------+-------+
之后广州的淘宝才发现:
mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | +----+------+-------+
如果两个不同的地方都在进行操作,事务A(小明)开启之后,它的数据仍可以被其他事务B(广州)读取到。这样就会出现(脏读)。
脏读:一个事务读到了另外一个事务没有提交的数据。实际上开发是不允许脏读出现的。
2.read committed; 读已提交的
修改隔离级别并查看:
mysql> set global transaction isolation level read committed; mysql> select @@global.transaction_isolation; +--------------------------------+ | @@global.transaction_isolation | +--------------------------------+ | READ-COMMITTED | +--------------------------------+
eg.
银行的会计小张:
mysql> start transaction; mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | +----+------+-------+
小张出去上厕所了。
此时的小王:
mysql> use bank; Database changed mysql> start transaction; mysql> insert into user values(5,'c',100); mysql> commit; mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | | 5 | c | 100 | +----+------+-------+
小张回来了:
mysql> select avg(money) from user; +------------+ | avg(money) | +------------+ | 820.0000 | +------------+
咦?money的平均值不应该是1000么?怎么变成了820?
虽然我能读到另外一个事务提交的数据,但还是会出现问题,就是读取同一个表的数据,发现前后不一致。
此称为:不可重复读现象。
3.repeatable read; 可以重复读
修改隔离级别并查看:
mysql> set global transaction isolation level repeatable read; mysql> select @@global.transaction_isolation; +--------------------------------+ | @@global.transaction_isolation | +--------------------------------+ | REPEATABLE-READ | +--------------------------------+
在REPEATABLE-READ隔离级别下又会出现什么问题?
张全蛋在成都
mysql> start transaction;
王尼玛在北京
mysql> start transaction;
张全蛋在成都
mysql> insert into user values(6,'d',1000);
mysql> commit; mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | | 5 | c | 100 | | 6 | d | 1000 | +----+------+-------+
王尼玛在北京
mysql> insert into user values(6,'d',1000); ERROR 1062 (23000): Duplicate entry '6' for key 'PRIMARY'
mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | | 5 | c | 100 | +----+------+-------+
这种现象叫幻读。
事务a和事务b同时操作一张表,事务a提交的数据,也不能被事务b读到,就可以造成幻读。
4.serializable; 串行化
修改隔离级别:
mysql> set global transaction isolation level serializable; mysql> select @@global.transaction_isolation; +--------------------------------+ | @@global.transaction_isolation | +--------------------------------+ | SERIALIZABLE | +--------------------------------+
当前记录:
mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | | 5 | c | 100 | | 6 | d | 1000 | +----+------+-------+
张全蛋在成都
mysql> start transaction;
王尼玛在北京
mysql> start transaction;
张全蛋在成都
mysql> insert into user values(7,'铁柱',1000); mysql> commit; mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | | 5 | c | 100 | | 6 | d | 1000 | | 7 | 铁柱 | 1000 | +----+------+-------+
王尼玛在北京
mysql> select * from user; +----+------+-------+ | id | name | money | +----+------+-------+ | 1 | a | 900 | | 2 | b | 1100 | | 3 | 小明 | 1000 | | 4 | 淘宝 | 1000 | | 5 | c | 100 | | 6 | d | 1000 | | 7 | 铁柱 | 1000 | +----+------+-------+
张全蛋在成都
mysql> insert into user values(8,'小花',1000);
sql语句会卡住,直到王尼玛commit。
当user表被另外一个事务操作的时候,其他事务里面的写操作是不可以进行的。进入排队状态(串行化)。
直到王尼玛事务结束以后,张全蛋的写入操作才会执行。
串行化的问题是:性能特差。性能:
READ-UNCOMMITTED (脏读)> READ COMMITTED > REPEATABLE READ(幻读) > SERIALIZABLE
隔离级别越高,性能越差,默认级别是REPEATABLE READ。