目录
ACID
原子性(Actomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。
隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。
持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
脏读、不可重复读、可重复读、幻读
脏读(读未提交):事务A正对某条数据做修改,并且未提交事务;这时,事务B来读取该条数据,并且读到了事务A修改的数据。
不可重复读(读已提交):事务A第一次查询数据的值是1,一会事务B修改了该数据值为2,并且提交了事务,此时事务A再次查询到的数据值是2,读了人家事务提交的数据。
可重复读:事务A在执行过程中,对某个数据的值,无论读多少次都是值1;哪怕这个过程中事务B修改了数据的值还提交了,但是事务A读到的还是自己事务开始时这个数据的值。
幻读:幻读针对的是插入。例如事务A第一次查询到的是一条数据,一会事务B插入一条数据且满足事务A的查询条件,此时事务A再次查询就会查到2条数据。
串行化:解决幻读,就需要使用串行化级别的隔离级别,所有事务都串行起来,不允许多个事务并行操作。例如事务A只查询数据,那么B事务这个时候如果要update相同数据,则必须等待A事务完成。
MySQL的默认隔离级别是Read Repeatable,就是可重复读,就是说每个事务都会开启一个自己要操作的某个数据的快照,事务期间,读到的都是这个数据的快照罢了,对一个数据的多次读都是一样的。
MVCC机制
innodb存储引擎,会在每行数据的最后加两个隐藏列,一个保存行的创建时间,一个保存行的删除时间,但是这儿存放的不是时间,而是事务id,事务id是mysql自己维护的自增的,全局唯一。
事务id,在mysql内部是全局唯一递增的,事务id=1,事务id=2,事务id=3
id | name | 创建事务id | 删除事务id |
1 | 张三 | 10 | 12 |
2 | 李四 | 9 | 空 |
2 | 李四2 | 12 | 空 |
事务id=11的事务,查询id=1的这一行的时候,一定会找到创建事务id <= 当前事务id的那一行,select * from table where id=1,就可以查到上面那一行;
事务id=12的事务,将id=1的这一行给删除了,此时就会将id=1的行的删除事务id设置成12;
事务id=11的事务,再次查询id=1的那一行,能查到吗?能查到,要求创建事务id <= 当前事务id,当前事务id < 删除事务id;
事务id=11的事务,查询id=2的那一行,查到name=李四;
事务id=12的事务,将id=2的那一行的name修改成name=李四2;
事务id=11的事务,查询id=2的那一行,答案是:李四,创建事务id <= 当前事务id,当 前事务id < 删除事务id;
在一个事务内查询的时候,mysql只会查询创建时间的事务id小于等于当前事务id的行,这样可以确保这个行是在当前事务中创建,或者是之前创建的;同时一个行的删除时间的事务id要么没有定义(就是没删除),要么是必当前事务id大(在事务开启之后才被删除);满足这两个条件的数据都会被查出来。
那么如果某个事务执行期间,别的事务更新了一条数据呢?这个很关键的一个实现,其实就是在innodb中,是插入了一行记录,然后将新插入的记录的创建时间设置为新的事务的id,同时将这条记录之前的那个版本的删除时间设置为新的事务的id。
这样的话,这个事务其实对某行记录的查询,始终都是查找的之前的那个快照,因为之前的那个快照的创建时间小于等于自己事务id,然后删除时间的事务id比自己事务id大,所以这个事务运行期间,会一直读取到这条数据的同一个版本。