数据库的隔离级别和锁

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

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

我要介绍的是锁和隔离级别的关系,事务的隔离本质是通过加锁实现的,事务开启和关闭的过程就是加锁和放锁的过程。

1.读未提交:对于同一条数据,线程A进行update操作,此时对这个数据进行加X锁操作,而线程B此时进行select操作,在这种隔离级别时不会对线程B加任何锁,所以线程B能在任何时候读到数据,不管事务是否提交。

2.读已提交:对于同一条数据,线程B先进行select操作,此时对这个数据进行加S锁操作,读取完立刻释放S锁,而不用等待事务完成。线程A进行update操作,此时要这个数据进行加X锁操作,而在这种隔离级别时,要先将S锁释放完才能加X锁,因为S锁读完就已经释放了,所以可以加X锁。如果这时线程B在上次事务中继续查询,要获取S锁,但是必须要等X锁释放才能获取,所以等线程A提交后才能读取,这时读取的就是更新后的数据,两次不一致。这就是不可重读。

3.可重读:对于同一条数据,线程B先进行select操作,此时对这个数据进行加S锁操作,必须等事务结束后才能释放S锁。线程A进行update操作,此时要这个数据进行加X锁操作,而在这种隔离级别时,要先将S锁释放完才能加X锁,所以线程B在同一事务内对此数据的查询结果都是一样的,因为S锁未释放,无法加X锁。

4.串行化:和可重读类似,只不过每次的锁都是锁住整张表。

5.数据库悲观所:当for update的时候会对所查询行上U锁,这时候select 语句可以正常查询,但是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的事务隔离级别。

https://yq.aliyun.com/articles/283418

 

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

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

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

你可能感兴趣的:(数据库)