数据库05——事务的实现原理1

161 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

原子性——undolog

事务更新数据的前置操作:

img

undo log:记录数据被修改前的旧值,用于事务回滚,实现事务的原子性和mvcc的快照读

undo log里面包含了以下一些东西:

  • 这条日志的开始位置、主键的各列长度和值、表id、undo log日志编号、undo log日志类型

每行记录后面都有三个隐藏列trx_id(最近修改该数据的版本号)和roll_pointer(回滚指针,指向上一个版本的undolog日志),row_id(没有主键与唯一索引时自动创建的id)。

事务里的insert、update、delete操作会产生对应的undo log日志,包括主键和列信息。

insert:产生特定的undolog日志,数据的回滚指针指向该日志,rollback时通过主键删除数据。

update:产生特定的undolog日志,数据的回滚指针指向新的undolog日志,新undolog日志指向前一个版本的日志,形成版本链(undolog日志序号递增),最末尾的日志是该数据insert时创建的。

对于更新主键的操作,先把原来数据的deletemark标识设为1,这时没有真正删除数据,真正的删除会交给清理线程判断,然后在后面插入一条新数据,新数据也会产生undo log,序号递增。

img

回滚:通过数据的回滚指针找到各个版本的undolog日志,根据日志类型执行反向的操作进行回滚。

回滚过程:

  • 通过undo no=3的日志把id=2的数据删除
  • 通过undo no=2的日志把id=1的数据的deletemark还原成0
  • 通过undo no=1的日志把id=1的数据的name还原成Tom
  • 通过undo no=0的日志把id=1的数据删除

一致性

一致性指事务执行前后数据库的完整性没有被破坏、数据库处于合法状态。

是事务的最终目标,是其他三个特性的原子性、隔离性、持久性目的。

隔离性——锁+MVCC

隔离的本质是解决并发时的冲突。

mysql通过实现写-写操作隔离性,通过MVCC实现写-读操作的隔离性。

当前读:在进行写-写操作(增删改)时需要当前读,读取当前最新版本的数据,会对数据添加X锁,并且会对当前记录添加记录锁,对查询范围内的记录添加间隙锁,所以可以防止幻读问题。

mysql有行锁,表锁,x锁,s锁,ls锁,lx锁,记录锁、间隙锁、临建锁。

不同场景对并发冲突的容忍程度不同,提出了四种隔离级别。

隔离级别脏读不可重复读幻读
read uncommitted(未提交读)
read committed(提交读)×
repeatable read(可重复读)××
serializable (可串行化)×××

MVCC

MVCC本质是依赖了undolog存储的历史数据,并使用快照读的可见性原则来确定当前事务可以看到哪些历史数据,达到无锁状态下的读写并发控制提高数据的可见性和数据库的性能

快照读 Read View :保存了各个版本的历史数据和系统中当前活跃(未commit)的事务ID

生成ReadView时,记录创建该ReadView的事务id(creator_trx_id)、当前系统中活跃的最小事务id(min_trx_id)、系统下一个分配的事务id(max_trx_id)、系统中活跃的事务id列表(m_ids)

只有当对表中的记录改动时,才会为事务分配唯一的事务id,否则事务id值默认为0。

Read View 快照读可见性原则 数据的id (trx_id)与系统中事务id比较

执行过程如下:

  • trx_id=creator_id,当前事务修改过的数据,可以访问
  • trx_id<min_trx_id,该数据的修改早于系统中所有的事务(生成快照时),可以访问
  • trx_id>=max_trx_id,该数据的修改晚于系统中所有的事务,不可以访问
  • trx_id是否在m_ids列表中(遍历) 4.1 是,创建ReadView时,该版本还是活跃的,该版本不可以被访问。查找版本链找下一个版本的数据,继续执行上面的步骤判断可见性,如果最后一个版本还不可见,意味着记录对当前事务完全不可见 4.2 否,创建ReadView时,生成该版本的事务已经被提交,该版本可以被访问
在这里插入图片描述

提交读可重复读两种隔离级别通过+MVCC实现

什么时候生成Read-View?

要看数据库的隔离级别:(可解决脏读、不可重复读)

  • 已提交读:每次select时都会重新生成一个新的【read-view】

事务提交前执行结果对其他事务不可见

使用MVCC快照读时可以使当前事务读取到后启动但先提交的数据。

  • 可重复读:第一次查询数据时生成的【read-view】

    每次读取数据的结果相同。