什么是事务(ACID)?事务的作用是什么?
MySQL中,事务其实是一个最小的不可分割的工作单元,事务能够保证一个业务的完整性。
UPDATE user SET money=money-100 where name = 'a';
UPDATE user SET money=money+100 where name ='b';
多条SQL语句,可能会有同时成功的要求,要么就同时失败
如何控制事务?
MySQL默认开启事务(自动提交) SELECT @@autocommit;
默认事务开启的作用是什么?
当我们执行一条SQL语句,效果会立即体现出来,且不会回滚。
回滚:撤销SQL语句执行效果 rollback;
设置MySQL自动提交为false SET autocommit=0;
关闭了MySQL的自动提交(commit) 需要自己手动提交 commit;
commit后再撤销(rollback)是无法撤销成功的(事务的持久性)
事务的四大特征?
A 原子性(atomicity):事务是最小的单位,不可再分割
C 一致性(consistency):事务要求,同一事务中的SQL语句,必须保证同时成功或者同时失败
I 隔离性(isolation):事务1和事务2之间是有隔离性的
D 持久性(durability):事务一旦结束(commit),就不可以返回(rollback)
小结
事务开启:1. 修改默认提交 SET autocommit=0;
2. BEGIN;
3. START TRANSACTION;
事务手动提交: commit;
事务手动回滚: rollback;
事务的隔离性?
1. read uncomitted; 读未提交的(脏读)
如果事务a和事务b,a 事务对数据进行 操作,在操作过程中,事务未被提交,但b可以看到a操作的结果
insert into user values(2,'b',1000);
insert into user values(3,'c',1000);
insert into user values(4,'d',1000);
如何查看数据库的隔离级别?
mysql 8.0 select @@global.transaction_isolation; (系统级别)
select @@transaction_isolation; (会话级别)
mysql5.x select @@global.tx_isolation; (系统级别)
select @@tx_isolation; (会话级别)
如何修改系统隔离级别?
set global transaction isolation level read uncommitted;
什么是脏读?
脏读:一个事务读到了另外一个事务未提交的数据
如果在两个不同的地方,都在进行操作,如果事务a开启之后,他的数据可以被其他事务读取到,
这样就会出现脏读。实际开发中,不允许脏读出现
例如:a 给 c 转账800块
2. read committed; 读已经提交的(不可重复读——针对多个事务对同一字段的修改)
一个事务的提交,导致另外一个事务已更新的数据丢失,或者两次读取结果不一致。
3. repeatable read; 可以重复读(MySQL默认级别)
什么是幻读?
事务a和事务b同时操作一张表,事务a提交的数据也没有被事务b读到,就会造成幻读(对于一个表查询到的行数不一致)(比如:明明a插入了一条数据,b同样也插入这条数据,插入前进行查询发现表中无记录,但插入会报错,说该数据已存在)
4. serializable; 串行化(可以解决幻读)
语句被卡住了,当表被另外一个事务操作时,其他事务的写操作是不可以进行的
进入排队状态(串行化),直到其他事务事务结束,这个事务的写入操作才会执行
在没有等待超时的情况下
串行化的问题是:性能很差
如何实现read repeatable?
MySQL是通过MVCC机制来实现的,即:多版本并发控制,multi-version concurrency control。
使用innodb存储引擎,会在每行数据后添加两个隐藏列,一个保存行创建时间的事务id,另一个保存删除时间的事务id,事务id由MySQL自己创建并且全局唯一递增的。
创建事务id<=当前事务id && 当前事务<删除事务id 就能查到这一行
在一个事务内查询的时候,mysql只会查询创建时间的事务id小于等于当前事务id的行,这样可以确保这个行是在当前事务中创建,或者是之前创建的;
同时一个行的删除时间的事务id要么没有定义(就是没删除),要么是比当前事务id大(在事务开启之后才被删除);满足这两个条件的数据都会被查出来。
如果在某个事务执行期间,别的事务更新了一条数据,其实就是在innodb中插入了一行记录,并把创建时间设置为新的事务id,同时将这条记录之前的那个版本的删除时间设置为新的事务的id。
这样的话,事务其实对某行记录的查询,始终都是查找之前的那个快照,因为之前的那个快照的创建时间小于等于自己事务id,然后删除时间的事务id比自己事务id大,所以这个事务运行期间,会一直读取到这条数据的同一个版本。