数据库并发带来的问题
数据库并发会带来脏读、不可重复读、幻读等问题。
- 脏读:当前事务读取了其它事务未提交的数据;
- 不可重复读:当前事务中,在相同的查询条件下前后读取的结果不一样,主要侧重于查询记录的某些列不一样;
- 幻读:当前事务中,在相同的查询条件下前后读取的结果集不一样,主要侧重于查询记录的个数增加或减少;
数据库隔离级别
数据库采用了事务的隔离级别来解决这些问题。
- 读未提交:当前事务可以读取其它事务未提交的数据,不解决脏读、不可重复读、幻读的问题;
- 读已提交:当前事务可以读取其它事务已提交的数据,解决了脏读,但是不解决不可重复读、幻读的问题;
- 可重复读:MySQL默认的隔离级别,解决脏读、不可重复读的问题,也解决了幻读的问题(通过行锁和间隙锁);
- 串行化:并发性最低,所有的事务按照顺序执行,解决了脏读、不可重复读、幻读的问题。
什么是MVCC
MVCC全称多版本并发控制,就是通过保存数据的历史版本,根据比较版本号来处理数据是否显示,从而达到在读操作的时候不会阻塞写操作,写操作的时候不会阻塞读操作,同时可以避免脏读和不可重复读。MySQL的可重复读级别就是用这种方法来处理的。
什么是快照读
快照度可以理解为不加锁的查询操作就是快照读,它的底层原理就是MVCC,由于MVCC是多版本并发控制,所以使用快照读读取的记录不一定是最新的。
MVCC实现原理
MVCC主要由隐藏字段、undolog、read-view来完成。MVCC的目标是如何正确地得到快照结果。
隐藏字段
隐藏字段包含:
- roll_pointer:回滚指针,指向这条记录的上一个版本;
- trx_id:记录操作该数据事务的事务ID,也叫做版本号,主要用于版本比较,从而找到快照;
- db_row_id:隐藏ID,当创建的表没有聚簇索引的时候,会用该ID创建聚簇索引。
undolog
undolog主要用于记录数据被修改之前的日志,在记录修改之前会先把数据拷贝到undolog中,当事务进行回滚时可以通过undolog里的日志进行数据还原。在MVCC多版本控制中,通过读取undolog历史版本数据实现不同的事务之前都有自己的快照数据。
read view
在每个SQL语句执行之前都会得到一个read view,也叫做一致性视图。然后我们查询的数据结果跟read view的几个重要属性匹配从而得到正确的快照结果。
read view的四个重要属性:
-
trx_ids:未提交事务的版本号集合;
-
low_limit_id:执行当前SQL时,当前系统中最大的事务版本号;
-
up_limit_id:执行当前SQL时,当前系统中最小的事务版本号;
-
creator_trx_id:执行当前SQL的事务版本号;
-
如果当前查找到的数据的事务ID<最小事务版本号,说明该数据是当前事务开启之前就已经存在了,可以显示;
-
如果当前查找到的数据的事务ID>最大事务版本号,说明该数据是当前事务开启之后才产生的,所以不可以显示;
-
如果当前查找的数据的事务ID在最小事务版本号和最大事务版本号之间,说明该数据是当前事务开启后,还没有提交;
-
这时候需要在未提交的事务的版本号集合中查找,如果查找到这个事务ID,说明数据还没有提交,如果还等于creator_trx_id,说明这个数据属于当前事务提交的,自己提交的数据是可以看到的;如果不等于,则说明不是当前事务提交的,不是自己提交的当然不能显示。如果事务ID不存在于未提交的事务的版本号集合中中,说明当前数据是已提交的,这种情况下可以显示。