关于锁和数据库的隔离级别可以参考下面的博客
转载地址:点击打开链接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用来标识该行所述的事务,7字节的回滚指针需要了解下Innodb的事务模型。
为了支持事务,Innbodb引入了下面几个概念:
https://yq.aliyun.com/articles/283418
innodb的行级锁是锁的索引,如果索引失效或者没用到索引,就会使用表级锁,因为他的mvcc特性,导致查询count(*)必须全表查(毕竟每个事务内访问的数据域版本都是不一样的)。
innodb是索引组织表IOT,即聚集索引和数据域在一个存储空间,保存的文件也是一个,主键就是聚集索引,如果没有主键的话会找一个非空的唯一索引来当聚集索引,如果还没有的话就默认用innodb的6个字节的rowid来当聚集索引。非聚集索引的叶子节点存储的是索引值和聚集索引值,通过索引找到聚集索引值,然后再根据聚集索引值去聚集索引里面找到数据域。
mylsam是堆组织表HOT,即数据域和索引分开,不要求必须有主键,保存的文件有两个,myi为索引,myd为数据域,主键和普通索引的区别就是主键是唯一不空的。他们的叶子节点都是存储的索引值和数据域的地址。