读读:不存在任何安全问题,不需要并发控制 读写:有线程安全问题,脏读、幻读、不可重复读 写写:有线程安全问题,更新丢失
为了解决读写的并发问题
什么是MVCC
只有InnoDB引擎支持mvcc,mysql默认支持可重复读,就是依赖mvcc实现的。
多版本并发控制,主要是为了提高数据库的并发性能,在多事务、高并发的情况下数据不会错乱。
同一行数据平时发生读写请求时,会上锁阻塞住。
但mvcc用更好的方式去处理读一写请求,做到在发生读-写请求冲突时 不用加锁。不会有锁竞争
这个读是指的快照读,就是单纯的selectMVCC保障写者写操作造成的变化在写操作完成之前(或者数据库事务提交之前)对于其他的读者来说是不可见的。
当前读,当前读是一种加锁操作,是悲观锁。比如insert、delete、update
事务的隔离性就是通过加锁或者MVCC实现的
隔离级别中的读已提交RC和可重复读RR的快照读都是基于MVCC实现的
MVCC如何实现读已提交和可重复读
读已提交(read committed) Oracle默认的隔离级别 允许读取已经提交的数据。只有在同一个事务的第一个快照读才会创建readview,之后的每次快照读都使用的同一个readview,所以每次的查询结果都是一样的。可能会导致幻读和不可重复读。
可重复读(repeatable read)MySQL默认的隔离级别,安全性较好,性能一般 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,
因为可重复度会在第一次select的时候产生readview,只有在同一个事务的第一个快照读才会创建readview,之后的每次快照读都使用的同一个readview,所以另一个事务进行update或者insert操作后,第一个事务select查询的结果不会变
但可能会产生幻读,如果事务中全部都是快照读的话,那么是不会产生幻读问题的,但是快照读和当前读一起使用的时候就会产生幻读问题
解决幻读的问题 就是加锁
mvcc的实现
基于undolog、版本链、readview。
在我们能看到的字段后面有三个隐藏字段
-
trx_id:事务id,每进行一次事务操作,就会自增1。
-
roll_pointer:回滚指针,用于找到上一个版本的数据,结合undolog进行回滚。
-
row_id 隐藏主键,如果数据表没有设置主键,会有一个6字节的rowid
undolog:回滚日志,在insert、delete、update操作的时候方便回滚日志的信息
什么是readview视图呢?
readview叫做读视图,是事务在进行快照读操作的时候产“生的视图,此视图保存的并不是实际的数据,而是事务相关的信息,主要的用途是来进行可见性判断
当我们用select读取数据时,这一时刻的数据会有很多个版本(例如上图有四个版本),但我们并不知道读取哪个版本,这时就靠readview来对我们进行读取版本的限制,通过readview我们才知道自己能够读取哪个版本。
在一个readview快照中主要包括以下这些字段:
m_ids:活跃的事务就是指还没有commit的事务。
max_trx_id:例如m_ids中的事务id为(1,2,3),那么下一个应该分配的事务id就是4,max_trx_id就是4。
creator_trx_id:执行select读这个操作的事务的id。
readview如何判断版本链中哪个版本可用?
trx_id表示要读取的事务id
(1)如果要读取的事务id等于进行读操作的事务id,说明是我读取我自己创建的记录,所以可以访问这个版本
(2)如果要读取的事务id小于最小的活跃事务id,说明要读取的事务已经提交,那么可以读取。
(3)max_trx_id表示生成readview时,分配给下一个事务的id,如果要读取的事务id大于max_trx_id,说明该id已经不在该readview版本链中了,故无法访问。
(4)m_ids中存储的是活跃事务的id,如果要读取的事务id不在活跃列表,那么就可以读取,反之不行。