MVCC技术
MVCC(Multi-Version Concurrency Control)多版本并发控制。 Mysql的大多数事务(如InnoDB, FaIcon)型存储引擎实现的都不是简单的行级锁,基于提升 并发性能的考虑,他们一般都实现了MVCC。当前不仅仅是MYSQL,其它数据库系统如Oracle, PostgreSQL也都实现了MVCC,MVCC并没有一个统一的标准,不同的数据库,不同的存储引擎的 实现都不相同。
MVCC的优缺点 MVCC在大多数情况下代替了行级锁,实现了对读的非阻塞,读不加锁,读写不冲突。缺点是没 行记录都需要额外的存储空间,需要做更多的行维护和检查工作。
MVCC的实现原理 undo log 为了便于理解MVCC的实现原理,简单介绍一下undo log的工作过程。 1:开始事务 2:记录数据行数据快照到undo log 3: 更新数据 5:将undo log写入磁盘 6:将数据写入磁盘 7:提交事务 1)为了保证数据的持久性数据要在事务提交前持久化 2)undo log的持久化必须在数据持久化之前,这样才能保证系统崩溃时,可以用undo log来 回滚事务。 InnoDB中的隐藏列 InnoDB通过undo log保存了已更改行的旧版本的信息的快照。 InnoDB的内部实现中为每一行数据增加了三个隐藏列用于实现MVCC 列名 长度 作用 DB_TRX_ID 6 插入或更新行的最后一个事务的事务 标识符(删除视作更新,标记为删除) DB_ROLL_PRT 7 写入回滚段的撤销日志记录(若行已更新,则撤销日志记录包含在更新行之前重 建行内容所需的信息) DB_ROW_ID 6 行标识(隐藏单调自增ID)
MVCC的工作过程 MVCC只在READ COMMITED和REAPEATED READ两个隔离级别下工作。 READ UNCOMMITED 总是读取最新的数据行,而不是符合当前事务版本的数据行,而SERIALIZABLE则会 对所有的数据行加锁。
Select InnoDB会根据两个条件来检查每行记录: 1:InnoDB只查找版本(DB_TRX_ID)早于当前事务版本的数据行(行的系统版本号 <= 事务的系统版本号, 这样可以确保数据行要么是在开始之前已经存在的,要么是事务吱声插入或修改过的。 2:行的删除版本号(DB_ROLL_PTR)要么未定义(未更新过),要么大于当前事务版本号(在当前事务开始 之后更新的),这样可以确保事务读取到的行,在事务开始之前未被删除。
Insert InnoDB为新插入的每一行保存当前系统版本号作为行版本号
InnoDB为删除的每一行保存当前系统的版本号作为删除标识
UPDATE InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为删除标识。
事务 我们使用事务来保证每一条SQL语句的执行结果执行符合我们的预期。事务必须具备ACID特性。 ACID: 原子性 持久性 一致性 隔离性 前三者其实描述的都差不多,而隔离性就比较复杂了,隔离性描述的是在并发场景下数据库的表现,但并发量不是 固定的,而不同的业务可能有不同的需求,为了使数据库能适应不同的并发场景,所以伟大的人们又定义了四种隔离 级别: 1)READ UNCOMMITED (读为提交) 2)READ Commited (读已提交) 3) Repeated READ(可重复读) 5)Serializable 随着数据库隔离级别的提高,数据的并发能力也有所下降。
在RC隔离级别下: 修改数据会加排它锁,事务结束释放,其他事务不许读,解决脏读问题(共享锁当场释放) 在可重复读级别下: 读数据加共享锁,事务结束释放,其他事务不许修改,解决不可重复读(共享锁事务结束释放)
MVCC MVCC即多版本并发控制,使用了双版本好来解决数据的隔离问题,每个事务在开始对每涨表增删改查操作时都会生成 一个版本号,每个事务只能查到“create”小于版本号和"delete"大于版本号的数据,这样,增删查就完全可以并发进行 了,只有修改操作是一定要排队的,这样,就算没有共享锁也解决了不可重复读的问题,因为其他事务修改后,数据的 版本号比我打,我不会读到。
MVCC在RR隔离级别下的并发 引入了MVCC之后,看似很美,然而大家有没有想过两个事务先后对一条数据做更新操作,然后两个事务再读取那条 数据,分别读到什么?这根本是不可能出现的,因为修改操作是串行的,另一个事务必须先commited本事务才能修改。