数据库的隔离级别和锁

关于锁和数据库的隔离级别可以参考下面的博客

转载地址:点击打开链接http://blog.csdn.net/samjustin1/article/details/52210125#reply

数据库悲观锁:更新锁U和U,排他锁X互斥,和读锁S共享。当for update的时候会对所查询行上U锁,这时候select 语句上S锁可以正常查询,但是select for update 会等U锁释放,以加U锁。修改操作也会等U锁释放以加X锁。

注:共享锁和共享锁不互斥,共享锁和更新锁不互斥,其他都互斥。

MySql的可重读和读已提交级别用的MCVV概念,读已提交读的都是最新的snapshot,可重读是读取的当前事务id及以前的数据:

innodb会为每一行添加两个字段,分别表示该行创建的版本删除的版本,填入的是事务的版本号,这个版本号随着事务的创建不断递增。在repeated read的隔离级别(事务的隔离级别请看这篇文章)下,具体各种数据库操作的实现:

select:满足以下两个条件innodb会返回该行数据:(1)该行的创建版本号小于等于当前版本号,用于保证在select操作之前所有的操作已经执行落地。(2)该行的删除版本号大于当前版本或者为空。删除版本号大于当前版本意味着有一个并发事务将该行删除了。

insert:将新插入的行的创建版本号设置为当前系统的版本号。

delete:将要删除的行的删除版本号设置为当前系统的版本号。

update:不执行原地update,而是转换成insert + delete。将旧行的删除版本号设置为当前版本号,并将新行insert同时设置创建版本号为当前版本号。

其中,写操作(insert、delete和update)执行时,需要将系统版本号递增。

由于旧数据并不真正的删除,所以必须对这些数据进行清理,innodb会开启一个后台线程执行清理工作,具体的规则是将删除版本号小于当前系统版本的行删除,这个过程叫做purge。

MySQL实现MVCC:

 在Mysql中MVCC是在Innodb存储引擎中得到支持的,Innodb为每行记录都实现了三个隐藏字段:

  • 6字节的事务ID(DB_TRX_ID )
  • 7字节的回滚指针(DB_ROLL_PTR
  • 隐藏的ID

6字节的事物ID用来标识该行所述的事务,7字节的回滚指针需要了解下Innodb的事务模型。

1. Innodb的事务相关概念

为了支持事务,Innbodb引入了下面几个概念:

  • redo log
    redo log就是保存执行的SQL语句到一个指定的Log文件,当Mysql执行recovery时重新执行redo log记录的SQL操作即可。当客户端执行每条SQL(更新语句)时,redo log会被首先写入log buffer;当客户端执行COMMIT命令时,log buffer中的内容会被视情况刷新到磁盘。redo log在磁盘上作为一个独立的文件存在,即Innodb的log文件。
  • undo log
    与redo log相反,undo log是为回滚而用,具体内容就是copy事务前的数据库内容(行)到undo buffer,在适合的时间把undo buffer中的内容刷新到磁盘。undo buffer与redo buffer一样,也是环形缓冲,但当缓冲满的时候,undo buffer中的内容会也会被刷新到磁盘;与redo log不同的是,磁盘上不存在单独的undo log文件,所有的undo log均存放在主ibd数据文件中(表空间),即使客户端设置了每表一个数据文件也是如此。
  • rollback segment
    回滚段这个概念来自Oracle的事物模型,在Innodb中,undo log被划分为多个段,具体某行的undo log就保存在某个段中,称为回滚段。可以认为undo log和回滚段是同一意思。

  • Innodb提供了基于行的锁,如果行的数量非常大,则在高并发下锁的数量也可能会比较大,据Innodb文档说,Innodb对锁进行了空间有效优化,即使并发量高也不会导致内存耗尽。
    对行的锁有分两种:排他锁、共享锁。共享锁针对对,排他锁针对写,完全等同读写锁的概念。如果某个事务在更新某行(排他锁),则其他事物无论是读还是写本行都必须等待;如果某个事物读某行(共享锁),则其他读的事物无需等待,而写事物则需等待。通过共享锁,保证了多读之间的无等待性,但是锁的应用又依赖Mysql的事务隔离级别。

MVCC 运行在 读已提交 和可重读 这两个隔离级别下,当 InnoDB 隔离级别设置为二者其一时,在 SELECT 数据时就会用到版本链。InnoDB 为了解决这个问题,设计了 ReadView(可读视图)的概念。核心问题是版本链中哪些版本对当前事务可见

在 可重读隔离级别下,每个事务 touch first read 时(本质上就是执行第一个 SELECT 语句时,后续所有的 SELECT 都是复用这个 ReadView,其它 updatedeleteinsert 语句和一致性读 snapshot 的建立没有关系),会将当前系统中的所有的活跃事务拷贝到一个列表生成ReadView。在 读已提交 隔离级别下,每个 SELECT 语句开始时,都会重新将当前系统中的所有的活跃事务拷贝到一个列表生成 ReadView。二者的区别就在于生成 ReadView 的时间点不同,一个是事务之后第一个 SELECT 语句开始、一个是事务中每条 SELECT 语句开始。

参考下面

https://zhuanlan.zhihu.com/p/64576887

个人理解是,ReadView会把生成ReadView时候数据库当前最大的事物id记录下来。如果要读取的mvcc中的一个记录的事物id大于ReadView记录下的最大id,说明是当前事物创建ReadView之后提交的,不能访问,或者是等于ReadView中的一个id,说明当前事物创建ReadView时还没有提交或者回滚的,也不能访问,其余情况都可以访问(只要删除标志不是删除,因为delete只是将标志删除置为删除,即delete_flag 为 true,说明这条记录已被删除,不返回。)。读已提交和可重读二者的区别就在于生成 ReadView 的时间点不同。别的都一样。

像其他人说的,每条数据会记录创建时间和删除时间,mvcc是通过当前事物id和创建时间和删除时间进行读取。都是扯淡。

innodb的行级锁是锁的索引,如果索引失效或者没用到索引,就会使用表级锁,因为他的mvcc特性,导致查询count(*)必须全表查(毕竟每个事务内访问的数据域版本都是不一样的)。

innodb是索引组织表IOT,即聚集索引和数据域在一个存储空间,保存的文件也是一个,主键就是聚集索引,如果没有主键的话会找一个非空的唯一索引来当聚集索引,如果还没有的话就默认用innodb的6个字节的rowid来当聚集索引。非聚集索引的叶子节点存储的是索引值和聚集索引值,通过索引找到聚集索引值,然后再根据聚集索引值去聚集索引里面找到数据域。

mylsam是堆组织表HOT,即数据域和索引分开,不要求必须有主键,保存的文件有两个,myi为索引,myd为数据域,主键和普通索引的区别就是主键是唯一不空的。他们的叶子节点都是存储的索引值和数据域的地址。

mvcc详解https://www.jianshu.com/p/db334404d909https://yq.aliyun.com/articles/283418

猜你喜欢

转载自blog.csdn.net/cjc000/article/details/90484276