网站首页 > 技术文章 正文
目录
MVCC如何实现
旧版本数据延迟清除地原因
快照读(一致性读)的用途
MVCC总结
Mysql MVCC实现
Innodb的事务的相关概念
Innodb多版本的实现
Innodb的事务的提交与回滚
Oracle的多版本实现
ITL解释
ITL内容
Oracle数据块的结构
Oracle的多版本实现
旧版本的数据在回滚段中的结构
PostgreSQL MVCC实现
PostgreSQL事务ID
PostgreSQL MVCC实现
比较事务新旧的方法
可见性判断
以下内容来自于唐成老师的公开课内容,感谢老师不吝分享,仅限于学习交流。如有侵权,及时删除。
借助wiki上的解释:
Multiversion concurrency control (MCC or MVCC), is a concurrency control method commonly used by database management systems to provide concurrent access to the database and in programming languages to implement transactional memory.
MVCC如何实现
? 简单说,就是更新数据时,保留原先版本的数据, 即一行数据存在多个版本。
? 实现方法: – 回滚段的实现方法(Oracle、MySQL Innodb的实现方 法) – 在原先的数据文件中的旧版本数据不删除,生成新版 本的数据(PostgreSQL的实现方法)
? 通常与事务的功能集成在一起:
? 但MVCC时事物回滚或提交之后,旧版本的数据 仍然需要保留一段时间,是延迟清除的。
旧版本数据延迟清除的原因
? 就是为了提供快照读(一致性读)的功能
? 对于Read Commited隔离级别:需要保证当开始 执行一个SQL之后,这个SQL是读一个不变的快 照的数据,即使在这个SQL执行过程中有其它的事务提交了,这些提交的数据对于这个SQL也是 看不到了,这样保证了数据的一致性。
? 对于Read Repeatable隔离级别:从这个事务开 始之后,看到的数据就是一个不变的数据,执行 相同的SQL总是可以看到相同的数据。所以叫"可重复读"
快照读(一致性读)的用途
如果没有MVCC的解决方案
管理者在事务中持有对A的锁,等待获取B的锁,而用户B在事务中持有对B的锁,等待获取A的锁,于是便发生了死锁。
MVCC总结
- 查询和更新、删除、插入操作互相不阻塞
? 当开始一个查询后,读到的数据总是查询 开始时那个时间点的快照
- 在查询开始后,发生的变更(即使已提交), 这次查询也是看不到的。
- 一个事务无论运行多长时间,看到数据都是相 同的
- 不同开始时间的事务中相同的查询,返回的数 据也可能不同
Mysql MVCC实现
Innodb的事务的相关概念
? redo log – 在变更数据之前,把变更先记录到一个文件中, 称为redo log
? undo log – 与redo log相反,undo log是为回滚而用
? rollback segment – 在Innodb中,undo log被划分为多个段,具体 某行的undo log就保存在某个段中,称为回滚 段。
Innodb多版本的实现
事务1:insert t(id, col1,col2,col3,col4) values(23,'a1','b1','c1','d1');
行上有三个隐含字段:分别对应该行的rowid、事务号和回滚指针, id、Col1~Col4是表格列的名字,23、'a1'~'d1'是其对应的数据。
- 多次更新后,回滚指针会把不同版本的记录 串在一起。
- 在Innodb中存在purge线程,它会查询哪些 比现在最老的活动事务还早的undo log,并 删除它们,从而保证undo log文件不至于无 限增长。
Innodb的事务的提交与回滚
提交与回滚
– 当事务正常提交时,Innodb只需要更改事务状 态为COMMIT即可,不需做其他额外的工作
– Rollback需要根据当前回滚指针从undo log中 找出事务修改前的版本,并恢复。
? 如果事务影响的行非常多,回滚则可能会很慢,根 据经验值没提交的事务行数在1000~10000之间, Innodb效率还是非常高的,大量的回滚效能会影响。
? 回滚时,也会产生redo日志 – Innodb的COMMIT效率高,Rollback代价大
Innodb的可见性判断
? InnoDB表会有三个隐藏字段
– 6字节的DB_ROW_ID
– 6字节的DB_TX_ID
? InnoDB内部维护了一个递增的tx id counter,其当 前值可以通过show engine innodb status获得
– 7字节的DB_ROLL_PTR(指向回滚段的地址)
可见性比较的方法:
– 并不是用当前事务ID与表中各个数据行上的事 无ID去比较的
– 在每个事务开始的时候,会将当前系统中的所 有的活跃事务拷贝到一个列表中 (read view), 根据read view最早一个事务ID和最晚的一个事 务ID来做比较的,这样就能确保在当前事务之 前没有提交的所有事务的变更以及后续新启动 的事务的变更,在当前事务中都是看不到的。
– 当然,当前事务自身的变更还是需要看到的。
Oracle的多版本实现
? Oracle也是通过回滚段来实现多版本的 – 但Oracle的实现更复杂,更精细一些。
? Oracle中也有事务ID,但不是递增的
– Undo Segment Number + Transaction Table Slot Number + Wrap
? 与innodb不一样的地方:
– 事务信息并不是记录在每个数据行上的,而是 在块头中的ITL槽上,所以相对来说更省空间
ITL解释
? ITL(Interested Transaction List)在Oracle 数据块的头部
– ITL记录在一个数据块中有多条,每一条itl可 以看作是一个记录,每条记录常被称为槽位 (itl slot)
– 一个itl slot只可以记录一个事务的信息,如果 这个事务已经提交或回滚了,那么这个itl的 位置就可以被反复使用。
? 每个数据块上itl槽的多少可以动态创建, 建表时可以指定: – initrans:每个数据块默认ITL槽数目,默认 为2 maxtrans:每个数据块最多的ITL槽数,最 大255
ITL内容
Oracle数据块的结构
Oracle的多版本实现
? 由于Oracle中的事务ID不是递增的,为了判 断事务之间的先后关系,需要一个递增序 号,这个序号在Oracle中就叫SCN(当然 SCN还有其它用处)
? SCN(System Change Number)
– 是顺序递增的一个数字,在Oracle 中用来标识 数据库的每一次改动,及其先后顺序。
– SCN是由6字节组成,最大值是0xffff.ffffffff
– 单节点的instance中,SCN值存在SGA区,由 system commit number latch保护。任何进程 要得到当前的SCN值,都要先得到这个latch。
– RAC中是通过排队机制(Enqueue)实现SCN在 各并行节点之间的顺序增长,这里不再赘述。
旧版本的数据在回滚段中的结构
PostgreSQL MVCC实现
什么?PostgreSQL没有回滚段!!!
– 是的,没有回滚段。旧数据是放在原有数据文 件中的
– 如果放在原有的数据文件中,旧数据越来越多 怎么办?
? 垃圾回收操作vacuum来做这个事。
? 有自动垃圾回收autovacuum
? 更新操作中新行的物理位置发生了变化, 非更新列的索引是不是也要更新?
– 通常不会,HOT技术。如果原有的数据块之间 有空间,旧行与新行之间会建一个链接,索引 上仍然指向旧的数据行。
? 垃圾回收的代价会不会影响性能?
? 有很多参数控制这个影响:vacuum_cost_delay, vacuum_cost_limit
? 实现方法
– 每行上有xmin和xmax两个系统字段
– 当插入一行数据时,将这行上的xmin设置为当 前的事务id,而xmax设置为0
– 当更新一行时,实际上是插入新行,把旧行上 的xmax设置为当前事务id,新插入行的xmin设 置为当前事务id,新行的xmax设置为0
– 当删除一行时,把当前行的xmax设置为当前事 务id
– 当读到一行时,到commitlog中查询xmin和 xmax对应的事务状态是否是已提交还是回滚了, 就能判断出此行对当前行是否是可见。
– autovacuum进程会把一些不要的旧行清理掉
PostgreSQL事务ID
? 与innodb类似,是一个递增的数字,常常 被称为xid
– 但是一个无符号的32bit的数字表示
? 如何知道事务是提交了还是回滚了?
– 事务的状态记录一个叫commitlog的位图文件 中,即pg_clog目录下的文件中
– 每个事务的状态用两个bit来表示:
– #define TRANSACTION_STATUS_IN_PROGRESS 0x00
– #define TRANSACTION_STATUS_COMMITTED 0x01
– #define TRANSACTION_STATUS_ABORTED 0x02
– #define TRANSACTION_STATUS_SUB_COMMITTED 0x03
PostgreSQL MVCC实现
? 事务ID到达最大的一个数字后,怎么办?
– 到达最大后,又重新开始,
– 此问题被称之为事务ID回卷问题(Transaction ID Wraparound) ? 记录事务的commitlog文件会不会不断的增 大?
比较事务新旧的方法
– 普通事务的比较方法:(int32) (id1 - id2) < 0
– 表达式算出来值为真,则id1比id2更旧一些, 为假则id1比id2新 ? 例子:id1=4294967294,经过4个事务,id2=5, id1-id2=4294967289,而4294967289因大于2^31, 转成int32后会变成一个负数,表过式为真,所以id1 比id2更旧
– BootstrapXID比所有其它事务都旧,包括 FrozenXID
– FrozenXID比普通事务旧
可见性判断
? 与innodb一样,事务开始时,会把当前活 月的事务ID记录到一个列表中,这称之为 快照。
? 系统先通过判断t_xmin是否在全局活跃事务 列表中、是否在事务快照活跃事务列表中、 根据事务提交日志判断事务是提交还是回 滚了等来判断t_xmin事务是否在事务开始时 已经提交
猜你喜欢
- 2024-11-08 常用SQL系列之(九):日期计算、分页、跳行与分级等查询
你 发表评论:
欢迎- 617℃几个Oracle空值处理函数 oracle处理null值的函数
- 610℃Oracle分析函数之Lag和Lead()使用
- 599℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 595℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 591℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 582℃【数据统计分析】详解Oracle分组函数之CUBE
- 572℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 560℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
-
- PageHelper - 最方便的 MyBatis 分页插件
- 面试二:pagehelper是怎么实现分页的,
- MyBatis如何实现分页查询?(mybatis-plus分页查询)
- SpringBoot 各种分页查询方式详解(全网最全)
- 如何在Linux上运行exe文件,怎么用linux运行windows软件
- 快速了解hive(快速了解美国50个州)
- Python 中的 pyodbc 库(pydbclib)
- Linux搭建Weblogic集群(linux weblogic部署项目步骤)
- 「DM专栏」DMDSC共享集群之部署(一)——共享存储配置
- 故障分析 | MySQL 派生表优化(mysql pipe)
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端获取当前时间 (50)
- Oracle RAC (76)
- oracle恢复 (77)
- oracle 删除表 (52)
- oracle 用户名 (80)
- oracle 工具 (55)
- oracle 内存 (55)
- oracle 导出表 (62)
- oracle约束 (54)
- oracle 中文 (51)
- oracle链接 (54)
- oracle的函数 (58)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)