Answers:
“ 未提交的数据存储在哪里,以便READ_UNCOMMITTED事务可以从另一个事务读取未提交的数据? ”
新的未提交记录(群集PK)版本被视为页面上记录的“当前”版本。因此它们可以存储在缓冲池和/或表空间中(例如tablename.ibd)。然后,需要在READ-UNCOMMITTED以外的任何内容上构建快照/视图的事务,都需要使用UNDO记录(存储在系统表空间中)构造该行的先前版本(遵循历史记录列表)。当读取未提交的记录时,InnoDB可能还需要从变更缓冲区读取一些未提交的二级索引记录并应用它们,然后再将记录提交给用户。
正是这种行为使InnoDB中的回滚变得相对昂贵。这是导致保存已更新记录的长时间运行的空闲事务也可能导致潜在性能问题的重要因素,因为这些事务将阻止清除操作,并且旧记录版本的历史记录列表会增加,而UNDO记录需要重建这些旧版本点播,将继续增长。这会减慢需要读取记录的旧版本/已提交版本的新事务的速度,因为它们需要遍历越来越长的历史记录列表(这是UNDO记录的单链接列表),并且需要做更多的工作才能重建记录的旧版本。因此,您最终要使用很多CPU周期(更不用说内部锁定原语了:互斥体,rw_locks,信号量等)。
希望这有意义吗?:)
作为FYI,在MySQL 5.7中,您可以移动UNDO表空间并注销系统表空间,并使它们自动被截断。如果您有一个长时间运行的事务来阻止清除操作,则它们可能会变得非常大,从而导致历史记录列表的长度非常长且不断增长。将它们存储在系统表空间中是ibdata1文件很大/正在增长的最常见原因,而该文件又不能被截断/缩小/抽空以便以后回收该空间。
您询问
未提交的数据存储在哪里,以便READ_UNCOMMITTED事务可以从另一个事务读取未提交的数据?
为了回答您的问题,您需要知道InnoDB Architecture的外观。
下图是Percona首席技术官Vadim Tkachenko于多年前创建的
根据有关InnoDB事务模型和锁定的 MySQL文档
COMMIT表示在当前事务中所做的更改将永久化,并在其他会话中可见。另一方面,ROLLBACK语句取消当前事务所做的所有修改。COMMIT和ROLLBACK都释放在当前事务期间设置的所有InnoDB锁。
由于COMMIT和ROLLBACK控制数据可见性,因此READ COMMITTED和READ UNCOMMITTED将不得不依赖记录更改的结构和机制。
回滚段和撤消空间将在应用更改之前知道更改后的数据是什么样的。重做日志将知道要进行哪些更改才能使数据更新。
你也问过
为什么READ_COMMITTED事务无法读取未提交的数据,即执行“脏读取”?什么机制强制执行此限制?
重做日志,撤消空间和锁定行开始起作用。您还必须考虑InnoDB缓冲池(您可以在其中使用innodb_max_dirty_pages_pct,innodb_buffer_pool_pages_dirty和innodb_buffer_pool_bytes_dirty测量脏页)。
鉴于此,READ COMMITTED会知道什么数据永久出现。因此,无需查找未提交的脏页。READ COMMITED就是已提交的脏读。READ UNCOMMITTED将继续知道要锁定哪些行以及已读取或忽略哪些重做日志以使数据可见。
要完全了解要管理隔离的行锁定,请阅读InnoDB事务模型和锁定